diff --git a/geo.html b/geo.html index 4b9e63b..33703cc 100644 --- a/geo.html +++ b/geo.html @@ -106,7 +106,7 @@ 路径参数 - +
@@ -514,7 +514,7 @@ document.getElementById('resultContainer').classList.add('hidden'); } - // 清空所有:起点重置为空(而非默认坐标) + // 清空所有: 起点重置为空(而非默认坐标) function clearAll() { clearRoute(); @@ -578,7 +578,7 @@ }); if (response.data.code !== 200 || !response.data.data || !response.data.data[0]) { - throw new Error(`上传失败:${response.data.message || '未知错误'}`); + throw new Error(`上传失败: ${response.data.message || '未知错误'}`); } const fileId = response.data.data[0].id; @@ -587,7 +587,7 @@ } catch (error) { const errorMsg = error.response?.data?.message || error.message || '上传异常'; updateLoadProgress(false); - alert(`⚠️ 上传失败:${errorMsg}`); + alert(`⚠️ 上传失败: ${errorMsg}`); throw error; } } @@ -600,7 +600,7 @@ const response = await axios.post(`${API_BASE_URL}/graphhopper/loadMap`, formData); if (response.data.code !== 200) { - throw new Error(`加载失败:${response.data.message || '接口返回异常'}`); + throw new Error(`加载失败: ${response.data.message || '接口返回异常'}`); } updateLoadProgress(false); @@ -612,7 +612,7 @@ } catch (error) { const errorMsg = error.response?.data?.message || error.message || '加载异常'; updateLoadProgress(false); - alert(`⚠️ 地图加载失败:${errorMsg}`); + alert(`⚠️ 地图加载失败: ${errorMsg}`); throw error; } } @@ -662,13 +662,13 @@ ); if (response.data.code !== 200 || !response.data.data) { - throw new Error(`计算失败:${response.data.message || '接口返回异常'}`); + throw new Error(`计算失败: ${response.data.message || '接口返回异常'}`); } handleRouteResponse(response.data.data); } catch (error) { const errorMsg = error.response?.data?.message || error.message || '计算异常'; - alert(`⚠️ 路径计算失败:${errorMsg}`); + alert(`⚠️ 路径计算失败: ${errorMsg}`); } finally { calcBtn.disabled = false; calcBtn.innerHTML = '计算路径'; @@ -691,12 +691,12 @@ routeLine = L.polyline(latLngs, lineStyles[document.getElementById('profile').value]) .addTo(map) - .bindPopup(`

距离:${routeData.distanceKm.toFixed(2)} 公里

时间:${routeData.timeMinutes} 分钟

`); + .bindPopup(`

距离: ${routeData.distanceKm.toFixed(2)} 公里

时间: ${routeData.timeMinutes} 分钟

`); map.fitBounds(routeLine.getBounds(), {padding: [50, 50], maxZoom: 14}); } - // 事件绑定:移除起点按钮弹窗 + // 事件绑定: 移除起点按钮弹窗 function bindEvents() { // 文件选择相关 document.getElementById('selectFileBtn').addEventListener('click', () => { @@ -713,7 +713,7 @@ const file = e.target.files[0]; if (file.name.toLowerCase().endsWith('.pbf')) { const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2); - fileInfoEl.textContent = `已选择:${file.name}(${fileSizeMB} MB)`; + fileInfoEl.textContent = `已选择: ${file.name}(${fileSizeMB} MB)`; fileInfoEl.className = 'mt-1 text-sm text-gray-600'; } else { fileInfoEl.textContent = '❌ 请选择PBF格式的地图文件'; @@ -736,11 +736,11 @@ fileInput.value = ''; document.getElementById('fileInfo').classList.add('hidden'); } catch (error) { - console.error('地图加载失败:', error); + console.error('地图加载失败: ', error); } }); - // 起点按钮:移除弹窗、仅保留地图选点逻辑 + // 起点按钮: 移除弹窗、仅保留地图选点逻辑 document.getElementById('setStartBtn').addEventListener('click', () => { map.once('click', (e) => setStartPoint(e.latlng.lat, e.latlng.lng)); }); diff --git a/src/main/java/com/yj/earth/auth/AuthGenerator.java b/src/main/java/com/yj/earth/auth/AuthGenerator.java index 738befd..8182c5f 100644 --- a/src/main/java/com/yj/earth/auth/AuthGenerator.java +++ b/src/main/java/com/yj/earth/auth/AuthGenerator.java @@ -85,9 +85,9 @@ public class AuthGenerator { // 将授权字符串写入文件 Files.write(licPath, authContent.getBytes(StandardCharsets.UTF_8)); - System.out.println("授权文件生成成功:" + licPath.toAbsolutePath()); + System.out.println("授权文件生成成功: " + licPath.toAbsolutePath()); } catch (Exception e) { - System.err.println("生成授权文件失败:" + e.getMessage()); + System.err.println("生成授权文件失败: " + e.getMessage()); e.printStackTrace(); } } diff --git a/src/main/java/com/yj/earth/auth/AuthValidator.java b/src/main/java/com/yj/earth/auth/AuthValidator.java index 2ea5af8..a5bf3d2 100644 --- a/src/main/java/com/yj/earth/auth/AuthValidator.java +++ b/src/main/java/com/yj/earth/auth/AuthValidator.java @@ -100,15 +100,15 @@ public class AuthValidator { String serverUniqueId = ServerUniqueIdUtil.getServerUniqueId(); // 生成授权 String authString = AuthGenerator.generateAuth("标准版", 1000, 30, "35A0DF1D05AEAE77E1E2715CC36A7368"); - System.out.println("授权字符串:" + authString); + System.out.println("授权字符串: " + authString); // 验证授权 boolean isValid = AuthValidator.validateAuth(authString, serverUniqueId); - System.out.println("授权是否有效:" + isValid); + System.out.println("授权是否有效: " + isValid); int remainingDays = AuthValidator.getRemainingDays(authString); - System.out.println("剩余天数:" + remainingDays); + System.out.println("剩余天数: " + remainingDays); AuthInfo authInfo = AuthValidator.getAuthInfo(authString); - System.out.println("授权信息:" + authInfo); + System.out.println("授权信息: " + authInfo); } } diff --git a/src/main/java/com/yj/earth/business/controller/AuthController.java b/src/main/java/com/yj/earth/business/controller/AuthController.java index 29c0b39..e7da2af 100644 --- a/src/main/java/com/yj/earth/business/controller/AuthController.java +++ b/src/main/java/com/yj/earth/business/controller/AuthController.java @@ -61,7 +61,7 @@ public class AuthController { Files.write(path, authContent.getBytes(StandardCharsets.UTF_8)); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("导入授权文件失败:" + e.getMessage()); + return ApiResponse.failure("导入授权文件失败: " + e.getMessage()); } } @@ -80,7 +80,7 @@ public class AuthController { AuthInfo authInfo = AuthValidator.getAuthInfo(authContent); return ApiResponse.success(authInfo); } catch (Exception e) { - return ApiResponse.failure("获取授权信息失败:" + e.getMessage()); + return ApiResponse.failure("获取授权信息失败: " + e.getMessage()); } } } diff --git a/src/main/java/com/yj/earth/business/controller/DeviceController.java b/src/main/java/com/yj/earth/business/controller/DeviceController.java index 323dc8f..1bf86a7 100644 --- a/src/main/java/com/yj/earth/business/controller/DeviceController.java +++ b/src/main/java/com/yj/earth/business/controller/DeviceController.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yj.earth.business.domain.Device; import com.yj.earth.business.service.DeviceService; import com.yj.earth.common.util.ApiResponse; +import com.yj.earth.common.util.NetUtils; import com.yj.earth.vo.AddDeviceDto; import com.yj.earth.dto.device.ImportDeviceDto; import com.yj.earth.dto.device.UpdateDeviceDto; @@ -27,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.util.*; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @Tag(name = "设备信息管理") @@ -73,14 +75,57 @@ public class DeviceController { } @GetMapping("/list") - @Operation(summary = "查询设备信息") - public ApiResponse listDevice(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize,@Parameter(description = "设备名称") String cameraName) { + @Operation(summary = "查询设备信息(含在线状态)") + public ApiResponse listDevice( + @Parameter(description = "分页数量") Integer pageNum, + @Parameter(description = "分页大小") Integer pageSize, + @Parameter(description = "设备名称") String cameraName + ) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotBlank(cameraName)) { queryWrapper.like(Device::getCameraName, cameraName); } + + // 执行标准的分页查询 Page devicePage = deviceService.page(new Page<>(pageNum, pageSize), queryWrapper); - return ApiResponse.success(devicePage); + List deviceList = devicePage.getRecords(); + + if (CollectionUtils.isEmpty(deviceList)) { + // 如果没有数据,直接返回空分页 + return ApiResponse.success(devicePage); + } + + try { + // 提取所有设备的 IP 地址、用于批量检测 + List ipAddresses = deviceList.stream() + .map(Device::getIp) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + // 异步并发检测所有 IP 的在线状态 + Map reachabilityMap = NetUtils.checkReachabilityAsync(ipAddresses); + + // 更新设备状态:遍历设备列表,根据 IP 设置 status 字段 + deviceList.forEach(device -> { + String ip = device.getIp(); + if (ip != null) { + Boolean isOnline = reachabilityMap.get(ip); + // 根据 Ping 结果设置 status: 1 为在线,0 为离线 + device.setStatus(isOnline != null && isOnline ? 1 : 0); + } else { + // 如果 IP 地址为空,直接设置为离线 + device.setStatus(0); + } + }); + + // 直接返回更新后的分页对象 + return ApiResponse.success(devicePage); + + } catch (ExecutionException | InterruptedException e) { + // 恢复线程中断状态 + Thread.currentThread().interrupt(); + return ApiResponse.success(devicePage); + } } @GetMapping("/delete") @@ -270,7 +315,7 @@ public class DeviceController { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("设备信息导入模板", "UTF-8").replaceAll("\\+", "%20"); - response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + response.setHeader("Content-disposition", "attachment;filename*=" + fileName + ".xlsx"); // 写入模板表头(通过空数据列表触发表头生成) EasyExcel.write(response.getOutputStream(), ImportDeviceDto.class) .sheet("设备信息") diff --git a/src/main/java/com/yj/earth/business/controller/FileInfoController.java b/src/main/java/com/yj/earth/business/controller/FileInfoController.java index 4cd8315..5a8f9ad 100644 --- a/src/main/java/com/yj/earth/business/controller/FileInfoController.java +++ b/src/main/java/com/yj/earth/business/controller/FileInfoController.java @@ -193,7 +193,7 @@ public class FileInfoController { // 标准化路径 targetFilePath = Paths.get(fileAbsolutePath).toRealPath(); - // 校验文件合法性:是否存在、是否为普通文件 + // 校验文件合法性: 是否存在、是否为普通文件 BasicFileAttributes fileAttr = Files.readAttributes(targetFilePath, BasicFileAttributes.class); if (!fileAttr.isRegularFile()) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); @@ -214,7 +214,7 @@ public class FileInfoController { } response.setContentLengthLong(fileAttr.size()); - // 关键修改:将attachment改为inline实现预览 + // 关键修改: 将attachment改为inline实现预览 response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); @@ -231,13 +231,13 @@ public class FileInfoController { } catch (NoSuchFileException e) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); - fileInfoService.writeResponseMessage(response, "文件不存在:" + fileAbsolutePath); + fileInfoService.writeResponseMessage(response, "文件不存在: " + fileAbsolutePath); } catch (SecurityException e) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); - fileInfoService.writeResponseMessage(response, "访问拒绝:无权限读取该文件"); + fileInfoService.writeResponseMessage(response, "访问拒绝: 无权限读取该文件"); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - fileInfoService.writeResponseMessage(response, "预览失败:" + e.getMessage()); + fileInfoService.writeResponseMessage(response, "预览失败: " + e.getMessage()); } } } diff --git a/src/main/java/com/yj/earth/business/controller/IconLibraryController.java b/src/main/java/com/yj/earth/business/controller/IconLibraryController.java index 2fc1d27..1d487c1 100644 --- a/src/main/java/com/yj/earth/business/controller/IconLibraryController.java +++ b/src/main/java/com/yj/earth/business/controller/IconLibraryController.java @@ -71,22 +71,22 @@ public class IconLibraryController { if (!parentDir.exists()) { boolean mkdirsSuccess = parentDir.mkdirs(); if (!mkdirsSuccess) { - return ApiResponse.failure("创建父目录失败:" + folderPath); + return ApiResponse.failure("创建父目录失败: " + folderPath); } } // 校验图标库文件是否已存在 if (iconFile.exists()) { if (iconFile.isDirectory()) { - return ApiResponse.failure("同名目录已存在、无法创建文件:" + iconPath); + return ApiResponse.failure("同名目录已存在、无法创建文件: " + iconPath); } - return ApiResponse.failure("图标库文件已存在:" + iconPath); + return ApiResponse.failure("图标库文件已存在: " + iconPath); } // 创建SQLite文件 boolean createFileSuccess = iconFile.createNewFile(); if (!createFileSuccess) { - return ApiResponse.failure("创建图标库文件失败:" + iconPath); + return ApiResponse.failure("创建图标库文件失败: " + iconPath); } // 初始化图标库表结构 @@ -95,7 +95,7 @@ public class IconLibraryController { addIconLibrary(iconPath); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("创建图标库失败:" + e.getMessage()); + return ApiResponse.failure("创建图标库失败: " + e.getMessage()); } } @@ -107,13 +107,13 @@ public class IconLibraryController { // 校验路径是否存在 File iconFile = new File(iconPath); if (!iconFile.exists() || !iconFile.isFile()) { - return ApiResponse.failure("图标库文件不存在:" + iconPath); + return ApiResponse.failure("图标库文件不存在: " + iconPath); } // 添加到配置表并启用 addIconLibrary(iconPath); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("导入图标库失败:" + e.getMessage()); + return ApiResponse.failure("导入图标库失败: " + e.getMessage()); } } @@ -136,7 +136,7 @@ public class IconLibraryController { iconPath, checkParentSql, parentParams, IconType.class ); if (parentType == null) { - return ApiResponse.failure("父级图标类型不存在:" + parentId); + return ApiResponse.failure("父级图标类型不存在: " + parentId); } } diff --git a/src/main/java/com/yj/earth/business/controller/MilitaryLibraryController.java b/src/main/java/com/yj/earth/business/controller/MilitaryLibraryController.java index 719e8b9..77cec6e 100644 --- a/src/main/java/com/yj/earth/business/controller/MilitaryLibraryController.java +++ b/src/main/java/com/yj/earth/business/controller/MilitaryLibraryController.java @@ -81,22 +81,22 @@ public class MilitaryLibraryController { if (!parentDir.exists()) { boolean mkdirsSuccess = parentDir.mkdirs(); if (!mkdirsSuccess) { - return ApiResponse.failure("创建父目录失败:" + folderPath); + return ApiResponse.failure("创建父目录失败: " + folderPath); } } // 校验军标库文件是否已存在 if (militaryFile.exists()) { if (militaryFile.isDirectory()) { - return ApiResponse.failure("同名目录已存在、无法创建文件:" + militaryPath); + return ApiResponse.failure("同名目录已存在、无法创建文件: " + militaryPath); } - return ApiResponse.failure("军标库文件已存在:" + militaryPath); + return ApiResponse.failure("军标库文件已存在: " + militaryPath); } // 创建SQLite文件 boolean createFileSuccess = militaryFile.createNewFile(); if (!createFileSuccess) { - return ApiResponse.failure("创建军标库文件失败:" + militaryPath); + return ApiResponse.failure("创建军标库文件失败: " + militaryPath); } // 初始化军标库表结构 @@ -105,7 +105,7 @@ public class MilitaryLibraryController { addMilitaryLibrary(militaryPath); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("创建军标库失败:" + e.getMessage()); + return ApiResponse.failure("创建军标库失败: " + e.getMessage()); } } @@ -117,13 +117,13 @@ public class MilitaryLibraryController { // 校验路径是否存在 File militaryFile = new File(militaryPath); if (!militaryFile.exists() || !militaryFile.isFile()) { - return ApiResponse.failure("军标库文件不存在:" + militaryPath); + return ApiResponse.failure("军标库文件不存在: " + militaryPath); } // 添加到配置表并启用 addMilitaryLibrary(militaryPath); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("导入军标库失败:" + e.getMessage()); + return ApiResponse.failure("导入军标库失败: " + e.getMessage()); } } @@ -146,7 +146,7 @@ public class MilitaryLibraryController { militaryPath, checkParentSql, parentParams, MilitaryType.class ); if (parentType == null) { - return ApiResponse.failure("父级军标类型不存在:" + parentId); + return ApiResponse.failure("父级军标类型不存在: " + parentId); } } diff --git a/src/main/java/com/yj/earth/business/controller/ModelLibraryController.java b/src/main/java/com/yj/earth/business/controller/ModelLibraryController.java index dcc1666..0f9066f 100644 --- a/src/main/java/com/yj/earth/business/controller/ModelLibraryController.java +++ b/src/main/java/com/yj/earth/business/controller/ModelLibraryController.java @@ -81,27 +81,27 @@ public class ModelLibraryController { if (!parentDir.exists()) { boolean mkdirsSuccess = parentDir.mkdirs(); if (!mkdirsSuccess) { - return ApiResponse.failure("创建父目录失败:" + folderPath); + return ApiResponse.failure("创建父目录失败: " + folderPath); } } // 检查模型库文件是否已存在 if (modelFile.exists()) { if (modelFile.isDirectory()) { - return ApiResponse.failure("同名目录已存在、无法创建文件:" + modelPath); + return ApiResponse.failure("同名目录已存在、无法创建文件: " + modelPath); } - return ApiResponse.failure("模型库文件已存在:" + modelPath); + return ApiResponse.failure("模型库文件已存在: " + modelPath); } // 创建模型库文件( boolean createSuccess = modelFile.createNewFile(); if (!createSuccess) { - return ApiResponse.failure("创建模型库文件失败:" + modelPath); + return ApiResponse.failure("创建模型库文件失败: " + modelPath); } // 添加模型库信息 addModelLibrary(modelPath); SQLiteUtil.initializationModel(modelPath); return ApiResponse.success(null); } catch (Exception e) { - return ApiResponse.failure("创建模型库失败:" + e.getMessage()); + return ApiResponse.failure("创建模型库失败: " + e.getMessage()); } } @@ -174,7 +174,7 @@ public class ModelLibraryController { @Operation(summary = "模型类型列表") @GetMapping("/modelTypeList") - public ApiResponse modelTypeTree(@Parameter(description = "模型名称") @RequestParam("modelName") String modelName) throws SQLException, IllegalAccessException, InstantiationException { + public ApiResponse modelTypeTree(@Parameter(description = "模型名称") @RequestParam(value = "modelName",required = false) String modelName) throws SQLException, IllegalAccessException, InstantiationException { return ApiResponse.success(modelTypeList(modelName)); } @@ -190,7 +190,7 @@ public class ModelLibraryController { return ApiResponse.failure("请先创建或导入模型库"); } - // 简化校验:仅判断列表是否为空 + // 简化校验: 仅判断列表是否为空 if (filePaths == null || filePaths.isEmpty()) { return ApiResponse.failure("文件路径列表不能为空"); } @@ -286,9 +286,12 @@ public class ModelLibraryController { } - @Operation(summary = "根据模型类型查看模型列表") + @Operation(summary = "根据模型类型和名称模糊查询模型列表") @PostMapping("/modelList") - public ApiResponse modelList(@Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws SQLException, IllegalAccessException, InstantiationException { + public ApiResponse modelList( + @Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId, + @Parameter(description = "模型名称模糊查询") @RequestParam(value = "name", required = false) String name + ) throws SQLException, IllegalAccessException, InstantiationException { // 获取最新的模型库 String modelPath = getModelLibrary(); if (modelPath == null) { @@ -298,15 +301,15 @@ public class ModelLibraryController { // 获取分类ID及其所有子分类ID的列表 List typeIdList = getModelTypeIdsWithChildren(modelTypeId); if (typeIdList.isEmpty()) { - return ApiResponse.success(null); + return ApiResponse.success(Collections.emptyList()); } String idsWithQuotes = typeIdList.stream() .map(id -> "'" + id + "'") .collect(Collectors.joining(",")); - // 多表联查、查询所有指定分类及其子分类下的模型 - String sql = """ + // 构建SQL语句 + StringBuilder sql = new StringBuilder(""" SELECT model.id, model.model_type_id as modelTypeId, @@ -318,12 +321,18 @@ public class ModelLibraryController { model_type.name as modelTypeName FROM model JOIN model_type ON model.model_type_id = model_type.id - WHERE model.model_type_id IN (?) - """.replace("?", idsWithQuotes); + WHERE model.model_type_id IN (%s) + """.formatted(idsWithQuotes)); + + // 如果传入了名称、则增加模糊查询条件 + if (name != null && !name.trim().isEmpty()) { + // 直接拼接SQL、注意SQL注入风险 + sql.append(" AND model.model_name LIKE '%" + name.trim() + "%'"); + } + + // 执行查询 + List modelVos = SQLiteUtil.queryForList(modelPath, sql.toString(), null, ModelVo.class); - // 使用所有分类ID作为查询参数 - List params = new ArrayList<>(); - List modelVos = SQLiteUtil.queryForList(modelPath, sql, null, ModelVo.class); // 循环遍历数据 for (ModelVo modelVo : modelVos) { if (modelVo.getModelType() != null) { @@ -443,27 +452,35 @@ public class ModelLibraryController { return ApiResponse.success(modelTypeList(null)); } + /** + * 查询模型分类树形结构 + */ private List modelTypeList(String modelName) throws SQLException, IllegalAccessException, InstantiationException { String modelPath = getModelLibrary(); if (modelPath == null) { return null; } + // 构建基础SQL语句 StringBuilder sqlBuilder = new StringBuilder(""" - SELECT id, name, parent_id as parentId, - tree_index as treeIndex, created_at as createdAt, - updated_at as updatedAt - FROM model_type + SELECT DISTINCT model_type.id, model_type.name, model_type.parent_id, + model_type.tree_index, model_type.created_at, + model_type.updated_at + FROM model_type """); + // 如果传入了模型名称、则拼接 JOIN 和 WHERE 条件 if (modelName != null && !modelName.trim().isEmpty()) { - sqlBuilder.append("WHERE name LIKE '%").append(modelName.trim()).append("%' "); + String trimmedName = modelName.trim(); + // 直接拼接SQL、不做任何转义(存在SQL注入风险) + sqlBuilder.append(" INNER JOIN model ON model_type.id = model.model_type_id"); + sqlBuilder.append(" WHERE model.model_name LIKE '%" + trimmedName + "%'"); } // 为所有查询都加上统一的排序条件 - sqlBuilder.append("ORDER BY tree_index ASC"); + sqlBuilder.append(" ORDER BY model_type.tree_index ASC"); - // 执行查询 + // 执行查询、获取符合条件的模型分类列表 List modelTypes = SQLiteUtil.queryForList( modelPath, sqlBuilder.toString(), @@ -471,9 +488,10 @@ public class ModelLibraryController { ModelType.class ); - // 转换为树形结构 + // 将扁平的分类列表转换为树形结构 return buildModelTypeTree(modelTypes); } + private List buildModelTypeTree(List modelTypes) { List treeNodes = modelTypes.stream() .map(modelType -> new ModelTypeVo(modelType)) @@ -572,7 +590,7 @@ public class ModelLibraryController { // 结果列表初始化 List typeIdList = new ArrayList<>(); - // 校验入参:分类ID不能为空 + // 校验入参: 分类ID不能为空 if (StringUtils.isEmpty(modelTypeId)) { return null; } diff --git a/src/main/java/com/yj/earth/business/controller/PoiController.java b/src/main/java/com/yj/earth/business/controller/PoiController.java index 2117e03..5d80536 100644 --- a/src/main/java/com/yj/earth/business/controller/PoiController.java +++ b/src/main/java/com/yj/earth/business/controller/PoiController.java @@ -34,7 +34,7 @@ public class PoiController { Class.forName("org.sqlite.JDBC"); String dbPath = System.getProperty("user.dir") + File.separator + "poi" + File.separator + "poi.db"; connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath); - logger.info("POI数据库连接初始化成功、路径:{}", dbPath); + logger.info("POI数据库连接初始化成功、路径: {}", dbPath); } } @@ -92,10 +92,10 @@ public class PoiController { return ApiResponse.success(poiList); } catch (SQLException e) { logger.error("POI数据查询失败", e); - return ApiResponse.failure("POI数据查询失败:" + e.getMessage()); + return ApiResponse.failure("POI数据查询失败: " + e.getMessage()); } catch (ClassNotFoundException e) { logger.error("POI数据库驱动加载失败", e); - return ApiResponse.failure("POI功能初始化失败:驱动未找到"); + return ApiResponse.failure("POI功能初始化失败: 驱动未找到"); } } } diff --git a/src/main/java/com/yj/earth/business/controller/PoiInfoController.java b/src/main/java/com/yj/earth/business/controller/PoiInfoController.java index e3b1894..c4461ca 100644 --- a/src/main/java/com/yj/earth/business/controller/PoiInfoController.java +++ b/src/main/java/com/yj/earth/business/controller/PoiInfoController.java @@ -130,7 +130,7 @@ public class PoiInfoController { return ApiResponse.success(poiList); } catch (Exception e) { - return ApiResponse.failure("POI数据查询失败:" + e.getMessage()); + return ApiResponse.failure("POI数据查询失败: " + e.getMessage()); } } @@ -144,13 +144,13 @@ public class PoiInfoController { } File dbFile = new File(absolutePath.trim()); if (!dbFile.exists()) { - throw new SQLException("数据库文件不存在:" + absolutePath); + throw new SQLException("数据库文件不存在: " + absolutePath); } if (!dbFile.isFile()) { - throw new SQLException("路径指向的不是文件:" + absolutePath); + throw new SQLException("路径指向的不是文件: " + absolutePath); } if (!dbFile.canRead()) { - throw new SQLException("没有权限读取数据库文件:" + absolutePath); + throw new SQLException("没有权限读取数据库文件: " + absolutePath); } try { Class.forName(JDBC.class.getName()); diff --git a/src/main/java/com/yj/earth/business/controller/SystemController.java b/src/main/java/com/yj/earth/business/controller/SystemController.java index 1e864fa..23a1561 100644 --- a/src/main/java/com/yj/earth/business/controller/SystemController.java +++ b/src/main/java/com/yj/earth/business/controller/SystemController.java @@ -93,9 +93,9 @@ public class SystemController { return ApiResponse.success(null); } catch (IOException e) { - return ApiResponse.failure("更新端口配置失败:" + e.getMessage()); + return ApiResponse.failure("更新端口配置失败: " + e.getMessage()); } catch (Exception e) { - return ApiResponse.failure("操作失败:" + e.getMessage()); + return ApiResponse.failure("操作失败: " + e.getMessage()); } } @@ -141,10 +141,10 @@ public class SystemController { String originalParentId = source.getParentId(); if (originalParentId != null && !originalParentId.trim().isEmpty() && originalIdToNewId.containsKey(originalParentId)) { - // 父节点在导入数据中:用新ID关联 + // 父节点在导入数据中: 用新ID关联 source.setParentId(originalIdToNewId.get(originalParentId)); } else { - // 父节点不在导入数据中:挂到总根节点 + // 父节点不在导入数据中: 挂到总根节点 source.setParentId(newParentId); } return source; @@ -177,7 +177,7 @@ public class SystemController { source.setSourceType(resultSet.getString("source_type")); source.setSourcePath(resultSet.getString("source_path")); - // 关键修改:读取原始parent_id、而非覆盖为新父节点ID + // 关键修改: 读取原始parent_id、而非覆盖为新父节点ID source.setParentId(resultSet.getString("parent_id")); Integer treeIndex = resultSet.getObject("tree_index") != null @@ -318,13 +318,13 @@ public class SystemController { } File dbFile = new File(absolutePath.trim()); if (!dbFile.exists()) { - throw new SQLException("数据库文件不存在:" + absolutePath); + throw new SQLException("数据库文件不存在: " + absolutePath); } if (!dbFile.isFile()) { - throw new SQLException("路径指向的不是文件:" + absolutePath); + throw new SQLException("路径指向的不是文件: " + absolutePath); } if (!dbFile.canRead()) { - throw new SQLException("没有权限读取数据库文件:" + absolutePath); + throw new SQLException("没有权限读取数据库文件: " + absolutePath); } try { Class.forName(JDBC.class.getName()); diff --git a/src/main/java/com/yj/earth/business/controller/TsPlanController.java b/src/main/java/com/yj/earth/business/controller/TsPlanController.java index 5059cd6..6a20a3e 100644 --- a/src/main/java/com/yj/earth/business/controller/TsPlanController.java +++ b/src/main/java/com/yj/earth/business/controller/TsPlanController.java @@ -18,8 +18,14 @@ import org.springframework.beans.BeanUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; @Tag(name = "态势方案管理") @@ -67,41 +73,100 @@ public class TsPlanController { @Operation(summary = "查询所有方案") @PostMapping("/list") - public ApiResponse list(@Parameter(description = "分页数量") @RequestParam(value = "pageNum") Integer pageNum, - @Parameter(description = "分页大小") @RequestParam(value = "pageSize") Integer pageSize, - @Parameter(description = "方案名称") @RequestParam(value = "name", required = false) String name, - @Parameter(description = "用户名称") @RequestParam(value = "username", required = false) String username, - @Parameter(description = "开始时间") @RequestParam(value = "startTime", required = false) String startTime, - @Parameter(description = "结束时间") @RequestParam(value = "endTime", required = false) String endTime) { + public ApiResponse list(@Parameter(description = "分页数量") @RequestParam Integer pageNum, + @Parameter(description = "分页大小") @RequestParam Integer pageSize, + @Parameter(description = "方案名称") @RequestParam(required = false) String name, + @Parameter(description = "用户名称") @RequestParam(required = false) String username, + @Parameter(description = "开始时间") @RequestParam(required = false) String startTime, + @Parameter(description = "结束时间") @RequestParam(required = false) String endTime) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 方案名称模糊查询 if (StringUtils.isNotBlank(name)) { queryWrapper.like(TsPlan::getName, name); } + + // 用户名查询 if (StringUtils.isNotBlank(username)) { - // 根据用户名查询用户ID LambdaQueryWrapper userQueryWrapper = new LambdaQueryWrapper<>(); userQueryWrapper.like(User::getUsername, username); - User user = userService.getOne(userQueryWrapper); - if (user != null) { - queryWrapper.eq(TsPlan::getCreatedBy, user.getId()); + List userList = userService.list(userQueryWrapper); + + // 判断列表是否为空 + if (userList.isEmpty()) { + // 无匹配用户、直接返回空分页结果 + Page emptyPage = new Page<>(pageNum, pageSize); + emptyPage.setTotal(0); + emptyPage.setRecords(Collections.emptyList()); + return ApiResponse.success(emptyPage); } + + // 提取用户 ID 列表(确保类型匹配) + List userIdList = userList.stream() + .map(User::getId) + .filter(Objects::nonNull) + .toList(); + + // 若 ID 列表为空,也返回空结果 + if (userIdList.isEmpty()) { + Page emptyPage = new Page<>(pageNum, pageSize); + emptyPage.setTotal(0); + emptyPage.setRecords(Collections.emptyList()); + return ApiResponse.success(emptyPage); + } + + queryWrapper.in(TsPlan::getCreatedBy, userIdList); } + + // 时间条件优化 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - if (startTime != null && endTime != null) { - // 把字符串的时间字符串 转换成 LocalDateTime - LocalDateTime startTimeDate = LocalDateTime.parse(startTime, formatter); - LocalDateTime endTimeDate = LocalDateTime.parse(endTime, formatter); - queryWrapper.between(TsPlan::getCreatedAt, startTimeDate, endTimeDate); - } - Page page = tsPlanService.page(new Page<>(pageNum, pageSize), queryWrapper); - // 需要设置创建人名称 - page.getRecords().forEach(tsPlan -> { - // 根据用户ID查询用户名 - User user = userService.getById(tsPlan.getCreatedBy()); - if (user != null) { - tsPlan.setCreatedBy(user.getNickname()); + if (StringUtils.isNotBlank(startTime)) { + try { + LocalDateTime start = LocalDateTime.parse(startTime, formatter); + queryWrapper.ge(TsPlan::getCreatedAt, start); + } catch (DateTimeException e) { + return ApiResponse.failure("开始时间格式错误,需为 yyyy-MM-dd HH:mm:ss"); } - }); + } + if (StringUtils.isNotBlank(endTime)) { + try { + LocalDateTime end = LocalDateTime.parse(endTime, formatter); + queryWrapper.le(TsPlan::getCreatedAt, end); + } catch (DateTimeException e) { + return ApiResponse.failure("结束时间格式错误,需为 yyyy-MM-dd HH:mm:ss"); + } + } + + // 分页查询方案 + Page page = tsPlanService.page(new Page<>(pageNum, pageSize), queryWrapper); + + // 优化用户名查询 + if (!page.getRecords().isEmpty()) { + // 提取所有创建人 ID + List creatorIds = page.getRecords().stream() + .map(TsPlan::getCreatedBy) + .filter(Objects::nonNull) + .distinct() + .toList(); + + // 批量查询用户 + if (!creatorIds.isEmpty()) { + List creators = userService.listByIds(creatorIds); + // 构建 ID -> 昵称映射 + Map creatorNicknameMap = creators.stream() + .collect(Collectors.toMap( + User::getId, + User::getNickname, + (k1, k2) -> k1)); + + // 填充昵称 + page.getRecords().forEach(plan -> { + String nickname = creatorNicknameMap.getOrDefault(plan.getCreatedBy(), "未知用户"); + plan.setCreatedBy(nickname); + }); + } + } + return ApiResponse.success(page); } } diff --git a/src/main/java/com/yj/earth/business/service/impl/FileInfoServiceImpl.java b/src/main/java/com/yj/earth/business/service/impl/FileInfoServiceImpl.java index 807ce35..22fce39 100644 --- a/src/main/java/com/yj/earth/business/service/impl/FileInfoServiceImpl.java +++ b/src/main/java/com/yj/earth/business/service/impl/FileInfoServiceImpl.java @@ -211,7 +211,7 @@ public class FileInfoServiceImpl extends ServiceImpl i if (degreesMinutesSeconds == null || degreesMinutesSeconds.length != 3 || ref == null) { return null; } - // 度分秒转十进制:度 + 分/60 + 秒/3600 + // 度分秒转十进制: 度 + 分/60 + 秒/3600 double degrees = degreesMinutesSeconds[0].doubleValue(); double minutes = degreesMinutesSeconds[1].doubleValue(); double seconds = degreesMinutesSeconds[2].doubleValue(); diff --git a/src/main/java/com/yj/earth/business/service/impl/SourceServiceImpl.java b/src/main/java/com/yj/earth/business/service/impl/SourceServiceImpl.java index 4a0338a..bad0a69 100644 --- a/src/main/java/com/yj/earth/business/service/impl/SourceServiceImpl.java +++ b/src/main/java/com/yj/earth/business/service/impl/SourceServiceImpl.java @@ -218,15 +218,15 @@ public class SourceServiceImpl extends ServiceImpl impleme // 添加createdAt时间范围过滤(如果时间不为空) if (startTime != null && endTime != null) { - // 同时存在开始和结束时间:查询范围内的数据 + // 同时存在开始和结束时间: 查询范围内的数据 sourceQueryWrapper.between(Source::getCreatedAt, startTime, endTime); countQueryWrapper.between(Source::getCreatedAt, startTime, endTime); } else if (startTime != null) { - // 仅存在开始时间:查询大于等于开始时间的数据 + // 仅存在开始时间: 查询大于等于开始时间的数据 sourceQueryWrapper.ge(Source::getCreatedAt, startTime); countQueryWrapper.ge(Source::getCreatedAt, startTime); } else if (endTime != null) { - // 仅存在结束时间:查询小于等于结束时间的数据 + // 仅存在结束时间: 查询小于等于结束时间的数据 sourceQueryWrapper.le(Source::getCreatedAt, endTime); countQueryWrapper.le(Source::getCreatedAt, endTime); } @@ -252,7 +252,7 @@ public class SourceServiceImpl extends ServiceImpl impleme if (pageNum != null && pageSize != null) { // 分页时、查询条件排除directory类型 sourceQueryWrapper.ne(Source::getSourceType, "directory"); - // 分页查询:获取分页数据 + // 分页查询: 获取分页数据 Page page = new Page<>(pageNum, pageSize); IPage resultPage = sourceService.page(page, sourceQueryWrapper); // 使用单独的count查询条件计算总数(已排除directory) @@ -260,7 +260,7 @@ public class SourceServiceImpl extends ServiceImpl impleme resultMap.put("list", resultPage.getRecords()); resultMap.put("total", total); } else { - // 不分页:获取全部数据(包含所有类型)和总数(排除directory) + // 不分页: 获取全部数据(包含所有类型)和总数(排除directory) List sourceList = sourceService.list(sourceQueryWrapper); long total = sourceService.count(countQueryWrapper); diff --git a/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java b/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java index 6700dc8..bb80113 100644 --- a/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java +++ b/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java @@ -172,7 +172,7 @@ public class MilitaryConverter { } } - System.out.println("\n成功转换 jun_biao_types 表数据:" + count + "条记录"); + System.out.println("\n成功转换 jun_biao_types 表数据: " + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); @@ -225,7 +225,7 @@ public class MilitaryConverter { } } - System.out.println("\n成功转换 jun_biaos 表数据:" + count + "条记录"); + System.out.println("\n成功转换 jun_biaos 表数据: " + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); diff --git a/src/main/java/com/yj/earth/common/convert/ModelConverter.java b/src/main/java/com/yj/earth/common/convert/ModelConverter.java index f99f338..d2cf32b 100644 --- a/src/main/java/com/yj/earth/common/convert/ModelConverter.java +++ b/src/main/java/com/yj/earth/common/convert/ModelConverter.java @@ -155,7 +155,7 @@ public class ModelConverter { } } - System.out.println("\n成功转换 mode_types 表数据:" + count + "条记录"); + System.out.println("\n成功转换 mode_types 表数据: " + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); @@ -209,7 +209,7 @@ public class ModelConverter { } } - System.out.println("\n成功转换 models 表数据:" + count + "条记录"); + System.out.println("\n成功转换 models 表数据: " + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); diff --git a/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java b/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java index 7a30b70..1475ab0 100644 --- a/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java +++ b/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java @@ -92,7 +92,7 @@ public class SourceDataGenerator { continue; } - // 基本类型、包装类、字符串:直接设默认值 + // 基本类型、包装类、字符串: 直接设默认值 if (isPrimitiveOrWrapper(fieldType) || fieldType.equals(String.class)) { field.set(instance, getDefaultValue(fieldType)); continue; @@ -238,13 +238,13 @@ public class SourceDataGenerator { } /** - * 获取类型默认值:字符串返回空字符串、其他类型按原有逻辑 + * 获取类型默认值: 字符串返回空字符串、其他类型按原有逻辑 */ private static Object getDefaultValue(Class type) { if (type.equals(String.class)) { return ""; } - // 原有逻辑:基本类型返回对应默认值(boolean=false/数值=0/char='\0') + // 原有逻辑: 基本类型返回对应默认值(boolean=false/数值=0/char='\0') if (type.isPrimitive()) { if (type == boolean.class) return false; if (type == char.class) return '\0'; diff --git a/src/main/java/com/yj/earth/common/util/NetUtils.java b/src/main/java/com/yj/earth/common/util/NetUtils.java new file mode 100644 index 0000000..b55b94c --- /dev/null +++ b/src/main/java/com/yj/earth/common/util/NetUtils.java @@ -0,0 +1,120 @@ +package com.yj.earth.common.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.*; +import java.util.concurrent.*; + +/** + * 网络工具类 + */ +public class NetUtils { + private static final Logger logger = LoggerFactory.getLogger(NetUtils.class); + + // Ping 超时时间(毫秒) + private static final int PING_TIMEOUT = 500; + // TCP Ping 端口 + private static final int TCP_PING_PORT = 80; + // TCP Ping 超时时间(毫秒) + private static final int TCP_TIMEOUT = 1500; + + private static final ExecutorService PING_EXECUTOR = Executors.newFixedThreadPool( + Math.min(Runtime.getRuntime().availableProcessors() * 2, 50), + new ThreadFactory() { + private int count = 0; + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setName("ping-executor-" + (count++)); + thread.setDaemon(true); + return thread; + } + } + ); + + /** + * 使用 ICMP Echo Request (标准 Ping) 检测主机是否可达 + * @param ipAddress IP地址或主机名 + * @return true if reachable, false otherwise + */ + public static boolean isReachableByIcmp(String ipAddress) { + if (ipAddress == null || ipAddress.trim().isEmpty()) { + return false; + } + try { + InetAddress address = InetAddress.getByName(ipAddress); + // isReachable 会尝试 ICMP Echo 和 TCP Echo (端口 7) + return address.isReachable(PING_TIMEOUT); + } catch (UnknownHostException e) { + logger.warn("未知的主机: {}", ipAddress); + return false; + } catch (IOException e) { + // 发生 IO 异常、视为不可达 + return false; + } + } + + /** + * 使用 TCP 连接检测主机端口是否开放(模拟 Ping) + * 当 ICMP 被防火墙禁止时、此方法更有效 + * @param ipAddress IP地址 + * @param port 端口号 + * @return true if port is open, false otherwise + */ + public static boolean isReachableByTcp(String ipAddress, int port) { + if (ipAddress == null || ipAddress.trim().isEmpty() || port < 1 || port > 65535) { + return false; + } + Socket socket = null; + try { + socket = new Socket(); + socket.connect(new java.net.InetSocketAddress(ipAddress, port), TCP_TIMEOUT); + return true; + } catch (IOException e) { + // 连接失败、端口未开放或主机不可达 + return false; + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + /** + * 异步并发检测多个IP的可达性 + * @param ips 需要检测的IP列表 + * @return 一个 Map、key 是 IP、value 是 Boolean (是否在线) + * @throws ExecutionException 执行异常 + * @throws InterruptedException 线程中断异常 + */ + public static Map checkReachabilityAsync(Collection ips) throws ExecutionException, InterruptedException { + if (ips == null || ips.isEmpty()) { + return Collections.emptyMap(); + } + + List>> tasks = new ArrayList<>(); + for (String ip : ips) { + tasks.add(() -> { + boolean isOnline = isReachableByTcp(ip, TCP_PING_PORT) || isReachableByIcmp(ip); + return new AbstractMap.SimpleEntry<>(ip, isOnline); + }); + } + + List>> futures = PING_EXECUTOR.invokeAll(tasks); + Map results = new ConcurrentHashMap<>(); + for (Future> future : futures) { + Map.Entry entry = future.get(); + results.put(entry.getKey(), entry.getValue()); + } + return results; + } +} diff --git a/src/main/java/com/yj/earth/common/util/PoiExporter.java b/src/main/java/com/yj/earth/common/util/PoiExporter.java index e53d0aa..4d67218 100644 --- a/src/main/java/com/yj/earth/common/util/PoiExporter.java +++ b/src/main/java/com/yj/earth/common/util/PoiExporter.java @@ -35,12 +35,12 @@ public class PoiExporter { } log.info("查询完成、共获取到[{}]个地区码(含子区域)", adcodeList.size()); - log.info("开始导出数据到新文件:{}", newDbPath); + log.info("开始导出数据到新文件: {}", newDbPath); exportDataToNewDb(originalDbPath, newDbUrl, adcodeList); - log.info("=== [{}]的POI数据导出成功!文件路径:{} ===", areaName, newDbPath); + log.info("=== [{}]的POI数据导出成功!文件路径: {} ===", areaName, newDbPath); } catch (SQLException e) { - log.error("=== [{}]的POI数据导出失败!原因:{} ===", areaName, e.getMessage(), e); + log.error("=== [{}]的POI数据导出失败!原因: {} ===", areaName, e.getMessage(), e); throw e; } } @@ -57,7 +57,7 @@ public class PoiExporter { log.warn("未查询到[{}]对应的根地区码", areaName); return adcodeList; } - log.info("查询到[{}]的根地区码:{}", areaName, rootAdcode); + log.info("查询到[{}]的根地区码: {}", areaName, rootAdcode); // 递归查询所有子adcode String recursiveSql = "WITH RECURSIVE sub_areas(adcode) AS (" + @@ -75,7 +75,7 @@ public class PoiExporter { } } } catch (SQLException e) { - log.error("查询地区码列表失败!原因:{}", e.getMessage(), e); + log.error("查询地区码列表失败!原因: {}", e.getMessage(), e); throw e; } return adcodeList; @@ -94,7 +94,7 @@ public class PoiExporter { } } } catch (SQLException e) { - log.error("查询[{}]的根地区码失败!原因:{}", areaName, e.getMessage(), e); + log.error("查询[{}]的根地区码失败!原因: {}", areaName, e.getMessage(), e); throw e; } return null; @@ -146,7 +146,7 @@ public class PoiExporter { // 每10000条打印一次进度 if (totalCount % batchSize == 0) { insertPs.executeBatch(); - log.info("已处理数据:{}条", totalCount); + log.info("已处理数据: {}条", totalCount); } } // 处理剩余数据 @@ -158,7 +158,7 @@ public class PoiExporter { } } } catch (SQLException e) { - log.error("新数据库操作失败!原因:{}", e.getMessage(), e); + log.error("新数据库操作失败!原因: {}", e.getMessage(), e); throw e; } } diff --git a/src/main/java/com/yj/earth/common/util/SQLiteUtil.java b/src/main/java/com/yj/earth/common/util/SQLiteUtil.java index 81bcf4d..0ecb347 100644 --- a/src/main/java/com/yj/earth/common/util/SQLiteUtil.java +++ b/src/main/java/com/yj/earth/common/util/SQLiteUtil.java @@ -27,10 +27,10 @@ public class SQLiteUtil { // 统一日期格式(LocalDateTime) private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - // 连接池缓存:key=数据库文件路径、value=DBCP2数据源(支持多连接复用) + // 连接池缓存: key=数据库文件路径、value=DBCP2数据源(支持多连接复用) private static final Map DATA_SOURCE_POOL = new ConcurrentHashMap<>(); - // 字段缓存:缓存类的字段映射(避免反射重复开销) + // 字段缓存: 缓存类的字段映射(避免反射重复开销) private static final Map, Map> FIELD_CACHE = new ConcurrentHashMap<>(); @@ -64,17 +64,17 @@ public class SQLiteUtil { dataSource.setUrl("jdbc:sqlite:" + dbFilePath); // 2. 连接池核心参数(根据并发量调整、SQLite不建议过多连接) - dataSource.setMaxTotal(30); // 最大连接数:20-50(根据服务器CPU/内存调整) - dataSource.setMaxIdle(15); // 最大空闲连接:保留部分连接避免频繁创建 - dataSource.setMinIdle(5); // 最小空闲连接:保证基础并发响应速度 - dataSource.setTimeBetweenEvictionRunsMillis(60000); // 连接检测间隔:1分钟 - dataSource.setMinEvictableIdleTimeMillis(300000); // 连接空闲超时:5分钟(清理长期空闲连接) + dataSource.setMaxTotal(30); // 最大连接数: 20-50(根据服务器CPU/内存调整) + dataSource.setMaxIdle(15); // 最大空闲连接: 保留部分连接避免频繁创建 + dataSource.setMinIdle(5); // 最小空闲连接: 保证基础并发响应速度 + dataSource.setTimeBetweenEvictionRunsMillis(60000); // 连接检测间隔: 1分钟 + dataSource.setMinEvictableIdleTimeMillis(300000); // 连接空闲超时: 5分钟(清理长期空闲连接) // 3. 连接有效性验证(避免使用失效连接) dataSource.setTestOnBorrow(true); // 借连接时验证 dataSource.setTestOnReturn(false); // 还连接时不验证(减少开销) dataSource.setValidationQuery("SELECT 1"); // 轻量验证SQL(SQLite支持) - dataSource.setValidationQueryTimeout(2); // 验证超时:2秒 + dataSource.setValidationQueryTimeout(2); // 验证超时: 2秒 // 4. PreparedStatement缓存(减少SQL解析开销) dataSource.setPoolPreparedStatements(true); @@ -83,11 +83,11 @@ public class SQLiteUtil { // 5. SQLite底层性能优化(关键!提升并发能力) try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) { - stmt.execute("PRAGMA journal_mode=WAL;"); // 启用WAL模式:支持多读者+单写者(核心优化) + stmt.execute("PRAGMA journal_mode=WAL;"); // 启用WAL模式: 支持多读者+单写者(核心优化) stmt.execute("PRAGMA cache_size=-20000;"); // 页面缓存20MB(负号表示KB单位、内存足可调大) - stmt.execute("PRAGMA synchronous=NORMAL;"); // 同步级别:平衡性能与安全(崩溃最多丢WAL日志) + stmt.execute("PRAGMA synchronous=NORMAL;"); // 同步级别: 平衡性能与安全(崩溃最多丢WAL日志) stmt.execute("PRAGMA temp_store=MEMORY;"); // 临时表/索引存内存(减少磁盘IO) - stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时:2秒(避免瞬时并发锁等待) + stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时: 2秒(避免瞬时并发锁等待) } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("请检查路径是否存在"); @@ -108,7 +108,7 @@ public class SQLiteUtil { try { dataSource.close(); // DBCP2会自动关闭所有活跃/空闲连接 } catch (SQLException e) { - System.err.println("关闭SQLite数据源失败(路径:" + dbFilePath + "):" + e.getMessage()); + System.err.println("关闭SQLite数据源失败(路径: " + dbFilePath + "): " + e.getMessage()); } } } @@ -121,7 +121,7 @@ public class SQLiteUtil { try { dataSource.close(); } catch (SQLException e) { - System.err.println("关闭SQLite数据源失败:" + e.getMessage()); + System.err.println("关闭SQLite数据源失败: " + e.getMessage()); } } // 清理缓存(避免内存泄漏) @@ -129,7 +129,7 @@ public class SQLiteUtil { FIELD_CACHE.clear(); } - // JVM关闭钩子:确保程序退出时释放所有数据源资源 + // JVM关闭钩子: 确保程序退出时释放所有数据源资源 static { Runtime.getRuntime().addShutdownHook(new Thread(SQLiteUtil::closeAllDataSources)); } @@ -138,7 +138,7 @@ public class SQLiteUtil { // ========================== 数据查询核心方法 ========================== /** - * 执行查询并返回单个对象(优化版:连接池+字段缓存) + * 执行查询并返回单个对象(优化版: 连接池+字段缓存) * * @param dbFilePath 数据库路径 * @param sql 查询SQL @@ -152,7 +152,7 @@ public class SQLiteUtil { } /** - * 执行查询并返回对象列表(优化版:预构建字段映射+资源自动回收) + * 执行查询并返回对象列表(优化版: 预构建字段映射+资源自动回收) * * @param dbFilePath 数据库路径 * @param sql 查询SQL @@ -169,7 +169,7 @@ public class SQLiteUtil { // 预加载字段映射(缓存生效、避免重复反射) Map fieldMap = getFieldMap(clazz); - // try-with-resources:自动关闭Connection、PreparedStatement、ResultSet + // try-with-resources: 自动关闭Connection、PreparedStatement、ResultSet try (Connection conn = getConnection(dbFilePath); PreparedStatement pstmt = createPreparedStatement(conn, sql, params); ResultSet rs = pstmt.executeQuery()) { @@ -193,20 +193,20 @@ public class SQLiteUtil { resultList.add(obj); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { - throw new SQLException("创建对象实例失败(类型:" + clazz.getName() + ")", e); + throw new SQLException("创建对象实例失败(类型: " + clazz.getName() + ")", e); } } } catch (SQLException e) { // 异常时关闭当前数据源(避免后续请求使用异常连接) closeDataSource(dbFilePath); - throw new SQLException("执行查询失败(SQL:" + sql + ")", e); + throw new SQLException("执行查询失败(SQL: " + sql + ")", e); } return resultList; } /** - * 执行增删改SQL(优化版:连接池+自动提交) + * 执行增删改SQL(优化版: 连接池+自动提交) * * @param dbFilePath 数据库路径 * @param sql 增删改SQL @@ -223,12 +223,12 @@ public class SQLiteUtil { return pstmt.executeUpdate(); } catch (SQLException e) { closeDataSource(dbFilePath); - throw new SQLException("执行增删改失败(SQL:" + sql + ")", e); + throw new SQLException("执行增删改失败(SQL: " + sql + ")", e); } } /** - * 执行计数查询(优化版:轻量结果处理) + * 执行计数查询(优化版: 轻量结果处理) * * @param dbFilePath 数据库路径 * @param sql 计数SQL(如SELECT COUNT(*) ...) @@ -246,7 +246,7 @@ public class SQLiteUtil { return rs.next() ? rs.getInt(1) : 0; } catch (SQLException e) { closeDataSource(dbFilePath); - throw new SQLException("执行计数查询失败(SQL:" + sql + ")", e); + throw new SQLException("执行计数查询失败(SQL: " + sql + ")", e); } } @@ -270,12 +270,12 @@ public class SQLiteUtil { pstmt.execute(); } catch (SQLException e) { closeDataSource(dbFilePath); - throw new SQLException("执行DDL失败(SQL:" + sql + "、路径:" + dbFilePath + ")", e); + throw new SQLException("执行DDL失败(SQL: " + sql + "、路径: " + dbFilePath + ")", e); } } /** - * 重载:无参数的DDL执行 + * 重载: 无参数的DDL执行 */ public static void executeDDL(String dbFilePath, String sql) throws SQLException { executeDDL(dbFilePath, sql, null); @@ -408,7 +408,7 @@ public class SQLiteUtil { field.set(obj, convertedValue); } } catch (IllegalAccessException e) { - System.err.println("警告:字段赋值失败(字段:" + field.getName() + "、值类型:" + value.getClass().getName() + "):" + e.getMessage()); + System.err.println("警告: 字段赋值失败(字段: " + field.getName() + "、值类型: " + value.getClass().getName() + "): " + e.getMessage()); } } @@ -452,7 +452,7 @@ public class SQLiteUtil { } // 不支持的类型转换(打印警告) - System.err.println("警告:不支持的类型转换(目标类型:" + targetType.getName() + "、原始值类型:" + value.getClass().getName() + ")"); + System.err.println("警告: 不支持的类型转换(目标类型: " + targetType.getName() + "、原始值类型: " + value.getClass().getName() + ")"); return null; } @@ -461,7 +461,7 @@ public class SQLiteUtil { try { return Integer.parseInt(value.trim()); } catch (NumberFormatException e) { - System.err.println("警告:字符串转Integer失败(值:" + value + ")"); + System.err.println("警告: 字符串转Integer失败(值: " + value + ")"); return null; } } @@ -470,7 +470,7 @@ public class SQLiteUtil { try { return Long.parseLong(value.trim()); } catch (NumberFormatException e) { - System.err.println("警告:字符串转Long失败(值:" + value + ")"); + System.err.println("警告: 字符串转Long失败(值: " + value + ")"); return null; } } @@ -479,7 +479,7 @@ public class SQLiteUtil { try { return Double.parseDouble(value.trim()); } catch (NumberFormatException e) { - System.err.println("警告:字符串转Double失败(值:" + value + ")"); + System.err.println("警告: 字符串转Double失败(值: " + value + ")"); return null; } } @@ -489,7 +489,7 @@ public class SQLiteUtil { try { return LocalDateTime.parse((String) value, LOCAL_DATE_TIME_FORMATTER); } catch (DateTimeParseException e) { - System.err.println("警告:字符串转LocalDateTime失败(值:" + value + ")"); + System.err.println("警告: 字符串转LocalDateTime失败(值: " + value + ")"); return null; } } else if (value instanceof Timestamp) { @@ -513,7 +513,7 @@ public class SQLiteUtil { try { return blob.getBytes(1, (int) blob.length()); } catch (SQLException e) { - System.err.println("警告:Blob转byte[]失败:" + e.getMessage()); + System.err.println("警告: Blob转byte[]失败: " + e.getMessage()); } } return null; diff --git a/src/main/java/com/yj/earth/common/util/SdkUtil.java b/src/main/java/com/yj/earth/common/util/SdkUtil.java index cf912b8..3fb354e 100644 --- a/src/main/java/com/yj/earth/common/util/SdkUtil.java +++ b/src/main/java/com/yj/earth/common/util/SdkUtil.java @@ -139,7 +139,7 @@ public class SdkUtil { // 判断操作系统类型 boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); - // 构建JDK路径:根目录/jdk/bin/java(或java.exe) + // 构建JDK路径: 根目录/jdk/bin/java(或java.exe) String javaRelativePath = "jdk" + File.separator + "bin" + File.separator + (isWindows ? "java.exe" : "java"); return new File(rootDir, javaRelativePath).getAbsolutePath(); diff --git a/src/main/java/com/yj/earth/datasource/DatabaseManager.java b/src/main/java/com/yj/earth/datasource/DatabaseManager.java index 24e2996..69a18e3 100644 --- a/src/main/java/com/yj/earth/datasource/DatabaseManager.java +++ b/src/main/java/com/yj/earth/datasource/DatabaseManager.java @@ -83,7 +83,7 @@ public class DatabaseManager { ENTITY_CLASSES = Collections.unmodifiableList(classes); } - // 新增方法:提前初始化路径 + // 新增方法: 提前初始化路径 public static void initializePath(String path) { if (!isPathInitialized) { FOLDER_NAME = path;