diff --git a/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java b/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java index 317af6e..c8d0e66 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java @@ -2,8 +2,10 @@ package com.yfd.business.css.service; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.JsonNode; import com.yfd.business.css.domain.Scenario; import com.yfd.business.css.domain.ScenarioResult; +import com.yfd.business.css.domain.AlgorithmModel; import com.yfd.business.css.model.DeviceStepInfo; import com.yfd.business.css.model.InferRequest; import com.yfd.business.css.model.InferResponse; @@ -121,20 +123,30 @@ public class DeviceInferService { String modelRelPath = model.getModelPath(); log.debug("modelRelPath={}", modelRelPath); - // 解析模型的特征映射(feature_map_snapshot)以进行特征过滤 + // 解析模型的特征映射(feature_map_snapshot),优先以 input_cols 为准进行特征过滤 List requiredFeatures = new ArrayList<>(); if (model.getFeatureMapSnapshot() != null && !model.getFeatureMapSnapshot().isBlank()) { try { - // 解析 {"features": ["diameter", "height", ...]} 或者直接是一个 List + // 由于数据库里存的可能是转义的 JSON 字符串,我们需要多次解析直到它变成真正的 Object JsonNode fNode = objectMapper.readTree(model.getFeatureMapSnapshot()); + if (fNode.isTextual()) { + fNode = objectMapper.readTree(fNode.asText()); + } + if (fNode.isArray()) { for (JsonNode node : fNode) requiredFeatures.add(node.asText()); + } else if (fNode.has("input_cols") && fNode.get("input_cols").isArray()) { + for (JsonNode node : fNode.get("input_cols")) requiredFeatures.add(node.asText()); } else if (fNode.has("features") && fNode.get("features").isArray()) { + // 兼容旧版本 for (JsonNode node : fNode.get("features")) requiredFeatures.add(node.asText()); } + log.info("Parsed requiredFeatures from feature_map_snapshot: {}", requiredFeatures); } catch (Exception e) { log.warn("解析特征映射快照失败: {}", e.getMessage()); } + } else { + log.warn("模型 feature_map_snapshot 为空,将不进行特征过滤"); } // 将相对路径转换为绝对路径 @@ -202,11 +214,18 @@ public class DeviceInferService { if (requiredFeatures != null && !requiredFeatures.isEmpty()) { for (String featureName : requiredFeatures) { // 如果设备属性中包含模型需要的特征,则提取;否则填 0 或抛出异常(这里选择填 0.0,具体看业务要求) - filteredProps.put(featureName, allProps.getOrDefault(featureName, 0.0)); + Object value = allProps.get(featureName); + if (value == null) { + log.warn("设备 {} 缺少必需的特征: {},默认填充 0.0", device.getDeviceId(), featureName); + value = 0.0; + } + filteredProps.put(featureName, value); } + log.info("设备 {} 的过滤后特征: {}", device.getDeviceId(), filteredProps); } else { // 如果模型没有保存特征快照,退退回发送全部属性 filteredProps.putAll(allProps); + log.warn("没有 requiredFeatures,发送全部属性: {}", filteredProps); } deviceData.put("features", filteredProps); diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java index 22f19fc..0963ad6 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java @@ -41,10 +41,10 @@ import java.util.regex.Pattern; @Service public class ModelTrainServiceImpl extends ServiceImpl implements ModelTrainService { - @Value("${file-space.upload-path:./data/uploads/}") + @Value("${file-space.dataset-path:D:/keff_dataSpace/datasets/}") private String uploadPath; - @Value("${file-space.model-path:E:/python_coding/keffCenter/models/}") + @Value("${file-space.model-path:D:/keff_dataSpace/models/}") private String modelPath; @Value("${python.api.url:http://localhost:8000}") @@ -373,7 +373,7 @@ public class ModelTrainServiceImpl extends ServiceImpl 获取全局 `algorithmType` -> 加载模型 -> 批量推理。 + * **新逻辑**: + 1. 解析 `deviceAlgoConfig` JSON。 + 2. 遍历所有设备,确定每个设备的算法类型(优先取设备配置,无配置则取情景默认)。 + 3. **重分组**:将设备按 `(deviceType, algorithmType)` 组合进行分组。例如:`{ "CylindricalTank_GPR": [dev1, dev2], "CylindricalTank_MLP": [dev3] }`。 + 4. **分批推理**:遍历新的分组,分别加载对应的模型路径进行推理调用。 + + +--- + diff --git a/变更需求.md b/变更需求.md new file mode 100644 index 0000000..e1d7c0a --- /dev/null +++ b/变更需求.md @@ -0,0 +1,35 @@ +1.情景表添加keff阈值设置字段:在情景配置中新增keff阈值参数,当模拟过程中keff值达到或超过设定阈值时,系统对keff值进行红色高亮显示,用于风险预警。 +2.情景表,有算法类型字段,现在要求支持按设备设置所使用的算法类型,以满足不同设备在同一情景下采用差异化算法模型的需求。 +3.始发事件配置增强:多时间点、多数值的配置方式,支持通过excel模板批量导入。 +4.开放模型训练面板 +新增模型训练模块,主要包括: +选择算法类型、设备类型、选择样本数据集、支持模型参数配置,进行模型训练,展示模型训练指标(误差),支持模型文件保存。 +初步计划模型训练面板设计: + 设备类型:(下拉选择) + 算法类型:(下拉选择) + 样本数据集:根据时间范围选自临界数据库,或者上传的excel文件。 + 模型参数配置:(文本框输入) + 训练按钮:(点击触发训练) + 训练指标展示:(实时显示训练误差等指标) + 模型文件保存:(保存训练好的模型文件) + 训练完成后,需要在模型表里面加一条记录。 + + +当前已有模型表:CREATE TABLE `algorithm_model` ( + `algorithm_model_id` char(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '模型版本唯一ID', + `algorithm_type` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '算法类型(如GPR/MLP/FastRBF)', + `device_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '设备类型(如CylindricalTank/AnnularTank)', + `version_tag` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '版本标识(如v1或时间戳)', + `model_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '模型文件路径(pipeline.pkl)', + `feature_map_snapshot` json NOT NULL COMMENT '特征映射快照(Java属性→Python特征键)', + `metrics` json DEFAULT NULL COMMENT '训练评估指标(rmse/mae/r2/maxe)', + `metrics_image_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '误差散点图路径', + `trained_at` datetime DEFAULT NULL COMMENT '训练完成时间', + `is_current` tinyint NOT NULL DEFAULT '0' COMMENT '是否当前激活版本(1是,0否)', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + `modifier` varchar(40) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后修改人', + PRIMARY KEY (`algorithm_model_id`), + UNIQUE KEY `uk_algo_dev_ver` (`algorithm_type`,`device_type`,`version_tag`) COMMENT '同算法+设备类型的版本唯一', + KEY `idx_current` (`algorithm_type`,`device_type`,`is_current`) COMMENT '当前版本查询索引' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/增加材料类型的支持.md b/增加材料类型的支持.md new file mode 100644 index 0000000..9373d13 --- /dev/null +++ b/增加材料类型的支持.md @@ -0,0 +1,73 @@ +# 增加材料类型(material_type)支持的详细实施方案 + +根据现有的系统架构和需求,为算法模型增加“材料类型(material_type)”维度的支持,需要从数据库、实体类、核心业务逻辑(特别是路径解析与模型发布)以及状态控制等方面进行细化和落地。以下是详细的分析与实施细则: + +## 1. 数据库层面修改 (Database) +- **目标表**: `algorithm_model` +- **操作**: 增加 `material_type` 字段,用于区分该模型适用的材料(如 `Pu` 等)。 +- **SQL 参考**: + ```sql + ALTER TABLE algorithm_model + ADD COLUMN material_type VARCHAR(50) DEFAULT NULL COMMENT '材料类型' AFTER device_type; + ``` + +## 2. 实体类及映射修改 (Domain) +- **目标文件**: `AlgorithmModel.java` +- **操作**: 增加对应的 Java 属性,并使用 MyBatis-Plus 的 `@TableField` 进行映射。 +- **代码参考**: + ```java + /** + * 材料类型 (例如: Pu) + */ + @TableField("material_type") + private String materialType; + ``` + +## 3. 核心业务逻辑修改 (Service) + +### 3.1 提取材料类型 (Path Parsing) +- **目标文件**: `ModelTrainServiceImpl.java` -> `publishModel` 方法。 +- **逻辑细化**: `ModelTrainTask` 中保存的相对路径(如 `runs/GPR/AnnularTank/Pu/edb0e340-51e6-46bb-9c08-5ba38762089e/pipeline.pkl`)包含了材料信息。 +- **解析规则**: + 需要对 `task.getModelOutputPath()` 或 `task.getModelRelativePath()` 进行字符串分割(兼容 `/` 和 `\`)。 + 标准的目录层级为:`runs` / `{算法类型}` / `{设备类型}` / `{材料类型}` / `{TaskID}` / `{文件名}`。 + - 第 0 层: `runs` + - 第 1 层: `algorithm_type` (如 `GPR`) + - 第 2 层: `device_type` (如 `AnnularTank`) + - **第 3 层**: `material_type` (如 `Pu`) <- **需要提取的字段** + - 第 4 层: `task_id` + +### 3.2 更新模型发布路径 (Publish Path Assembly) +- **目标文件**: `ModelTrainServiceImpl.java` -> `publishModel` 方法。 +- **旧路径结构**: `{modelRoot}/{algorithmType}/{deviceType}/{versionTag}/` +- **新路径结构**: 将提取出的 `materialType` 拼接到发布路径中,使发布目录也具备材料维度。 + **新结构**: `{modelRoot}/{algorithmType}/{deviceType}/{materialType}/{versionTag}/` +- **持久化**: + 在构建要插入到 `algorithm_model` 表的记录时,不仅要保存新拼接出的模型绝对/相对路径,还要将解析出的 `materialType` 赋值给 `AlgorithmModel` 对象。 + +### 3.3 激活模型的唯一性约束控制 (Active Model Logic) +- **业务规则**: “每种设备 + 每种算法类型 + 每种材料类型” 只能由一个激活(`is_active = 1` 或者对应状态码)的算法。 +- **目标文件**: 处理模型发布或激活的 Service 方法(如 `AlgorithmModelServiceImpl` 中如果存在独立激活方法,或直接在 `publishModel` 之后的操作)。 +- **逻辑细化**: 在将某模型记录标记为激活前,需要执行排他更新: + ```java + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("device_type", model.getDeviceType()) + .eq("algorithm_type", model.getAlgorithmType()) + .eq("material_type", model.getMaterialType()) // 新增材料类型维度限制 + .set("is_active", 0); // 假设 0 为未激活,1 为激活 + algorithmModelMapper.update(null, updateWrapper); + ``` + +## 4. API 接口层面 (Controller) +- **目标文件**: `ModelTrainController.java` +- **接口定义**: + ```java + @PostMapping("/publish") + public ResponseResult publish(@RequestBody Map body) { + String taskId = body.get("taskId"); + String versionTag = body.get("versionTag"); + boolean success = modelTrainService.publishModel(taskId, versionTag); + return success ? ResponseResult.success() : ResponseResult.error("发布失败"); + } + ``` +- **结论**: 接口的入参定义**无需修改**。材料类型信息属于内部推导数据,完全可以由 Service 层通过 `taskId` 找到对应的 Task 数据,再解析路径得到 `material_type`,对前端调用保持透明。 \ No newline at end of file diff --git a/异步推理并保存结果.md b/异步推理并保存结果.md new file mode 100644 index 0000000..dbfe7b5 --- /dev/null +++ b/异步推理并保存结果.md @@ -0,0 +1,49 @@ +# 异步推理并保存结果 (SimInferService 流程解析) + +在仿真计算完成后,系统需要将每一步的计算结果发送给 Python 推理服务,以预测关键指标(如 Keff)。这个过程由 `SimInferService` 和 `DeviceInferService` 协同完成。 + +## 1. 推理数据不是一次性全部发送 + +**核心结论:** 并不是把所有结果封装为一个大 Request 调用一次推理接口,而是**进行了精细的多级分组后,分别调用推理接口**。 + +因为不同的设备、不同的材料、以及用户为设备配置的不同算法,都对应着不同的**模型文件(Pipeline/Model)**。一次推理请求只能加载一个模型文件,所以必须将必须使用同一模型的数据分在同一批次发送。 + +## 2. 数据转换与分组流程 + +整个流程分为两部分:在 `SimInferService` 中进行一级分组,然后在 `DeviceInferService` 中进行二级和三级分组。 + +### 2.1 第一级分组:按设备类型 (DeviceType) 分组 +**位置:** `SimInferService.asyncInferAndSave` + +1. **展平时间轴 (Flatten Timeline)**:仿真引擎返回的 `SimContext` 是按时间步(Step)组织的。代码首先遍历每个时间步,将散落的属性(如液位、浓度)按 `unitId` 聚合。 +2. **注入元数据**:通过 `SimUnit`,将静态属性(如设备固有的直径、高度)以及新加入的 `materialType`(如 U, Pu)合并到设备的属性集合中,生成 `DeviceStepInfo` 对象。 +3. **一级分组**:将所有时间步的 `DeviceStepInfo` 根据 **设备类型 (`deviceType`)** 放入 `Map> groupedDevices` 中。 + - *例如:所有的 CylindricalTank 数据在一组,AnnularTank 在另一组。* + +### 2.2 第二级分组:按算法类型 (AlgorithmType) 分组 +**位置:** `DeviceInferService.processDeviceInference` + +1. **读取配置**:获取全局配置的算法(如 GPR),以及针对特定设备独立配置的算法(`deviceAlgoConfig`)。 +2. **二级分组**:在某一个设备类型(如 CylindricalTank)的数据列表中,进一步按 **算法类型 (`algoType`)** 分组。 + - *例如:CylindricalTank 组被拆分为 -> 使用 GPR 算法的批次、使用 MLP 算法的批次。* + +### 2.3 第三级分组:按材料类型 (MaterialType) 分组 +**位置:** `DeviceInferService.processDeviceInference` + +1. **三级分组**:在特定的算法批次中,进一步按 **材料类型 (`materialType`)** 分组。 + - *例如:使用 GPR 的 CylindricalTank 批次被拆分为 -> 材料为 U 的批次、材料为 Pu 的批次。* + +## 3. 发起推理请求 + +经过上述三级分组后,我们得到了一个**高度同质化**的数据批次。对于每一个这样的最终批次: + +1. **查询模型**:调用 `algorithmModelService.getCurrentModelPath(algoType, deviceType, materialType)`。因为这三个维度相同,所以它们一定使用同一个物理模型文件(`.pkl`)。 +2. **构建 Request**:将这批数据(包含所有相关设备在所有时间步的状态)组装成一个 `InferRequest`。 +3. **发起调用**:向 Python 服务发送 HTTP POST 请求 `/v1/infer`。 +4. **入库保存**:Python 返回每个设备在每个时间步的预测结果(如 `keff`),Java 端解析这些结果并批量保存到 `scenario_result` 表中。 + +## 4. 异常与状态管理 + +- 整个过程是加上了 `@Async` 异步执行的,不会阻塞前端获取基础仿真数据。 +- 如果某一个批次因为找不到模型(例如上述终端日志中 `GPR + CylindricalTank + U` 的模型未配置),代码会记录错误并标记 `hasAnyError = true`,然后跳过该批次(`continue`)。 +- **兜底机制**:如果所有的批次都失败或找不到模型(`!hasAnySuccess && hasAnyError`),会抛出 `RuntimeException`。这会被外层 `SimInferService` 捕获,并将该情景的状态更新为 **"3" (失败)**。 \ No newline at end of file diff --git a/推理接口对材料类型的支持.md b/推理接口对材料类型的支持.md new file mode 100644 index 0000000..a6861a0 --- /dev/null +++ b/推理接口对材料类型的支持.md @@ -0,0 +1,112 @@ +# 推理接口对材料类型的支持分析与实施方案 + +根据最新的需求,`algorithm_model` 表已支持 `material_type` 字段,用于区分不同材料(如 `Pu`、`U` 等)的专用模型。为了在仿真推理阶段能够正确加载对应的模型,需要对现有的推理数据流进行改造,确保“材料类型”这一关键上下文能够从仿真计算传递到推理服务。 + +## 1. 现状分析 (As-Is) + +### 1.1 数据流现状 + +1. **SimController**: 调用 `simBuilder.buildUnits` 构建仿真单元。 +2. **SimBuilder**: 从 `Topology` 和 `MaterialService` 读取数据,构建 `SimUnit`。 + - *当前缺失*: `SimUnit` 仅包含 `materialId`,**未包含** `materialType`。 + - *当前缺失*: `Material` 实体类中也未明确发现 `materialType` 字段(仅有 `name`, `concentrations` 等)。 +3. **SimInferService**: 将 `SimContext` 转换为 `DeviceStepInfo`,并按 `deviceType` 分组。 + - *当前缺失*: 分组逻辑未考虑材料类型。 +4. **DeviceInferService**: 接收分组数据,再次按 `algorithmType` 分组,然后调用 `algorithmModelService.getCurrentModelPath(algo, device)`。 + - *当前缺失*: 调用获取模型路径接口时,缺少 `materialType` 参数,导致无法精确定位到特定材料的模型(可能默认返回第一个或报错)。 + +### 1.2 关键断点 + +- **源头缺失**: 仿真单元(SimUnit)不知道自己处理的是什么类型的材料。 +- **传递中断**: 推理服务(InferService)无法获取材料类型信息。 +- **查询不足**: 模型匹配逻辑未利用材料类型进行过滤。 + +## 2. 实施方案 (To-Be) + +### 2.1 第一步:完善领域模型 (Domain & Model) + +#### 1. 不修改 `Material` 实体 + +需要根据材料表获取的是U浓度值或者Pu浓度值,决定Material\_type = U  或者Pu + +这个主要是模型根据材料类型训练来的,所以只能把数据也做了材料分类。 + +#### 2. 修改 `SimUnit` Record + +在 `SimUnit` 中增加 `materialType` 字段,用于在内存中流转该信息。 + +```java +public record SimUnit( + String unitId, + String deviceId, + String materialId, + String deviceType, + String materialType, // 新增 + Map staticProperties +) {} +``` + +#### 3. 修改 `DeviceStepInfo` + +在 `DeviceStepInfo` 中增加 `materialType` 字段,确保分组时能携带此信息。 + +```java +public class DeviceStepInfo { + // ... 原有字段 + private String materialType; // 新增 + + // getter/setter ... +} +``` + +### 2.2 第二步:更新构建逻辑 (SimBuilder) + +在 `SimBuilder.buildUnits` 方法中: + +1. 在查询 `Material` 信息时,根据材料属性(U浓度/Pu浓度等)推导 `materialType`。 + - 规则示例:若 `pu_concentration > 0` 则为 `Pu`,否则若 `u_concentration > 0` 则为 `U`,否则为 `unknown`。 +2. 在构造 `SimUnit` 时,将 `material_type` 注入。 + +### 2.3 第三步:更新推理准备逻辑 (SimInferService) + +在 `SimInferService.asyncInferAndSave` 方法中: + +1. 在将 `SimContext` 转换为 `DeviceStepInfo` 时,从对应的 `SimUnit` 中提取 `materialType` 并赋值给 `DeviceStepInfo`。 +2. **分组策略变更**: + - 原: `Map>` (Key: DeviceType) + - 新: 保持外层按 `DeviceType` 分组不变,利用 `DeviceStepInfo` 内部携带的 `materialType` 在下一步进行细分。 + +### 2.4 第四步:更新推理执行逻辑 (DeviceInferService) + +在 `DeviceInferService.processDeviceInference` 方法中: + +1. 接收到的数据是按 `DeviceType` 分组的。 +2. **三级分组**: 遍历设备列表,进一步按 `AlgorithmType` **AND** `MaterialType` 进行分组。 + - 构造复合键或嵌套 Map: `Map>>` (AlgoType -> MaterialType -> List)。 +3. **模型匹配**: + ```java + String modelPath = algorithmModelService.getCurrentModelPath(currentAlgoType, deviceType, currentMaterialType); + ``` +4. **请求构建**: 确保找到的模型是与当前批次数据的材料类型匹配的。 + +### 2.5 第五步:更新模型查询接口 (AlgorithmModelService) + +完善 `getCurrentModelPath` 方法,增加 `materialType` 参数,并更新 MyBatis-Plus 查询条件。 + +```java +String getCurrentModelPath(String algorithmType, String deviceType, String materialType); +``` + +## 3. 任务清单 (Action Items) + +1. **Domain & Model**: + - [ ] 更新 `SimUnit.java` 增加 `materialType`。 + - [ ] 更新 `DeviceStepInfo.java` 增加 `materialType`。 +2. **Builder**: + - [ ] 修改 `SimBuilder.java`,实现材料类型推导逻辑并填充 `SimUnit`。 +3. **Service**: + - [ ] 修改 `SimInferService.java`,在转换时将 `materialType` 传递给 `DeviceStepInfo`。 + - [ ] 修改 `DeviceInferService.java`,实现 (Algo + Material) 的组合分组与模型匹配调用。 + - [ ] 修改 `AlgorithmModelService.java` 及实现类,增加带 `materialType` 的路径查询方法。 +4. **Controller**: + - [ ] `SimController` 无需修改代码,验证端到端流程。 diff --git a/推理数据封装问题定位.md b/推理数据封装问题定位.md new file mode 100644 index 0000000..0ee0916 --- /dev/null +++ b/推理数据封装问题定位.md @@ -0,0 +1,2 @@ +# 推理数据: + diff --git a/日志记录及日志文件.md b/日志记录及日志文件.md new file mode 100644 index 0000000..fbf9756 --- /dev/null +++ b/日志记录及日志文件.md @@ -0,0 +1,114 @@ +# 日志记录及日志文件分析与建议 + +在目前的 `framework` 框架和 `business-css` 业务模块中,日志系统已经基于 **Logback** 和 **Spring AOP** 搭建了良好的基础。为了在 `business-css` 模块中更好地使用日志并规范输出,以下是综合分析与具体建议: + +## 1. 当前日志架构分析 + +### 1.1 日志框架与配置 +- **底层框架**: 使用的是 Spring Boot 默认的 Logback。 +- **配置文件**: 位于 `framework/src/main/resources/logback-spring.xml`。 +- **输出策略**: + - **控制台 (CONSOLE)**: 强制使用 UTF-8 编码,包含时间、线程、级别、类名和消息。 + - **文件 (FILE)**: 采用按天滚动的策略 (`TimeBasedRollingPolicy`),最多保留 30 天的历史日志。 +- **配置文件覆盖**: 在 `business-css/src/main/resources/application-business.yml` 中,通过 `logging.file.name: logs/business-css.log` 重写了日志文件的路径和名称。这意味着启动 `business-css` 模块时,日志会自动写入该文件。 + +### 1.2 业务操作日志 (AOP 切面) +- **注解支持**: `framework` 中提供了自定义注解 `@com.yfd.platform.annotation.Log(value="操作描述", module="所属模块")`。 +- **切面拦截**: `LogAspect.java` 会拦截带有该注解的方法,自动提取请求信息(IP、浏览器、操作人等)以及方法执行的耗时和结果,并调用 `ISysLogService` 将这些操作日志**持久化到数据库**中。 + +--- + +## 2. 针对 business-css 模块的实施建议 + +为了在 `business-css` 模块中实现规范、高效的日志记录,建议从以下两个维度(**系统运行日志** 和 **用户操作日志**)入手: + +### 2.1 用户操作日志 (入库) —— 针对 Controller 层 +对于需要审计、追踪用户行为的核心业务接口(如增删改操作),应直接复用框架提供的 `@Log` 注解。 + +**实施方法:** +在 `business-css` 的 `Controller` 层方法上添加 `@Log` 注解。 + +**代码示例:** +```java +import com.yfd.platform.annotation.Log; + +@RestController +@RequestMapping("/project") +public class ProjectController { + + @Log(value = "新增仿真项目", module = "项目管理") + @PostMapping + public ResponseResult addProject(@RequestBody Project project) { + // 业务逻辑... + return ResponseResult.success(); + } +} +``` +**效果**:用户的操作会被自动拦截并写入数据库的系统日志表中,方便后台管理界面的“日志管理”模块进行展示和审计。 + +### 2.2 系统运行与调试日志 (写文件) —— 针对 Service / 核心逻辑层 +对于系统内部的运行状态、复杂计算过程(如仿真计算、模型推理等)以及异常捕获,应该使用标准的日志框架(SLF4J)打印日志,以便于排查 Bug。 + +**实施方法:** +在需要打印日志的类上使用 Lombok 的 `@Slf4j` 注解,然后使用 `log.info()`, `log.error()` 等方法。 + +**代码示例:** +```java +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class SimInferServiceImpl implements SimInferService { + + public void asyncInferAndSave(...) { + log.info("开始执行异步推理任务, scenarioId: {}", scenarioId); + try { + // 复杂的推理逻辑... + log.debug("中间计算状态: {}", someState); + } catch (Exception e) { + // 记录异常堆栈,这会被输出到 logs/business-css.log 中 + log.error("异步推理失败, scenarioId: {}, 原因: {}", scenarioId, e.getMessage(), e); + } + } +} +``` + +### 2.3 异常日志的统一处理 +目前的 `GlobalExceptionHandler` 已经能够拦截未处理的异常并使用 `log.error()` 打印。 +**建议**:在 `business-css` 模块抛出自定义业务异常(如 `BizException`)时,确保全局异常处理器能合理地记录(通常业务异常记为 `WARN` 即可,系统级异常记为 `ERROR`)。目前 `GlobalExceptionHandler` 统一按 `ERROR` 记录了所有 `Throwable`,这会导致日志文件中充斥着正常的业务校验阻断信息,建议后续对异常进行分类处理。 + +--- + +## 3. 日志文件输出管理建议 + +目前 `business-css` 的 `application-business.yml` 中配置了: +```yaml +logging: + file: + name: logs/business-css.log +``` +这会导致 Spring Boot 覆盖 `logback-spring.xml` 中定义的 `` 标签,但在某些 Spring Boot 版本中,如果不完全结合 `logback-spring.xml`,可能导致滚动策略失效。 + +**最佳实践建议:** +1. **统一由 Logback 接管**:建议删除 `application-business.yml` 中的 `logging.file.name` 配置。 +2. **通过变量传递**:在 `application-business.yml` 中定义自定义属性,或者直接利用 `spring.application.name`。然后在 `logback-spring.xml` 中通过 `` 读取该值,动态决定日志文件名。 + +**修改示例 (logback-spring.xml 改造建议):** +```xml + + + + + + + + ${LOG_PATH}/${APP_NAME}.log + + ${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.log + 30 + + + +``` +这样做能确保日志的按天滚动、文件大小限制等策略完美生效,且不同微服务/模块只要定义好 `spring.application.name`,就能自动隔离日志文件。 \ No newline at end of file diff --git a/模型训练任务.md b/模型训练任务.md new file mode 100644 index 0000000..a82dc72 --- /dev/null +++ b/模型训练任务.md @@ -0,0 +1,146 @@ +# 模型训练任务界面设计方案 + +本方案基于原有需求,对“模型训练面板”的功能模块、交互流程及字段定义进行了全面细化与完善,确保用户体验流畅且功能完整。 + +## 1. 任务列表 (Task List) + +### 1.1 查询栏 +* **任务名称**:文本输入框(支持模糊搜索)。 +* **算法类型**:下拉选择框(如 GPR, MLP, FastRBF, ...)。 +* **设备类型**:下拉选择框(如 CylindricalTank, AnnularTank, ...)。 +* **状态**:下拉选择框(全部, Pending, Training, Success, Failed)。 +* **操作按钮**:[查询] [重置] [新建训练任务] + +### 1.2 数据表格 +| 列名 | 数据类型 | 说明 | +| :--- | :--- | :--- | +| **任务名称** | String | 点击名称可直接跳转详情页 | +| **算法类型** | String | GPR / MLP 等 | +| **设备类型** | String | CylindricalTank 等 | +| **训练集来源** | String | 显示文件名或数据源类型(如 "Upload: dataset.csv") | +| **核心指标** | String | 成功时显示关键指标(如 RMSE: 0.02, R2: 0.98),失败/进行中显示 "-" | +| **状态** | Status | 使用 Tag 区分颜色:
🔵 Pending (等待中)
🟡 Training (训练中)
🟢 Success (成功)
🔴 Failed (失败) | +| **创建时间** | DateTime | yyyy-MM-dd HH:mm:ss | +| **操作** | ButtonGroup | [查看详情] [发布模型] [删除] | + +* **分页控件**:位于表格底部,支持调整每页显示条数。 + +### 1.3 操作说明 +* **查看详情**:任何状态下均可点击,跳转至详情页。 +* **发布模型**:仅当状态为 **Success** 时可用(否则禁用)。点击弹出发布确认框。 +* **删除**:仅当状态为 **Success** 或 **Failed** 时可用(Training 中不可删除)。点击需二次确认,删除后不可恢复。 + +--- + +## 2. 新建训练任务 (Create Task) + +采用分步向导或单页表单形式,推荐单页表单。 + +### 2.1 基础配置 +* **任务名称** (必填):文本框,建议自动生成默认名(如 "Train_GPR_20240311_001")。 +* **设备类型** (必填):下拉选择。 +* **算法类型** (必填):下拉选择。选择后,下方的“参数配置”应动态加载该算法的默认参数。 + +### 2.2 数据准备 +* **数据源类型** (必填):单选框 [文件上传] / [临界库数据]。 + * **选中“文件上传”**: + * 显示文件上传组件(支持 .csv, .xlsx)。 + * 上传后需显示文件名及大小,并提供“重新上传”按钮。 + * **选中“临界库数据”**(二期功能): + * 显示时间范围选择器(开始时间 - 结束时间)。 + * 显示“预览数据量”按钮(如:查询到 5000 条记录)。 + +### 2.3 参数配置 +* **训练超参数**: + * 提供“加载默认值”按钮。 + * 使用 **JSON 编辑器** 或 **Key-Value 列表** 展示参数(如 `epochs`, `learning_rate`, `kernel` 等)。 + * 相比单纯文本框,JSON 编辑器能提供格式校验,避免输入错误。 + +### 2.4 底部按钮 +* [提交训练]:点击后校验表单,成功则跳转回列表页并自动刷新。 +* [取消]:返回列表页。 + +--- + +## 3. 查看详情 (Task Details) + +详情页分为三个主要区域:基础信息、训练监控/结果、日志。 + +### 3.1 基础信息卡片 +展示任务的静态配置信息: +* 任务名称、ID +* 算法类型、设备类型 +* 数据源路径 +* 训练参数(JSON 格式化展示,只读) +* 创建时间、更新时间 + +### 3.2 训练监控/结果区 +根据状态不同,展示不同内容: + +* **Training (训练中)**: + * **进度条**:显示当前 Epoch/Total Epochs。 + * **实时指标图**:折线图展示 Loss (损失函数) 随迭代次数的变化。前端定时轮询或通过 WebSocket 获取最新数据。 + * **当前指标**:实时显示当前的 RMSE, Accuracy 等。 + +* **Success (成功)**: + * **最终指标卡片**:RMSE, MAE, R2, MaxError 等关键评估指标。 + * **误差散点图**:展示 `metrics_image_path` 对应的图片(预测值 vs 真实值)。 + * **特征重要性**(可选):如果算法支持,展示特征权重图。 + +* **Failed (失败)**: + * 显示醒目的红色错误提示。 + +### 3.3 错误日志区 +* 仅在 **Failed** 状态或 **Training** 状态下显示。 +* 展示后端或 Python 服务返回的详细异常堆栈或 Print 输出,便于排查问题。 + +### 3.4 顶部/底部操作栏 +* [发布模型](仅 Success 状态可见) +* [返回列表] + +--- + +## 4. 模型发布 (Publish Model) + +将临时训练任务产出的模型正式注册到系统模型库中。 + +### 4.1 交互流程 +1. 在列表页点击“发布模型”,或在详情页点击“发布模型”。 +2. 弹出模态框(Modal)。 + +### 4.2 弹窗内容 +* **任务名称**:(只读回显) +* **算法/设备**:(只读回显) +* **版本号 (Version Tag)** (必填):文本框。 + * 提示用户输入版本标识,如 `v1.0`, `20240311-release`。 + * 后端需校验该算法+设备下版本号的唯一性。 +* **备注描述**:多行文本框(可选),用于记录模型特性或变更点。 + +### 4.3 确认操作 +* [确认发布]: + * 后端将临时模型文件移动到正式目录。 + * 在 `algorithm_model` 表中创建新记录。 + * 发布成功后,提示用户“发布成功,请前往模型管理页激活使用”。 + * 关闭弹窗,刷新列表(建议在列表中增加“已发布”标记或状态)。 +* [取消]:关闭弹窗。 + + +### 4.4 模型路径规划 + +模型路径规划建议(推荐目录结构) 以你现有的配置 file-space.model-path 为根(例如 E:/python_coding/keffCenter/models/ ),建议分两棵树: + +- 训练产物(可清理) + + - {modelRoot}/runs/{algorithmType}/{deviceType}/{taskId}/ + - 里面放: pipeline.pkl 、 metrics.json 、 feature_map.json 、 metrics.png 、 train_params.json 等 + - 优点:用 taskId 保证唯一、不覆盖 +- 发布模型(长期保留、可回滚) + + - {modelRoot}/published/{algorithmType}/{deviceType}/{versionTag}/ + - 里面放: pipeline.pkl (或带 hash 的文件名)、配套文件、manifest(可选) + - algorithm_model.model_path 指向这里 +另外建议: + +- 路径里尽量只用 algorithmType/deviceType/versionTag/taskId 这种稳定字段,避免包含中文/空格 +- versionTag 建议由用户输入但要校验(只允许 [a-zA-Z0-9._-] ),避免路径注入 +- 如果担心文件重复,可在发布时生成 artifactId (hash)并写入模型表,便于去重/校验一致性 diff --git a/模型训练面板实现过程.md b/模型训练面板实现过程.md new file mode 100644 index 0000000..b6b276d --- /dev/null +++ b/模型训练面板实现过程.md @@ -0,0 +1,192 @@ +# 模型训练面板实现过程 + +根据《变更需求.md》中的第四点,我们需要为系统新增一个“模型训练面板”。该面板旨在支持用户通过简单的交互界面,上传数据、配置参数并启动模型训练,最终将训练好的模型发布到系统中。 + +## 1. 核心流程分析 + +1. **数据准备**:用户上传训练数据集(Excel/CSV),或从系统历史数据中选择(后续扩展)。 +2. **任务配置**:选择目标设备类型、算法类型,并输入模型超参数。 +3. **任务提交**:后端接收请求,创建训练任务记录,并异步调用 Python 算法服务。 +4. **过程监控**:前端轮询任务状态,展示实时训练进度和评估指标(如 Loss, Accuracy)。 +5. **模型入库**:训练成功后,自动解析模型元数据(特征映射、评估指标图表),并将其注册到 `algorithm_model` 表中。 + +## 2. 详细实现步骤 + +### 2.1 数据库设计 + +虽然已有 `algorithm_model` 表用于存储正式模型,但训练过程是一个长耗时且可能失败的操作,直接写入正式表会导致垃圾数据。因此,建议新增一张 **训练任务表 (`model_train_task`)** 来管理训练生命周期。 + +```sql +CREATE TABLE model_train_task ( + task_id VARCHAR(32) PRIMARY KEY COMMENT '任务ID', + task_name VARCHAR(100) COMMENT '任务名称', + algorithm_type VARCHAR(50) COMMENT '算法类型 (GPR, MLP...)', + device_type VARCHAR(50) COMMENT '设备类型 (CylindricalTank...)', + dataset_path VARCHAR(255) COMMENT '数据集文件路径', + train_params JSON COMMENT '训练超参数 ({"epochs": 100, "lr": 0.01...})', + status VARCHAR(20) COMMENT '状态 (PENDING, TRAINING, SUCCESS, FAILED)', + metrics JSON COMMENT '训练指标 ({"rmse": 0.002, "r2": 0.98})', + model_output_path VARCHAR(255) COMMENT '训练生成的临时模型路径', + feature_map_snapshot JSON COMMENT '特征映射快照', + metrics_image_path VARCHAR(255) COMMENT '误差散点图路径', + error_log TEXT COMMENT '错误日志', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); +``` + +### 2.2 后端接口设计 (`ModelTrainController`) + +| 接口路径 | 方法 | 描述 | 参数示例 | +| :--- | :--- | :--- | :--- | +| `/train/upload` | POST | 上传训练数据集 | File | +| `/train/submit` | POST | 提交训练任务 | `{name, algoType, deviceType, datasetPath, params}` | +| `/train/status/{taskId}` | GET | 查询训练状态与指标 | - | +| `/train/publish/{taskId}` | POST | 发布模型(入库) | `{versionTag, description}` | +| `/train/list` | GET | 查询历史训练任务 | `page, size` | + +### 2.3 业务逻辑实现 (`ModelTrainService`) + +1. **数据上传 (`uploadDataset`)**: + * 接收 Excel/CSV 文件。 + * 校验文件格式(必需列:`input_features`, `target_label`)。 + * 保存到服务器临时目录 `/data/uploads/{date}/{uuid}.csv`。 + +2. **任务提交 (`createTask`)**: + * 创建 `model_train_task` 记录,状态设为 `PENDING`。 + * 构建请求参数,异步调用 Python 服务的 `/v1/train` 接口。 + * 若调用成功,更新状态为 `TRAINING`;否则标记为 `FAILED`。 + +3. **状态同步 (`syncTaskStatus`)**: + * 定时任务或前端轮询触发:调用 Python 服务的 `/v1/train/status/{taskId}` 接口。 + * 更新数据库中的 `metrics` 和 `status`。 + * 如果状态为 `SUCCESS`,记录 `model_output_path`, `feature_map_snapshot`, `metrics_image_path`。 + +4. **模型发布 (`publishModel`)**: + * 校验任务状态是否为 `SUCCESS`。 + * 将模型文件从临时目录移动到正式模型目录 `/models/{algo}/{device}/`。 + * 在 `algorithm_model` 表中插入新记录: + * `algorithm_model_id`: 生成 UUID + * `version_tag`: 用户输入(如 v1.2) + * `feature_map_snapshot`: 从任务表复制 + * `metrics`: 从任务表复制 + * `metrics_image_path`: 从任务表复制 + * `is_current`: 默认为 0(需人工激活) + +### 2.4 Python 算法服务扩展 + +需要在 Python 服务中新增以下 API: + +* `POST /v1/train`: + * 接收:`dataset_path`, `algorithm_type`, `device_type`, `hyperparameters`。 + * 动作:启动后台线程/进程进行训练。 + * 返回:`task_id`。 +* `GET /v1/train/status/{task_id}`: + * 返回:`status` (TRAINING/SUCCESS/FAILED), `progress` (0-100%), `metrics`, `model_path`, `feature_map`, `metrics_image`。 + +### 2.5 前端界面开发 + +1. **新建训练任务向导**: + * **Step 1: 基础配置** + * 任务名称:文本框。 + * 设备类型:下拉框(从字典加载)。 + * 算法类型:下拉框(GPR, MLP...)。 + * **Step 2: 数据准备** + * 数据源选择:[上传文件] / [选择历史数据]。 + * 上传组件:支持 .csv, .xlsx。 + * **Step 3: 参数配置** + * 文本框输入 JSON 或 Key-Value 对。 + * 提供“加载默认参数”按钮。 + +2. **训练监控面板**: + * 展示任务列表。 + * 点击“查看详情”进入监控页。 + * **实时图表**:展示 Loss 曲线。 + * **评估结果**:展示 RMSE, R2 指标及误差散点图(`metrics_image_path`)。 + +3. **模型发布弹窗**: + * 输入版本号。 + * 点击确认后,将训练好的模型正式注册到系统中。 + +## 3. 关键问题与解决方案 + +1. **训练耗时问题**: + * **方案**:后端采用异步调用,前端使用轮询(每 3-5 秒)。Python 端必须使用非阻塞方式执行训练(如 `threading` 或 `Celery`)。 + +2. **数据格式校验**: + * **方案**:后端在上传阶段需解析 Excel 表头,确保包含必要的特征列。若缺失关键列,直接拒绝上传。 + +3. **模型文件管理**: + * **方案**:区分“临时模型”和“正式模型”。训练生成的模型先放在临时目录,只有用户确认发布后才移动到正式目录,避免无效模型占用空间。 + +4. **特征映射一致性**: + * **方案**:训练时必须生成 `feature_map_snapshot`(记录输入特征与 Java 实体属性的对应关系),并将其保存到 `algorithm_model` 表中。推理服务加载模型时,必须读取该快照以正确组装输入数据。 + + + +## 4. 接口改进 +1. list接口加上查询条件: + * `algoType`: 算法类型(GPR, MLP...)。 + * `deviceType`: 设备类型(CPU, GPU...)。 + * `status`: 任务状态(PENDING, TRAINING, SUCCESS, FAILED)。 + * `name`: 任务名称(模糊查询)。 + * `page`: 分页页码。 + * `size`: 每页数量(默认 10)。 +2. submit 接口加上MultipartFile file 非必须参数 + * `datasetPath`: 数据集路径(上传时返回的路径)。--这个需要根据是否上传文件来判断是否需要 + 、 +3.超参数面板 +GPR根据类型获取超参数面板。 +[ + { + "name": "RBF 平滑尺度", + "key": "rbf_length_scale", + "description": "控制模型整体平滑程度。值越小模型越敏感,值越大预测曲线越平滑。", + "default": 1.0, + "range": [0.01, 100], + "category": "基础参数" + }, + { + "name": "RQ 平滑尺度", + "key": "rq_length_scale", + "description": "控制多尺度变化的平滑程度,用于描述局部变化特征。", + "default": 1.0, + "range": [0.01, 100], + "category": "基础参数" + }, + { + "name": "RQ 多尺度系数", + "key": "rq_alpha", + "description": "控制多尺度变化强度。值越小多尺度变化越明显,值越大模型趋近于RBF。", + "default": 1.0, + "range": [0.1, 10] + }, + { + "name": "噪声水平", + "key": "noise_level", + "description": "数据观测噪声大小,用于提升模型鲁棒性。", + "default": 1e-5, + "range": [1e-8, 1e-1], + "category": "基础参数" + }, + { + "name": "优化重启次数", + "key": "optimizer_restarts", + "description": "模型超参数优化时的随机重启次数,用于避免陷入局部最优。", + "default": 5, + "range": [0, 20], + "category": "训练参数" + } + ] + + 生成这样的数据:{ + "rbf_length_scale": 2.0, + "rq_length_scale": 1.5, + "rq_alpha": 0.8, + "noise_level": 1e-4, + "optimizer_restarts": 10 +} + + + + diff --git a/模型训练面板编码实现规划.md b/模型训练面板编码实现规划.md new file mode 100644 index 0000000..dd4796d --- /dev/null +++ b/模型训练面板编码实现规划.md @@ -0,0 +1,134 @@ +# 模型训练面板编码实现规划 + +本规划基于《模型训练面板实现过程.md》,旨在提供具体的编码指导。**核心原则:简化开发,直接使用 Domain 实体,避免繁琐的 DTO 转换。** + +## 1. 数据库与实体层 + +### 1.1 数据库表 (`model_train_task`) +请确保数据库已执行以下 SQL: +```sql +CREATE TABLE IF NOT EXISTS `model_train_task` ( + `task_id` char(36) NOT NULL COMMENT '任务ID', + `task_name` varchar(100) DEFAULT NULL COMMENT '任务名称', + `algorithm_type` varchar(50) DEFAULT NULL COMMENT '算法类型', + `device_type` varchar(50) DEFAULT NULL COMMENT '设备类型', + `dataset_path` varchar(255) DEFAULT NULL COMMENT '数据集路径', + `train_params` json DEFAULT NULL COMMENT '训练参数', + `status` varchar(20) DEFAULT 'PENDING' COMMENT '状态', + `metrics` json DEFAULT NULL COMMENT '训练指标', + `model_output_path` varchar(255) DEFAULT NULL COMMENT '临时模型路径', + `feature_map_snapshot` json DEFAULT NULL COMMENT '特征映射', + `metrics_image_path` varchar(255) DEFAULT NULL COMMENT '指标图路径', + `error_log` text DEFAULT NULL COMMENT '错误日志', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`task_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +### 1.2 Domain 实体 (`ModelTrainTask.java`) +* **位置**: `com.yfd.business.css.domain` +* **注解**: `@TableName("model_train_task")`, `@TableId(type = IdType.ASSIGN_UUID)` +* **JSON 字段处理**: 使用 MyBatis-Plus 的 `@TableField(typeHandler = JacksonTypeHandler.class)` 处理 `trainParams`, `metrics`, `featureMapSnapshot` 等 JSON 字段。建议直接映射为 `Map` 或 `JsonNode`,或者简单的 `String`(前端解析)。为了简化,使用 `String` 存储 JSON 字符串。 + +```java +@Data +@TableName(value = "model_train_task", autoResultMap = true) +public class ModelTrainTask implements Serializable { + @TableId(type = IdType.ASSIGN_UUID) + private String taskId; + private String taskName; + private String algorithmType; + private String deviceType; + private String datasetPath; + + @TableField(typeHandler = JacksonTypeHandler.class) + private Map trainParams; // 或 String + + private String status; // PENDING, TRAINING, SUCCESS, FAILED + + @TableField(typeHandler = JacksonTypeHandler.class) + private Map metrics; + + private String modelOutputPath; + + @TableField(typeHandler = JacksonTypeHandler.class) + private Map featureMapSnapshot; + + private String metricsImagePath; + private String errorLog; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updatedAt; +} +``` + +## 2. Mapper 层 (`ModelTrainTaskMapper.java`) +* **位置**: `com.yfd.business.css.mapper` +* **继承**: `BaseMapper` + +## 3. Service 层 + +### 3.1 接口 (`ModelTrainService.java`) +* **位置**: `com.yfd.business.css.service` +* **继承**: `IService` +* **核心方法**: + * `String uploadDataset(MultipartFile file)`: 上传文件,返回路径。 + * `String submitTask(ModelTrainTask task)`: 提交任务(接收前端传来的实体,补充初始状态后保存,并异步调用 Python)。 + * `ModelTrainTask syncTaskStatus(String taskId)`: 同步并返回最新状态。 + * `boolean publishModel(String taskId, String versionTag, String description)`: 发布模型。 + +### 3.2 实现 (`ModelTrainServiceImpl.java`) +* **位置**: `com.yfd.business.css.service.impl` +* **依赖**: `ModelTrainTaskMapper`, `RestTemplate` (调用 Python), `AlgorithmModelService` (发布模型)。 + +## 4. Controller 层 (`ModelTrainController.java`) + +* **位置**: `com.yfd.business.css.controller` +* **原则**: 直接接收和返回 `ModelTrainTask` 实体或 `Map`。 + +### 4.1 接口定义 + +1. **上传数据集** + * `POST /train/upload` + * 参数: `@RequestParam("file") MultipartFile file` + * 返回: `Result` (文件路径) + +2. **提交任务** + * `POST /train/submit` + * 参数: `@RequestBody ModelTrainTask task` + * 逻辑: 前端构建好 `task` 对象(包含 name, algoType, deviceType, datasetPath, trainParams),传给后端。后端设置 status=PENDING, save, async call python。 + +3. **查询任务列表** + * `GET /train/list` + * 参数: `PageQuery`, `ModelTrainTask query` (查询条件) + * 返回: `Result>` + +4. **查询任务详情/状态** + * `GET /train/status/{taskId}` + * 逻辑: 调用 Service 的 `syncTaskStatus`,触发一次 Python 状态查询,更新数据库,然后返回最新实体。 + +5. **发布模型** + * `POST /train/publish` + * 参数: `Map body` (包含 taskId, versionTag, description) + * 逻辑: 检查状态 -> 移动文件 -> 插入 `algorithm_model` 表。 + +## 5. Python 服务交互 +* **调用方式**: `RestTemplate` +* **API 对应**: + * Java `submitTask` -> Python `POST /v1/train` + * Java `syncTaskStatus` -> Python `GET /v1/train/status/{taskId}` + +## 6. 开发步骤建议 + +1. **创建表**:执行 SQL。 +2. **生成代码**:创建 Domain, Mapper, Service, Controller。 +3. **实现上传接口**:确保文件能保存到指定目录(如 `data/upload/`)。 +4. **实现提交接口**: + * 先实现保存到数据库。 + * 再实现 `RestTemplate` 调用 Python(Mock 阶段可先跳过 Python 调用,直接设为 SUCCESS)。 +5. **实现状态查询**:对接 Python 查询接口。 +6. **实现发布接口**:操作 `algorithm_model` 表。 diff --git a/算法描述及原理.md b/算法描述及原理.md new file mode 100644 index 0000000..d2cc7c6 --- /dev/null +++ b/算法描述及原理.md @@ -0,0 +1,17 @@ +# 算法描述及原理一句话总结 + +**1. 高斯过程回归 (GPR)** +* **算法描述**:一种基于概率的非参数回归方法,不仅能预测目标值,还能给出预测的不确定性(置信区间)。 +* **算法原理**:假设所有观测数据服从一个联合高斯分布,通过核函数衡量样本间的相似性,利用贝叶斯推断由先验分布和观测数据计算出后验分布进行预测。 + +**2. 多层感知机 (MLP)** +* **算法描述**:一种经典的前馈人工神经网络,通过多层结构学习输入到输出的复杂非线性映射关系。 +* **算法原理**:由输入层、隐藏层和输出层组成,通过神经元间的权重连接和非线性激活函数传递信号,利用反向传播算法和梯度下降不断调整权重以最小化预测误差。 + +**3. 多维线性插值 (N-dimensional Linear Interpolation)** +* **算法描述**:一种在多维网格数据中,基于已知离散数据点估算网格内任意点数值的确定性插值方法。 +* **算法原理**:在目标点所在的多维超立方体网格内,沿各个维度依次进行一维线性插值(即基于距离的加权平均),最终得出该点的平滑过渡值。 + +**4. 快速径向基函数网络 (FastRBF)** +* **算法描述**:一种改进的径向基函数神经网络,专为处理大规模散乱数据的高效插值和拟合而设计。 +* **算法原理**:以样本点到中心的欧氏距离为自变量的非线性函数(径向基)为基底,结合快速多极子方法(FMM)或贪心算法来加速求解庞大的线性方程组,实现对高维复杂曲面的快速逼近。 \ No newline at end of file diff --git a/系统代码结构框架规划.md b/系统代码结构框架规划.md index d5bc797..f79ea91 100644 --- a/系统代码结构框架规划.md +++ b/系统代码结构框架规划.md @@ -3,18 +3,20 @@ ## 1. 目标与原则 ### 1.1 业务目标 + - framework 模块:沉淀系统级通用能力(安全、认证、权限、日志、任务调度、文件、缓存、监控、通用工具等),**不依赖任何业务代码**,可被 N 个业务模块复用 - business-css 模块:仅聚焦“临界事故情景分析”业务,**零系统代码**,所有共性能力通过 framework 提供 ### 1.2 设计原则 -| 原则 | 落地要求 | -|----|----| -| **高内聚低耦合** | 业务模块只依赖 framework,framework 绝不反向依赖业务 | -| **DRY** | 同一能力只在一个地方实现(framework),禁止业务模块重复造轮子 | -| **可演进** | 后续新增业务模块(business-xxx)零改动即可接入现有能力 | -| **可维护** | 通过 Maven 多模块 + 分层分包,让“修改点”收敛到最小范围 | ---- +| 原则 | 落地要求 | +| ---------- | ------------------------------------ | +| **高内聚低耦合** | 业务模块只依赖 framework,framework 绝不反向依赖业务 | +| **DRY** | 同一能力只在一个地方实现(framework),禁止业务模块重复造轮子 | +| **可演进** | 后续新增业务模块(business-xxx)零改动即可接入现有能力 | +| **可维护** | 通过 Maven 多模块 + 分层分包,让“修改点”收敛到最小范围 | + +*** ## 2. 多模块 Maven 结构(聚合工程) @@ -31,21 +33,23 @@ d:\JavaProjectSpace (root pom, packaging=pom) └── pom.xml ← modules 声明 ``` ---- +*** ## 3. framework 模块职责与目录 ### 3.1 职责矩阵 -| 能力域 | 是否包含 | 说明 | -|----|----|----| -| 统一配置 | ✅ | 数据源、Redis、Jackson、MyBatis-Plus、Swagger、监控、日志、加解密等 | -| 安全 & 认证 | ✅ | Spring Security + JWT 过滤器、权限注解、匿名访问标记 | -| 系统级 API | ✅ | 用户/角色/菜单/部门/字典/日志/文件/消息/定时任务等 | -| 通用工具 | ✅ | RSA、Excel、IP 归属、验证码、异步线程池、缓存封装 | -| 前端静态 | ✅ | 仅提供全局通用静态(如 404、error、swagger-ui);**业务页面由业务模块提供** | -| 业务代码 | ❌ | 禁止出现任何与“临界事故”相关的类、表、API | + +| 能力域 | 是否包含 | 说明 | +| ------- | ---- | ------------------------------------------------- | +| 统一配置 | ✅ | 数据源、Redis、Jackson、MyBatis-Plus、Swagger、监控、日志、加解密等 | +| 安全 & 认证 | ✅ | Spring Security + JWT 过滤器、权限注解、匿名访问标记 | +| 系统级 API | ✅ | 用户/角色/菜单/部门/字典/日志/文件/消息/定时任务等 | +| 通用工具 | ✅ | RSA、Excel、IP 归属、验证码、异步线程池、缓存封装 | +| 前端静态 | ✅ | 仅提供全局通用静态(如 404、error、swagger-ui);**业务页面由业务模块提供** | +| 业务代码 | ❌ | 禁止出现任何与“临界事故”相关的类、表、API | ### 3.2 代码分层 + ``` src/main/java/com/yfd/platform ├── config # 自动配置类(DataSource、Security、Async、Redis、MyBatisPlus...) @@ -67,6 +71,7 @@ src/main/java/com/yfd/platform ``` ### 3.3 资源目录 + ``` src/main/resources ├── mapper/system/*.xml # 仅系统表 SQL 映射 @@ -77,19 +82,21 @@ src/main/resources > **注意**:framework 绝不放业务 SQL、业务静态页面;业务资源全部下沉到业务模块。 ---- +*** ## 4. business-css 模块职责与目录 ### 4.1 职责矩阵 -| 能力域 | 是否包含 | 说明 | -|----|----|----| -| 业务领域代码 | ✅ | 临界事故实体、服务、控制器、DTO、Mapper XML | -| 业务静态页面 | ✅ | 仅与“临界事故”相关的页面、图片、图标 | -| 系统能力 | ❌ | 通过依赖 framework 获得,禁止复制粘贴 | -| 系统表 SQL | ❌ | 系统表由 framework 提供,业务模块只写业务表 | + +| 能力域 | 是否包含 | 说明 | +| ------- | ---- | ---------------------------- | +| 业务领域代码 | ✅ | 临界事故实体、服务、控制器、DTO、Mapper XML | +| 业务静态页面 | ✅ | 仅与“临界事故”相关的页面、图片、图标 | +| 系统能力 | ❌ | 通过依赖 framework 获得,禁止复制粘贴 | +| 系统表 SQL | ❌ | 系统表由 framework 提供,业务模块只写业务表 | ### 4.2 代码分层 + ``` src/main/java/com/yfd/business/css ├── model # 业务实体 / DTO / VO(仅业务表) @@ -100,6 +107,7 @@ src/main/java/com/yfd/business/css ``` ### 4.3 资源目录 + ``` src/main/resources ├── mapper/css/*.xml # 仅业务表 SQL 映射 @@ -107,57 +115,58 @@ src/main/resources └── application.yml # 业务个性化配置(端口、数据源 URL、业务开关) ``` ---- +*** ## 5. 依赖关系与 Maven 坐标 -| 模块 | 坐标 | 依赖 | -|----|----|----| -| framework | com.yfd:platform:1.0 | 无业务依赖,仅技术栈(Spring Boot、MyBatis-Plus、Security、Redis...) | -| business-css | com.yfd:business-css:1.0-SNAPSHOT | com.yfd:platform:1.0(plain classifier) | +| 模块 | 坐标 | 依赖 | +| ------------ | --------------------------------- | ------------------------------------------------------ | +| framework | com.yfd:platform:1.0 | 无业务依赖,仅技术栈(Spring Boot、MyBatis-Plus、Security、Redis...) | +| business-css | com.yfd:business-css:1.0-SNAPSHOT | com.yfd:platform:1.0(plain classifier) | > **plain classifier**:framework 通过 `maven-jar-plugin` 额外生成 `platform-1.0-plain.jar`,仅含 classes & resources,不含 Spring-Boot 启动器,避免业务模块引入重复主类。 ---- +*** ## 6. 配置隔离策略 -| 配置项 | 放在哪里 | 示例 | -|----|----|----| -| 数据源、Redis、MyBatis、线程池、监控、日志 | framework | `framework/src/main/resources/application.yml` | -| 业务开关、业务参数、端口、上下文 | business-css | `business-css/src/main/resources/application.yml` | -| 环境差异化(dev/prod) | 各自模块 | `application-dev.yml`、`application-prod.yml` | +| 配置项 | 放在哪里 | 示例 | +| --------------------------- | ------------ | ------------------------------------------------- | +| 数据源、Redis、MyBatis、线程池、监控、日志 | framework | `framework/src/main/resources/application.yml` | +| 业务开关、业务参数、端口、上下文 | business-css | `business-css/src/main/resources/application.yml` | +| 环境差异化(dev/prod) | 各自模块 | `application-dev.yml`、`application-prod.yml` | > **启动顺序**:业务模块的 `application.yml` 会覆盖 framework 的同名 key,实现“共性 + 个性”组合。 ---- +*** ## 7. 静态资源隔离策略 -| 资源类型 | 放在哪里 | 访问路径 | 说明 | -|----|----|----|----| -| 全局通用(swagger、error、login 背景) | framework | `/error`、`/swagger-ui.html` | 所有业务模块共用 | -| 业务页面(*.html、*.js、*.png) | business-css | `/css/index.html` | 仅当前业务使用 | -| 业务接口 | business-css | `/api/simulation/*` | REST 无静态冲突 | +| 资源类型 | 放在哪里 | 访问路径 | 说明 | +| ---------------------------- | ------------ | --------------------------- | ---------- | +| 全局通用(swagger、error、login 背景) | framework | `/error`、`/swagger-ui.html` | 所有业务模块共用 | +| 业务页面(*.html、*.js、\*.png) | business-css | `/css/index.html` | 仅当前业务使用 | +| 业务接口 | business-css | `/api/simulation/*` | REST 无静态冲突 | > **最佳实践**:业务静态统一放在 `/static/{业务简称}/` 目录,避免文件名冲突。 ---- +*** ## 8. 数据库隔离策略 -| 表归属 | 模块 | 说明 | -|----|----|----| -| 系统表(sys_user、sys_role、sys_menu...) | framework | 由 framework 提供 Mapper & XML,业务模块只读/调用 Service | -| 业务表(css_simulation、css_record...) | business-css | 由业务模块自行创建 Mapper & XML,禁止写入系统表 SQL | +| 表归属 | 模块 | 说明 | +| ------------------------------------- | ------------ | --------------------------------------------- | +| 系统表(sys\_user、sys\_role、sys\_menu...) | framework | 由 framework 提供 Mapper & XML,业务模块只读/调用 Service | +| 业务表(css\_simulation、css\_record...) | business-css | 由业务模块自行创建 Mapper & XML,禁止写入系统表 SQL | > **多业务共享同一库**:系统表前缀 `sys_`,业务表前缀 `css_`、`xxx_`,物理共存但逻辑隔离。 ---- +*** ## 9. 启动与部署 ### 9.1 开发阶段 + ```bash # 1. 安装 framework 到本地仓库 mvn -DskipTests clean install -pl framework @@ -170,6 +179,7 @@ mvn -DskipTests spring-boot:run -pl business-css ``` ### 9.2 交付阶段 + ```bash # 一次性打包所有模块 mvn -DskipTests clean package @@ -180,10 +190,11 @@ business-css/target/business-css-1.0-SNAPSHOT.jar # 业务服务(含内嵌 To ``` > **部署方式**: +> > - 轻量场景:只部署 `business-css-1.0-SNAPSHOT.jar`,内嵌 framework 能力 > - 分离场景:独立部署 `platform-1.0.jar`(提供统一后台),再部署多个业务 jar,网关统一路由 ---- +*** ## 10. 横向扩展:新增业务模块(business-xxx) @@ -197,46 +208,56 @@ business-css/target/business-css-1.0-SNAPSHOT.jar # 业务服务(含内嵌 To **零改动接入**:无需复制任何系统代码,仅需以上 7 步即可拥有完整的用户/权限/日志/文件/任务能力。 ---- +*** ## 11. 常见误区与禁止项 -| 误区 | 正确做法 | -|----|----| -| 把系统表 SQL 写在业务模块 | 系统 SQL 只能放在 framework/mapper/system | -| 把系统静态页面(login、404)复制到业务模块 | 全局静态统一收敛到 framework/static | +| 误区 | 正确做法 | +| -------------------------------- | -------------------------------------- | +| 把系统表 SQL 写在业务模块 | 系统 SQL 只能放在 framework/mapper/system | +| 把系统静态页面(login、404)复制到业务模块 | 全局静态统一收敛到 framework/static | | 业务模块直接依赖 `platform-1.0.jar`(可执行) | 使用 `platform-1.0-plain.jar`(仅 classes) | -| framework 里出现“css”字样 | framework 必须业务无关,出现业务代码即重构 | -| 多个业务模块端口重复 | 每个业务模块独立端口(8082、8083...) | +| framework 里出现“css”字样 | framework 必须业务无关,出现业务代码即重构 | +| 多个业务模块端口重复 | 每个业务模块独立端口(8082、8083...) | ---- +*** ## 12. 演进路线 -| 阶段 | 动作 | -|----|----| -| **当前** | 单库多模块,Maven 聚合,静态/配置/表逻辑隔离 | -| **下一步** | 引入 Spring Cloud Gateway,统一路由 & 鉴权,业务模块只关注领域逻辑 | -| **未来** | 业务模块拆分为独立微服务(docker-compose/k8s),framework 作为共享 lib 或 sidecar | +| 阶段 | 动作 | +| ------- | ------------------------------------------------------------- | +| **当前** | 单库多模块,Maven 聚合,静态/配置/表逻辑隔离 | +| **下一步** | 引入 Spring Cloud Gateway,统一路由 & 鉴权,业务模块只关注领域逻辑 | +| **未来** | 业务模块拆分为独立微服务(docker-compose/k8s),framework 作为共享 lib 或 sidecar | ---- +*** > **一句话总结**:framework 做“平台”,business-css 做“产品”;平台沉淀,产品迭代,互不污染,横向复制。 -## 快速稳定方案(作用:脚本先设置 JAVA_HOME 与 PATH 指向 17,再调用 Maven,确保所有构建/运行都用 JDK 17,不受工具自带 JDK 8 影响。) +## 快速稳定方案(作用:脚本先设置 JAVA\_HOME 与 PATH 指向 17,再调用 Maven,确保所有构建/运行都用 JDK 17,不受工具自带 JDK 8 影响。) + 使用项目内脚本强制 JDK 17(已为你添加): scripts\mvn17.cmd -s mvn-settings.xml -DskipTests -pl framework -am install -scripts\mvn17.cmd -s mvn-settings.xml -DskipTests -f business-css\pom.xml spring-boot:run -Dspring-boot.run.profiles=business +## 后端启动方法 -- debug方式启动 -scripts\mvn17-debug.cmd -s mvn-settings.xml -DskipTests -f business-css\pom.xml spring-boot:run -Dspring-boot.run.profiles=business +scripts\mvn17.cmd -DskipTests -f business-css\pom.xml spring-boot:run "-Dspring-boot.run.profiles=business" + +参数说明: + +- -f business-css\pom.xml :指定要运行的模块 POM 文件。 +- -DskipTests :跳过单元测试,加快启动速度。 +- spring-boot:run :运行 Spring Boot 应用。 +- -Dspring-boot.run.profiles=business :激活 business 配置文件(对应 application-business.yml ),加载业务专用配置。 +- 调试模式启动 + scripts\mvn17-debug.cmd -DskipTests -f business-css\pom.xml spring-boot:run "-Dspring-boot.run.profiles=business" ## 在工具内终端验证并调整到 17: + - 验证: mvn -version 、 where java 、 where mvn - 临时修正当前终端(一次性手动): - - set "JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-17.0.17.10-hotspot" - - set "PATH=%JAVA_HOME%\bin;%PATH%" + - set "JAVA\_HOME=C:\Program Files\Eclipse Adoptium\jdk-17.0.17.10-hotspot" + - set "PATH=%JAVA\_HOME%\bin;%PATH%" - 再 mvn -version 应为 17 ## 开发模式 @@ -244,7 +265,6 @@ scripts\mvn17-debug.cmd -s mvn-settings.xml -DskipTests -f business-css\pom.xml - 进入前端目录: business-css/frontend - 安装依赖: npm install - 启动开发服务器: npm run dev -- 访问: http://localhost:3000/ +- 访问: set "PATH=d:/Program Files/nodejs;%PATH%" - diff --git a/训练任务数据.md b/训练任务数据.md new file mode 100644 index 0000000..36fb635 --- /dev/null +++ b/训练任务数据.md @@ -0,0 +1,65 @@ +# 训练任务回填数据 + +根据现有的 `algorithm_model` 数据,生成了对应的 `model_train_task` 回填 SQL 语句。这些数据代表了系统中已存在的模型训练记录。 + +## 映射规则说明 +* **Task ID**:复用 `algorithm_model_id`,以保持与模型表的关联(实际场景中可能通过此 ID 关联,或视作一次性导入)。 +* **Task Name**:自动生成格式 `Import_{Algorithm}_{Device}_{Version}`。 +* **Status**:统一设为 `SUCCESS`,因为这些模型已经发布。 +* **Dataset Path / Train Params**:由于原表无此信息,设为默认占位符。 +* **Metrics / Feature Map**:直接从原表复制。 + +## SQL 插入语句 + +请在数据库中执行以下 SQL 语句以完成数据回填: + +```sql +INSERT INTO `model_train_task` ( + `task_id`, + `task_name`, + `algorithm_type`, + `device_type`, + `dataset_path`, + `train_params`, + `status`, + `metrics`, + `model_output_path`, + `feature_map_snapshot`, + `metrics_image_path`, + `error_log`, + `created_at`, + `updated_at` +) VALUES +( + 'a3c3b6b0-6c2f-4f9c-9b77-2b9d3c8a1f02', + 'Import_GPR_AnnularTank_V1', + 'GPR', + 'AnnularTank', + 'Unknown (Historical Import)', + '{}', + 'SUCCESS', + '{\"r2\": 0.991, \"mae\": 0.007, \"maxe\": 0.034, \"rmse\": 0.012}', + 'E:/python_coding/keffCenter/models/GPR/AnnularTank/V1/pipeline.pkl', + '{\"直径\": \"diameter\", \"高度\": \"height\", \"钚浓度\": \"fissile_concentration\", \"pu_isotope\": \"isotopic_abundance\", \"pu_concentration\": \"fissile_concentration\", \"钚同位素比例\": \"isotopic_abundance\"}', + 'GPR/AnnularTank/V1/error.png', + NULL, + '2025-12-26 07:53:27', + '2026-01-20 07:25:28' +), +( + 'a3c3b6b0-6c2f-4f9c-9b77-2b9d3c8a1f01', + 'Import_GPR_CylindricalTank_v1', + 'GPR', + 'CylindricalTank', + 'Unknown (Historical Import)', + '{}', + 'SUCCESS', + '{\"r2\": 0.991, \"mae\": 0.007, \"maxe\": 0.034, \"rmse\": 0.012}', + 'E:/python_coding/keffCenter/models/GPR/CylindricalTank/v1/pipeline.pkl', + '{\"直径\": \"diameter\", \"高度\": \"height\", \"铀浓度\": \"fissile_concentration\", \"u_enrichment\": \"isotopic_abundance\", \"铀富集度\": \"isotopic_abundance\", \"u_concentration\": \"fissile_concentration\"}', + 'GPR/CylindricalTank/V1/error.png', + NULL, + '2025-12-26 07:34:05', + '2026-01-20 07:25:22' +); +``` diff --git a/设备size处理.md b/设备size处理.md new file mode 100644 index 0000000..6b62074 --- /dev/null +++ b/设备size处理.md @@ -0,0 +1,55 @@ +1.设备有多种类型,为了方便提取尺寸, +扁平槽:FlatTank,形状参数(尺寸)json表达:{ + "length": 100, + "width": 40, + "height": 15 +} +圆柱槽:CylindricalTank ,形状参数(尺寸)json表达:{ + "diameter": 200, + "height":120 + } +环形槽:AnnularTank,形状参数(尺寸)json表达: { + "outer_diameter": 100, + "height": 150 + } +管束槽:TubeBundleTank,形状参数(尺寸)json表达:{ + "outer_diameter":120, + "height": 200 + } +萃取柱:ExtractionColumn,形状参数(尺寸)json表达: { + "upper_expanded": { + "diameter": 40, + "height": 150 + }, + "tray_section": { + "diameter": 100, + "height":800 + }, + "lower_expanded": { + "diameter": 30, + "height": 50 + } + } +流化床:FluidizedBed,形状参数(尺寸)json表达: { + "expanded_section": { + "diameter": 120, + "height":40 + }, + "transition_section": { + "height": 60 + }, + "reaction_section": { + "diameter": 60, + "height": 400 + } +} +锥底环形槽:ACFTank,形状参数(尺寸)json表达:{ + "annular_cylinder": { + "outer_diameter": 70, + "height": 300 + }, + "frustum_bottom": { + "bottom_diameter": 20, + "height": 30 + } + } diff --git a/设备类型支持v2.0.txt b/设备类型支持v2.0.txt new file mode 100644 index 0000000..e69de29