From c78e3be98226fe05d3be2b45f307bf3d56806cea Mon Sep 17 00:00:00 2001 From: wanxiaoli Date: Fri, 20 Mar 2026 18:12:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=8E=A8=E7=90=86?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/business/css/service/DeviceInferService.java | 8 ++++++-- .../css/service/impl/ModelTrainServiceImpl.java | 12 ++++++------ .../src/main/resources/application-business.yml | 11 ++++++----- .../main/resources/{logback.xml => logback.xml.bak} | 0 4 files changed, 18 insertions(+), 13 deletions(-) rename framework/src/main/resources/{logback.xml => logback.xml.bak} (100%) 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..ee94be9 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,15 +123,17 @@ 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 JsonNode fNode = objectMapper.readTree(model.getFeatureMapSnapshot()); 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()); } } catch (Exception e) { 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 Date: Fri, 20 Mar 2026 19:00:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../css/service/DeviceInferService.java | 17 +- data.txt | 24 +++ scripts/mvn17.cmd | 4 + 变更实现方案.md | 46 +++++ 变更需求.md | 35 ++++ 增加材料类型的支持.md | 73 +++++++ 异步推理并保存结果.md | 49 +++++ 推理接口对材料类型的支持.md | 112 ++++++++++ 推理数据封装问题定位.md | 2 + 日志记录及日志文件.md | 114 +++++++++++ 模型训练任务.md | 146 +++++++++++++ 模型训练面板实现过程.md | 192 ++++++++++++++++++ 模型训练面板编码实现规划.md | 134 ++++++++++++ 算法描述及原理.md | 17 ++ 系统代码结构框架规划.md | 158 +++++++------- 训练任务数据.md | 65 ++++++ 设备size处理.md | 55 +++++ 设备类型支持v2.0.txt | 0 18 files changed, 1173 insertions(+), 70 deletions(-) create mode 100644 data.txt create mode 100644 scripts/mvn17.cmd create mode 100644 变更实现方案.md create mode 100644 变更需求.md create mode 100644 增加材料类型的支持.md create mode 100644 异步推理并保存结果.md create mode 100644 推理接口对材料类型的支持.md create mode 100644 推理数据封装问题定位.md create mode 100644 日志记录及日志文件.md create mode 100644 模型训练任务.md create mode 100644 模型训练面板实现过程.md create mode 100644 模型训练面板编码实现规划.md create mode 100644 算法描述及原理.md create mode 100644 训练任务数据.md create mode 100644 设备size处理.md create mode 100644 设备类型支持v2.0.txt 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 ee94be9..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 @@ -127,7 +127,12 @@ public class DeviceInferService { List requiredFeatures = new ArrayList<>(); if (model.getFeatureMapSnapshot() != null && !model.getFeatureMapSnapshot().isBlank()) { try { + // 由于数据库里存的可能是转义的 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()) { @@ -136,9 +141,12 @@ public class DeviceInferService { // 兼容旧版本 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 为空,将不进行特征过滤"); } // 将相对路径转换为绝对路径 @@ -206,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/data.txt b/data.txt new file mode 100644 index 0000000..d868ac7 --- /dev/null +++ b/data.txt @@ -0,0 +1,24 @@ +batch=[Sample(meta={'deviceType': 'AnnularTank', 'step': 0, 'time': 0, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), + Sample(meta={'deviceType': 'AnnularTank', 'step': 1, 'time': 1, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 2, +'time': 2, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, + features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 3, 'time': 3, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), + Sample(meta={'deviceType': 'AnnularTank', 'step': 4, 'time': 4, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 5, 'time': 5, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 6, 'time': 6, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 7, 'time': 7, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 8, 'time': 8, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 9, 'time': 9, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0}), +Sample(meta={'deviceType': 'AnnularTank', 'step': 10, 'time': 10, 'deviceId': '786a9f85-2fd4-4b56-b5f6-4afb1cf33d32'}, +features={'diameter': 100.0, 'concentration': 0.0, 'concentration*ratio': 0.0, 'height': 150.0, 'ratio': 0.0, 'diameter*height': 0.0})] +model_dir=None modelDir='D:\\keff_dataSpace\\models\\MLP\\AnnularTank\\Pu\\V2.0\\pipeline.pkl' algorithm_type=None device_type='AnnularTank' deviceType=None material_type=None materialType=None \ No newline at end of file diff --git a/scripts/mvn17.cmd b/scripts/mvn17.cmd new file mode 100644 index 0000000..73701a2 --- /dev/null +++ b/scripts/mvn17.cmd @@ -0,0 +1,4 @@ +@echo off +set "JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-17.0.17.10-hotspot" +set "PATH=%JAVA_HOME%\bin;%PATH%" +mvn %* diff --git a/变更实现方案.md b/变更实现方案.md new file mode 100644 index 0000000..a9845f1 --- /dev/null +++ b/变更实现方案.md @@ -0,0 +1,46 @@ +# 变更实现方案 + +本方案旨在响应《变更需求.md》中提出的四项核心改进需求,涵盖前端展示、后端逻辑、数据库设计及与 Python 算法服务的交互。 + +## 1. 需求一:情景表添加 Keff 阈值设置字段 + +### 1.1 需求描述 +在情景配置中新增 `keff` 阈值参数,并在仿真结果中对超过阈值的 `keff` 值进行红字高亮预警。 + +### 1.2 数据库变更 +在 `scenario` 表中增加字段: +```sql +ALTER TABLE scenario ADD COLUMN keff_threshold decimal(8,6) COMMENT 'Keff预警阈值'; +``` + +### 1.3 后端实现 +1. **实体类更新**:在 `Scenario.java` 中添加 `BigDecimal keffThreshold` 字段。 +2. **接口调整**:`ScenarioController` 的新增/修改接口自动支持该字段(基于 MyBatis-Plus)。 +3. **结果返回**:`SimController` 返回仿真结果时,可以顺便在元数据中返回该阈值,或者前端单独查询情景详情获取。 + + + +## 2. 需求二:情景表支持按设备设置算法类型 + +### 2.1 需求描述 +打破原有的“一情景一算法”限制,支持针对不同设备配置不同的算法模型(例如:圆柱槽使用 GPR,环形槽使用 MLP)。 + +### 2.2 数据库变更 +在 `scenario` 表中增加字段,存储设备级算法配置(JSON 格式): +```sql +ALTER TABLE scenario ADD COLUMN device_algo_config JSON COMMENT '设备算法配置映射,格式:{"deviceId1":"GPR", "deviceId2":"MLP"}'; +``` + +### 2.3 后端实现 +1. **实体类更新**:`Scenario.java` 添加 `String deviceAlgoConfig` 字段。 +2. **推理服务重构 (`DeviceInferService`)**: + * **当前逻辑**:按 `deviceType` 分组 -> 获取全局 `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