diff --git a/.vscode/launch.json b/.vscode/launch.json index 014e576..fdbcbb4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,13 +7,16 @@ "hostName": "localhost", "port": "5005" }, - java + { "type": "java", "name": "CriticalScenarioApplication", "request": "launch", + "cwd": "${workspaceFolder}", "mainClass": "com.yfd.business.css.CriticalScenarioApplication", - "projectName": "business-css" + "projectName": "business-css", + "args": "", + "envFile": "${workspaceFolder}/.env" }, { "type": "java", diff --git a/business-css/docs/分析模拟系统方案.md b/business-css/docs/分析模拟系统方案.md index 9886ead..d947c9c 100644 --- a/business-css/docs/分析模拟系统方案.md +++ b/business-css/docs/分析模拟系统方案.md @@ -51,6 +51,23 @@ 4. 增加 OpenAPI 文档与前端集成接口规范。 5. 引入结果持久化与查询报表。 +## 调试与开发指南 + +### Maven 命令行启动 + 远程调试 + +如果您偏好使用命令行启动,或者需要模拟特定的 Maven 环境,可采用以下方式: + +1. **启动应用**: + 在终端中运行以下命令,该命令会以调试模式启动应用并监听 `5005` 端口(`suspend=n` 表示不等待调试器连接直接启动,如需等待可改为 `y`)。 + > 注意:PowerShell 中需要使用单引号包裹 JVM 参数,防止解析错误。 + + ```bash + mvn -DskipTests spring-boot:run -pl business-css '-Dspring-boot.run.jvmArguments=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005' + ``` + +2. **附加调试器**: + 应用启动后,转到 IDE 的 "运行和调试" (Run and Debug) 面板,选择 **"Attach to Remote Program(5005)"** 配置(需确保 `.vscode/launch.json` 中已存在相应配置),然后点击运行。IDE 将连接到正在运行的 Maven 进程,即可开始断点调试。 + ## 运维与配置 - 端口默认 `8082`,环境覆盖通过 `application.yml` 与外部化配置。 - 数据库连接按环境注入(dev/test/prod)。 diff --git a/business-css/pom.xml b/business-css/pom.xml index 925ee02..fdadb3a 100644 --- a/business-css/pom.xml +++ b/business-css/pom.xml @@ -64,7 +64,14 @@ platform 1.0 plain - + + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.6 + diff --git a/business-css/src/main/java/com/yfd/business/css/config/BusinessCssAutoConfiguration.java b/business-css/src/main/java/com/yfd/business/css/config/BusinessCssAutoConfiguration.java deleted file mode 100644 index 2f66575..0000000 --- a/business-css/src/main/java/com/yfd/business/css/config/BusinessCssAutoConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.yfd.business.css.config; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -@Configuration(proxyBeanMethods = false) -@ComponentScan(basePackages = { - "com.yfd.business.css.controller" -}) -@Import(OpenApiConfig.class) -public class BusinessCssAutoConfiguration { -} diff --git a/business-css/src/main/java/com/yfd/business/css/config/OpenApiConfig.java b/business-css/src/main/java/com/yfd/business/css/config/OpenApiConfig.java index 339e2e9..d49934f 100644 --- a/business-css/src/main/java/com/yfd/business/css/config/OpenApiConfig.java +++ b/business-css/src/main/java/com/yfd/business/css/config/OpenApiConfig.java @@ -14,9 +14,6 @@ import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilde import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.databind.PropertyNamingStrategies; import java.time.format.DateTimeFormatter; import java.io.IOException; @@ -41,9 +38,6 @@ public class OpenApiConfig { builder.deserializers(new LocalDateTimeDeserializer(fmt)); builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); builder.serializationInclusion(JsonInclude.Include.ALWAYS); - builder.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NONE); - builder.visibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.PUBLIC_ONLY); - builder.propertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE); SimpleModule module = new SimpleModule(); module.setSerializerModifier(new BeanSerializerModifier() { @Override diff --git a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java index 1f2c58a..a3edf4c 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java @@ -11,11 +11,15 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.Authentication; import org.springframework.security.authentication.AnonymousAuthenticationToken; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + import java.util.List; import java.time.LocalDateTime; @RestController @RequestMapping("/algorithms") +@Tag(name = "算法接口", description = "算法字典的增删改查与搜索") public class AlgorithmController { @Autowired @@ -25,11 +29,13 @@ public class AlgorithmController { @GetMapping("/{id}") + @Operation(summary = "根据算法ID获取算法", description = "路径参数传入算法ID,返回算法对象") public Algorithm getAlgorithmById(@PathVariable String id) { return algorithmService.getById(id); } @PostMapping + @Operation(summary = "新增算法", description = "请求体传入算法对象,返回是否新增成功") public boolean createAlgorithm(@RequestBody Algorithm algorithm) { algorithm.setModifier(currentUsername()); algorithm.setCreatedAt(LocalDateTime.now()); @@ -38,6 +44,7 @@ public class AlgorithmController { } @PutMapping + @Operation(summary = "修改算法", description = "请求体传入算法对象(需包含主键),返回是否修改成功") public boolean updateAlgorithm(@RequestBody Algorithm algorithm) { algorithm.setModifier(currentUsername()); algorithm.setUpdatedAt(LocalDateTime.now()); @@ -45,15 +52,57 @@ public class AlgorithmController { } @DeleteMapping("/{id}") + @Operation(summary = "删除算法(单条)", description = "根据算法ID删除算法") public boolean deleteAlgorithm(@PathVariable String id) { return algorithmService.removeById(id); } @DeleteMapping + @Operation(summary = "删除算法(批量)", description = "请求体传入算法ID列表,批量删除算法") public boolean deleteAlgorithms(@RequestBody List ids) { return algorithmService.removeByIds(ids); } + //算法类型激活 + @PostMapping("/activate") + @Operation(summary = "激活算法", description = "激活当前算法类型") + public boolean activate(@RequestParam String algorithmId) { + Algorithm algorithm = algorithmService.getById(algorithmId); + if (algorithm == null) return false; + // 先将当前算法类型激活 + algorithm.setStatus("1"); + algorithm.setModifier(currentUsername()); + algorithm.setUpdatedAt(LocalDateTime.now()); + return algorithmService.updateById(algorithm); + } + + //算法类型关闭 + @PostMapping("/unactivate") + @Operation(summary = "关闭算法", description = "关闭当前算法类型") + public boolean unactivate(@RequestParam String algorithmId) { + Algorithm algorithm = algorithmService.getById(algorithmId); + if (algorithm == null) return false; + // 先将当前算法类型激活 + algorithm.setStatus("0"); + algorithm.setModifier(currentUsername()); + algorithm.setUpdatedAt(LocalDateTime.now()); + return algorithmService.updateById(algorithm); + } + + //获取激活的算法类型 + /** + * 获取所有激活的算法类型 + * 输出参数:激活的算法类型列表 + * @return 激活的算法类型列表 + */ + @GetMapping("/getActiveAlgorithms") + @Operation(summary = "获取激活的算法类型", description = "返回所有激活的算法类型") + public List getActiveAlgorithms() { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("status", "1"); + return algorithmService.list(qw); + } + /** * 根据算法名称搜索并分页返回 * 输入参数:查询参数 name(算法名称关键词,可为空),pageNum(页码,默认1),pageSize(每页条数,默认10) @@ -64,6 +113,7 @@ public class AlgorithmController { * @return 算法分页列表 */ @GetMapping("/search") + @Operation(summary = "搜索算法并分页返回", description = "按名称关键词模糊查询,返回分页结果") public Page searchAlgorithms(@RequestParam(required = false) String name, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { diff --git a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java index 7b485d8..ad0fc9e 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java @@ -3,7 +3,9 @@ package com.yfd.business.css.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.business.css.domain.AlgorithmModel; +import com.yfd.business.css.domain.Algorithm; import com.yfd.business.css.service.AlgorithmModelService; +import com.yfd.business.css.service.AlgorithmService; import com.yfd.platform.system.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -11,24 +13,46 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.Authentication; import org.springframework.security.authentication.AnonymousAuthenticationToken; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.type.TypeReference; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.URI; +import java.time.Duration; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.HashMap; +import java.util.UUID; + import java.time.LocalDateTime; import java.util.List; @RestController @RequestMapping("/algorithm-models") +@Tag(name = "算法模型接口", description = "算法模型版本的增删改查、查询当前版本与在线训练") public class AlgorithmModelController { @Autowired private AlgorithmModelService algorithmModelService; @Autowired private IUserService userService; + @Autowired + private AlgorithmService algorithmService; + @Autowired + private ObjectMapper objectMapper; @GetMapping("/{id}") + @Operation(summary = "根据模型ID获取模型版本", description = "路径参数传入模型ID,返回模型版本对象") public AlgorithmModel getById(@PathVariable String id) { return algorithmModelService.getById(id); } @PostMapping + @Operation(summary = "新增模型版本", description = "请求体传入模型版本对象,返回是否新增成功") public boolean create(@RequestBody AlgorithmModel model) { model.setModifier(currentUsername()); model.setCreatedAt(LocalDateTime.now()); @@ -37,6 +61,7 @@ public class AlgorithmModelController { } @PutMapping + @Operation(summary = "修改模型版本", description = "请求体传入模型版本对象(需包含主键),返回是否修改成功") public boolean update(@RequestBody AlgorithmModel model) { model.setModifier(currentUsername()); model.setUpdatedAt(LocalDateTime.now()); @@ -44,24 +69,31 @@ public class AlgorithmModelController { } @DeleteMapping("/{id}") + @Operation(summary = "删除模型版本(单条)", description = "根据模型ID删除模型版本") public boolean delete(@PathVariable String id) { return algorithmModelService.removeById(id); } @DeleteMapping + @Operation(summary = "删除模型版本(批量)", description = "请求体传入模型ID列表,批量删除模型版本") public boolean deleteBatch(@RequestBody List ids) { return algorithmModelService.removeByIds(ids); } //返回:该算法+设备类型的版本列表 @GetMapping("/search") + @Operation(summary = "查询模型版本列表", description = "按算法类型与设备类型过滤并分页返回模型版本") public Page search(@RequestParam(required = false) String algorithmType, @RequestParam(required = false) String deviceType, + @RequestParam(required = false) String versionTag, + @RequestParam(required = false) String isCurrent, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { QueryWrapper qw = new QueryWrapper<>(); if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType); if (deviceType != null && !deviceType.isEmpty()) qw.eq("device_type", deviceType); + if (versionTag != null && !versionTag.isEmpty()) qw.eq("version_tag", versionTag); + if (isCurrent != null && !isCurrent.isEmpty()) qw.eq("is_current", isCurrent); qw.orderByDesc("updated_at"); Page page = new Page<>(pageNum, pageSize, true); return algorithmModelService.page(page, qw); @@ -69,6 +101,7 @@ public class AlgorithmModelController { //返回:该算法+设备类型的当前激活版本 @GetMapping("/current") + @Operation(summary = "获取当前激活版本", description = "根据算法类型与设备类型,返回 is_current=1 的模型版本") public AlgorithmModel getCurrent(@RequestParam String algorithmType, @RequestParam String deviceType) { QueryWrapper qw = new QueryWrapper<>(); @@ -81,6 +114,7 @@ public class AlgorithmModelController { //版本激活 @PostMapping("/activate") + @Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组其他版本设为非当前") public boolean activate(@RequestParam String algorithmModelId) { AlgorithmModel model = algorithmModelService.getById(algorithmModelId); if (model == null) return false; @@ -98,15 +132,122 @@ public class AlgorithmModelController { return algorithmModelService.updateById(model); } - //在线训练 + // 在线训练(Excel 数据集) + @PostMapping("/train/excel") + @Operation(summary = "在线训练(Excel)", description = "传入算法类型、设备类型与Excel路径,训练完成新增模型版本记录,可选激活") + public Map trainExcel(@RequestBody Map body) { + String algorithmType = str(body.get("algorithm_type")); + String deviceType = str(body.get("device_type")); + String datasetPath = str(body.get("dataset_path")); + String modelDir = str(body.getOrDefault("model_dir", "")); + boolean activate = bool(body.getOrDefault("activate", false)); + String featureMapSnapshot = toJson(body.get("feature_map_snapshot")); + if (isBlank(algorithmType) || isBlank(deviceType) || isBlank(datasetPath)) { + return Map.of("code", 1, "msg", "algorithm_type/device_type/dataset_path 必填"); + } + Algorithm algo = getAlgorithmByType(algorithmType); + if (algo == null || isBlank(algo.getTrainBaseUrl())) { + return Map.of("code", 1, "msg", "算法或训练URL未配置"); + } + String baseUrl = algo.getTrainBaseUrl(); + if (baseUrl != null && baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + String url = baseUrl + "/v1/train/" + deviceType; + Map payload = new HashMap<>(); + payload.put("dataset_path", datasetPath); + if (!isBlank(modelDir)) payload.put("model_dir", modelDir); + Map resp = httpPostJson(url, payload); + if (resp == null) return Map.of("code", 1, "msg", "训练接口无响应"); + int code = 0; + Object codeObj = resp.get("code"); + if (codeObj instanceof Number) { + code = ((Number) codeObj).intValue(); + } else if (codeObj instanceof String) { + try { + code = Integer.parseInt((String) codeObj); + } catch (NumberFormatException ignored) { + code = 1; + } + } + if (code != 0) return Map.of("code", 1, "msg", "训练失败: " + str(resp.get("msg"))); + Map data = castMap(resp.get("data")); + String modelPath = str(data.get("model_path")); + String metrics = toJson(data.get("metrics")); + AlgorithmModel model = new AlgorithmModel(); + model.setAlgorithmModelId(UUID.randomUUID().toString()); + model.setAlgorithmType(algorithmType); + model.setDeviceType(deviceType); + model.setVersionTag(genVersionTag()); + model.setModelPath(modelPath); + model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); + model.setMetrics(metrics); + model.setTrainedAt(LocalDateTime.now()); + model.setIsCurrent(activate ? 1 : 0); + model.setCreatedAt(LocalDateTime.now()); + model.setUpdatedAt(LocalDateTime.now()); + model.setModifier(currentUsername()); + if (activate) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); + AlgorithmModel upd = new AlgorithmModel(); + upd.setIsCurrent(0); + algorithmModelService.update(upd, qw); + } + algorithmModelService.save(model); + return Map.of("code", 0, "msg", "训练成功", "data", model); + } - @PostMapping("/train") - public boolean train(@RequestParam String algorithmModelId) { - AlgorithmModel model = algorithmModelService.getById(algorithmModelId); - if (model == null) return false; - // 调用训练接口 - // ... - return true; + // 在线训练(样本集合) + @PostMapping("/train/samples") + @Operation(summary = "在线训练(样本集合)", description = "传入算法类型、设备类型与样本集,训练完成新增模型版本记录,可选激活") + public Map trainSamples(@RequestBody Map body) { + String algorithmType = str(body.get("algorithm_type")); + String deviceType = str(body.get("device_type")); + Object samples = body.get("samples"); // 期望为 List,由前端提供 + String modelDir = str(body.getOrDefault("model_dir", "")); + boolean activate = bool(body.getOrDefault("activate", false)); + String featureMapSnapshot = toJson(body.get("feature_map_snapshot")); + if (isBlank(algorithmType) || isBlank(deviceType) || samples == null) { + return Map.of("code", 1, "msg", "algorithm_type/device_type/samples 必填"); + } + Algorithm algo = getAlgorithmByType(algorithmType); + if (algo == null || isBlank(algo.getTrainBaseUrl())) { + return Map.of("code", 1, "msg", "算法或训练URL未配置"); + } + String url = "";//trimSlash(algo.getTrainBaseUrl()) + "/v1/train/" + deviceType + "/from-samples"; + Map payload = new HashMap<>(); + payload.put("samples", samples); + if (!isBlank(modelDir)) payload.put("model_dir", modelDir); + Map resp = httpPostJson(url, payload); + if (resp == null) return Map.of("code", 1, "msg", "训练接口无响应"); + int code = 0;//intVal(resp.get("code")); + if (code != 0) return Map.of("code", 1, "msg", "训练失败: " + str(resp.get("msg"))); + Map data = castMap(resp.get("data")); + String modelPath = str(data.get("model_path")); + String metrics = toJson(data.get("metrics")); + AlgorithmModel model = new AlgorithmModel(); + model.setAlgorithmModelId(UUID.randomUUID().toString()); + model.setAlgorithmType(algorithmType); + model.setDeviceType(deviceType); + model.setVersionTag(genVersionTag()); + model.setModelPath(modelPath); + model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); + model.setMetrics(metrics); + model.setTrainedAt(LocalDateTime.now()); + model.setIsCurrent(activate ? 1 : 0); + model.setCreatedAt(LocalDateTime.now()); + model.setUpdatedAt(LocalDateTime.now()); + model.setModifier(currentUsername()); + if (activate) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); + AlgorithmModel upd = new AlgorithmModel(); + upd.setIsCurrent(0); + algorithmModelService.update(upd, qw); + } + algorithmModelService.save(model); + return Map.of("code", 0, "msg", "训练成功", "data", model); } @@ -121,4 +262,55 @@ public class AlgorithmModelController { return "anonymous"; } } + + private Algorithm getAlgorithmByType(String algorithmType) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("algorithm_type", algorithmType); + return algorithmService.getOne(qw); + } + + private Map httpPostJson(String url, Object payload) { + try { + String json = objectMapper.writeValueAsString(payload); + HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build(); + HttpRequest req = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .timeout(Duration.ofSeconds(30)) + .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) + .build(); + HttpResponse res = client.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (res.statusCode() >= 200 && res.statusCode() < 300) { + return objectMapper.readValue(res.body(), new TypeReference>() {}); + } + return Map.of("code", 1, "msg", "HTTP " + res.statusCode()); + } catch (Exception e) { + return Map.of("code", 1, "msg", e.getMessage()); + } + } + + private String genVersionTag() { + return "v" + LocalDateTime.now().toString().replace(":", "-"); + } + private String str(Object v) { return v == null ? "" : String.valueOf(v); } + private boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } + private boolean bool(Object v) { + if (v instanceof Boolean) return (Boolean) v; + String s = str(v).toLowerCase(); + return "true".equals(s) || "1".equals(s) || "yes".equals(s); + } + private String toJson(Object v) { + try { + if (v == null) return null; + if (v instanceof String) return (String) v; + return objectMapper.writeValueAsString(v); + } catch (Exception e) { + return null; + } + } + @SuppressWarnings("unchecked") + private Map castMap(Object v) { + if (v instanceof Map) return (Map) v; + return new HashMap<>(); + } } diff --git a/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java b/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java index 70c8b15..25f3b14 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java @@ -285,6 +285,21 @@ public class ProjectController { )); } + //输入模拟数据,调用推理接口,得到推理结果并写入结果表 + @PostMapping("/simulation/run") + @Operation(summary = "运行项目模拟", description = "输入模拟数据,调用推理接口,得到推理结果并写入结果表") + public ResponseEntity> runSimulation(@RequestParam @Parameter(description = "项目ID", required = true) String projectId, + @RequestParam @Parameter(description = "情景ID", required = true) String scenarioId, + @RequestBody(required = false) Map params) { + var res = projectService.runSimulation(projectId, scenarioId, params == null ? Map.of() : params); + return ResponseEntity.ok(Map.of( + "code", 0, + "msg", "运行完成", + "data", res + )); + } + + } diff --git a/business-css/src/main/java/com/yfd/business/css/domain/Algorithm.java b/business-css/src/main/java/com/yfd/business/css/domain/Algorithm.java index 75a02db..fe7dcaf 100644 --- a/business-css/src/main/java/com/yfd/business/css/domain/Algorithm.java +++ b/business-css/src/main/java/com/yfd/business/css/domain/Algorithm.java @@ -9,57 +9,80 @@ import lombok.Data; import java.io.Serializable; import java.time.LocalDateTime; +/** + * 算法配置信息表 + * 存储算法的基本配置、参数和状态信息 + */ @Data @TableName("algorithm") public class Algorithm implements Serializable { + /** 序列化版本号 */ private static final long serialVersionUID = 1L; + /** 算法ID - 主键,UUID生成 */ @TableId(value = "algorithm_id", type = IdType.ASSIGN_UUID) private String algorithmId; + /** 算法类型 */ @TableField("algorithm_type") private String algorithmType; + /** 算法名称 */ @TableField("name") private String name; + /** 算法描述 */ @TableField("description") private String description; + /** 算法版本 - 版本号,例如v1.0 */ @TableField("version") private String version; + /** 算法原理说明 */ @TableField("principle") private String principle; + /** 调用参数 - 以JSON存储参数名称、类型、默认值等 */ @TableField("input_params") private String inputParams; + /** 输出参数 - 以JSON存储参数名称、类型、说明等 */ @TableField("output_params") private String outputParams; + /** 推理URL */ @TableField("infer_base_url") private String inferBaseUrl; + /** 训练URL */ @TableField("train_base_url") private String trainBaseUrl; + /** 支持设备类型 - JSON格式 */ @TableField("supported_device_types") private String supportedDeviceTypes; + /** 默认超参 - JSON格式 */ @TableField("default_hyper_params") private String defaultHyperParams; + /** 激活状态 - 1:激活;0:关闭 */ @TableField("status") private String status; + /** 更新时间 */ @TableField("updated_at") private LocalDateTime updatedAt; + /** 创建时间 */ @TableField("created_at") private LocalDateTime createdAt; + /** 最后修改人 */ @TableField("modifier") private String modifier; + + } diff --git a/business-css/src/main/java/com/yfd/business/css/domain/CriticalData.java b/business-css/src/main/java/com/yfd/business/css/domain/CriticalData.java index c01d596..7370c3d 100644 --- a/business-css/src/main/java/com/yfd/business/css/domain/CriticalData.java +++ b/business-css/src/main/java/com/yfd/business/css/domain/CriticalData.java @@ -7,45 +7,65 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.io.Serializable; -import java.time.LocalDateTime; import java.math.BigDecimal; +import java.time.LocalDateTime; +/** + * 设备临界数据表 + * 存储设备的临界数据信息,包括几何参数、材料特性和k_eff值 + */ @Data @TableName("critical_data") public class CriticalData implements Serializable { + /** 序列化版本号 */ private static final long serialVersionUID = 1L; + /** 临界数据主键 */ @TableId(value = "critical_id", type = IdType.ASSIGN_UUID) private String criticalId; + /** 设备类型 */ @TableField("device_type") private String deviceType; + /** 等效直径 */ @TableField("diameter") private BigDecimal diameter; + /** 等效高度 */ @TableField("height") private BigDecimal height; + /** 核材料浓度(U 或 Pu) */ @TableField("fissile_concentration") private BigDecimal fissileConcentration; + /** 同位素丰度(铀富集度 或 Pu-240 占比) */ @TableField("isotopic_abundance") private BigDecimal isotopicAbundance; + /** 扩展物理/算法特征 - JSON格式 */ @TableField("extra_features") private String extraFeatures; + /** 对应 k_eff 值 */ @TableField("keff_value") private BigDecimal keffValue; + /** 属性状态 - JSON格式 */ + @TableField("attr_state") + private String attrState; + + /** 创建时间 */ @TableField("created_at") private LocalDateTime createdAt; + /** 最后更新时间 */ @TableField("updated_at") private LocalDateTime updatedAt; + /** 最后修改人 */ @TableField("modifier") private String modifier; } diff --git a/business-css/src/main/java/com/yfd/business/css/domain/Project.java b/business-css/src/main/java/com/yfd/business/css/domain/Project.java index 9a1c8ba..82751ae 100644 --- a/business-css/src/main/java/com/yfd/business/css/domain/Project.java +++ b/business-css/src/main/java/com/yfd/business/css/domain/Project.java @@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.io.Serializable; @@ -32,11 +31,9 @@ public class Project implements Serializable { private String topology; @TableField("created_at") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createdAt; @TableField("updated_at") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updatedAt; @TableField("modifier") diff --git a/business-css/src/main/java/com/yfd/business/css/dto/EventAttrParseResult.java b/business-css/src/main/java/com/yfd/business/css/dto/EventAttrParseResult.java index cac2cd4..53c9857 100644 --- a/business-css/src/main/java/com/yfd/business/css/dto/EventAttrParseResult.java +++ b/business-css/src/main/java/com/yfd/business/css/dto/EventAttrParseResult.java @@ -11,6 +11,6 @@ public class EventAttrParseResult { private Map target; private String unit; private List segments; - private List> schedule; // optional derived ramp/step + private List> schedule; private List issues; -} +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/dto/EventAttrSegment.java b/business-css/src/main/java/com/yfd/business/css/dto/EventAttrSegment.java index ffbb9da..3e2b150 100644 --- a/business-css/src/main/java/com/yfd/business/css/dto/EventAttrSegment.java +++ b/business-css/src/main/java/com/yfd/business/css/dto/EventAttrSegment.java @@ -11,4 +11,4 @@ public class EventAttrSegment { private double end; private String interp; private List timeline; -} +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/dto/TopoEdge.java b/business-css/src/main/java/com/yfd/business/css/dto/TopoEdge.java index 0758780..83f2ef0 100644 --- a/business-css/src/main/java/com/yfd/business/css/dto/TopoEdge.java +++ b/business-css/src/main/java/com/yfd/business/css/dto/TopoEdge.java @@ -10,6 +10,6 @@ public class TopoEdge { private String toEntityType; private String toEntityId; private String toProperty; - private Double coefficient; - private Long delayMs; -} + private double coefficient; + private long delayMs; +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/dto/TopoNode.java b/business-css/src/main/java/com/yfd/business/css/dto/TopoNode.java index f6ba98c..8c83b87 100644 --- a/business-css/src/main/java/com/yfd/business/css/dto/TopoNode.java +++ b/business-css/src/main/java/com/yfd/business/css/dto/TopoNode.java @@ -8,4 +8,4 @@ public class TopoNode { private String entityId; private String property; private String unit; -} +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/dto/TopologyParseResult.java b/business-css/src/main/java/com/yfd/business/css/dto/TopologyParseResult.java index 69d882c..5d4a6d9 100644 --- a/business-css/src/main/java/com/yfd/business/css/dto/TopologyParseResult.java +++ b/business-css/src/main/java/com/yfd/business/css/dto/TopologyParseResult.java @@ -8,11 +8,11 @@ import java.util.Map; @Data public class TopologyParseResult { private String projectId; - private Integer deviceCount; - private Integer nodeCount; - private Integer edgeCount; private List nodes; private List edges; private List> plans; private List issues; -} + private int deviceCount; + private int nodeCount; + private int edgeCount; +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/mapper/AlgorithmModelMapper.java b/business-css/src/main/java/com/yfd/business/css/mapper/AlgorithmModelMapper.java index e4ec87b..50abae8 100644 --- a/business-css/src/main/java/com/yfd/business/css/mapper/AlgorithmModelMapper.java +++ b/business-css/src/main/java/com/yfd/business/css/mapper/AlgorithmModelMapper.java @@ -2,8 +2,7 @@ package com.yfd.business.css.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yfd.business.css.domain.AlgorithmModel; -import org.apache.ibatis.annotations.Mapper; -@Mapper public interface AlgorithmModelMapper extends BaseMapper { -} + +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java b/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java new file mode 100644 index 0000000..ca59bc4 --- /dev/null +++ b/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java @@ -0,0 +1,74 @@ +package com.yfd.business.css.model; + +import java.util.Map; + +public class DeviceStepInfo { + private String deviceId; + private String deviceType; + private Map properties; + private int step; + private int time; + + public DeviceStepInfo() {} + + public DeviceStepInfo(String deviceId, String deviceType, Map properties,int step,int time) { + this.deviceId = deviceId; + this.deviceType = deviceType; + this.properties = properties; + this.step = step; + this.time = time; + } + + // Getter和Setter方法 + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public int getTime() { + return time; + } + + public void setTime(int time) { + this.time = time; + } + + @Override + public String toString() { + return "DeviceStepInfo{" + + "deviceId='" + deviceId + '\'' + + ", deviceType='" + deviceType + '\'' + + ", properties=" + properties + + ", step=" + step + + ", time=" + time + + '}'; + } +} \ No newline at end of file diff --git a/business-css/src/main/java/com/yfd/business/css/model/InferRequest.java b/business-css/src/main/java/com/yfd/business/css/model/InferRequest.java new file mode 100644 index 0000000..08cc4ba --- /dev/null +++ b/business-css/src/main/java/com/yfd/business/css/model/InferRequest.java @@ -0,0 +1,37 @@ +package com.yfd.business.css.model; + +import java.util.List; +import java.util.Map; + +public class InferRequest { + private String modelDir; + private String deviceType; + private List> batch; + private Map features; + private Map meta; + + public InferRequest() {} + + // Getter和Setter方法 + public String getModelDir() { return modelDir; } + public void setModelDir(String modelDir) { this.modelDir = modelDir; } + public String getDeviceType() { return deviceType; } + public void setDeviceType(String deviceType) { this.deviceType = deviceType; } + public List> getBatch() { return batch; } + public void setBatch(List> batch) { this.batch = batch; } + public Map getFeatures() { return features; } + public void setFeatures(Map features) { this.features = features; } + public Map getMeta() { return meta; } + public void setMeta(Map meta) { this.meta = meta; } + + @Override + public String toString() { + return "InferRequest{" + + "modelDir='" + modelDir + '\'' + + ", deviceType='" + deviceType + '\'' + + ", batch=" + batch + + ", features=" + features + + ", meta=" + meta + + '}'; + } +} diff --git a/business-css/src/main/java/com/yfd/business/css/model/InferResponse.java b/business-css/src/main/java/com/yfd/business/css/model/InferResponse.java new file mode 100644 index 0000000..3280c24 --- /dev/null +++ b/business-css/src/main/java/com/yfd/business/css/model/InferResponse.java @@ -0,0 +1,78 @@ +package com.yfd.business.css.model; + +import java.util.List; +import java.util.Map; +import java.math.BigDecimal; + +public class InferResponse { + private int code; + private String msg; + private InferData data; + + public static class InferData { + private List items; + private Map meta; + private Map features; + private BigDecimal keff; + + // Getter和Setter方法 + public List getItems() { return items; } + public void setItems(List items) { this.items = items; } + public Map getMeta() { return meta; } + public void setMeta(Map meta) { this.meta = meta; } + public Map getFeatures() { return features; } + public void setFeatures(Map features) { this.features = features; } + public BigDecimal getKeff() { return keff; } + public void setKeff(BigDecimal keff) { this.keff = keff; } + + @Override + public String toString() { + return "InferData{" + + "items=" + items + + ", meta=" + meta + + ", features=" + features + + ", keff=" + keff + + '}'; + } + } + + public static class InferItem { + private Map meta; + private Map features; + private BigDecimal keff; + + // Getter和Setter方法 + public Map getMeta() { return meta; } + public void setMeta(Map meta) { this.meta = meta; } + public Map getFeatures() { return features; } + public void setFeatures(Map features) { this.features = features; } + public BigDecimal getKeff() { return keff; } + public void setKeff(BigDecimal keff) { this.keff = keff; } + + @Override + public String toString() { + return "InferItem{" + + "meta=" + meta + + ", features=" + features + + ", keff=" + keff + + '}'; + } + } + + // Getter和Setter方法 + public int getCode() { return code; } + public void setCode(int code) { this.code = code; } + public String getMsg() { return msg; } + public void setMsg(String msg) { this.msg = msg; } + public InferData getData() { return data; } + public void setData(InferData data) { this.data = data; } + + @Override + public String toString() { + return "InferResponse{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java b/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java index e1a4ecd..c88573b 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java @@ -4,4 +4,14 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.business.css.domain.AlgorithmModel; public interface AlgorithmModelService extends IService { + + /** + * 根据算法类型、设备类型,获取当前激活的模型路径 + * + * @param algorithmType 算法类型(如GPR/MLP/FastRBF) + * @param deviceType 设备类型(如CylindricalTank/AnnularTank) + * @return 激活版本的模型文件路径,如果不存在则返回null + */ + String getCurrentModelPath(String algorithmType, String deviceType) ; + } 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 new file mode 100644 index 0000000..83dc302 --- /dev/null +++ b/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java @@ -0,0 +1,175 @@ +package com.yfd.business.css.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.business.css.domain.ScenarioResult; +import com.yfd.business.css.model.DeviceStepInfo; +import com.yfd.business.css.model.InferRequest; +import com.yfd.business.css.model.InferResponse; + +import jakarta.annotation.Resource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; +import org.springframework.stereotype.Service; + +import java.util.*; +@Service +public class DeviceInferService { + @Value("${python.api.url:http://localhost:8000}") + private String pythonInferUrl; + + @Resource + private ScenarioService scenarioService; + @Resource + private AlgorithmModelService algorithmModelService; + @Resource + private ScenarioResultService scenarioResultService; + + @Autowired + private ObjectMapper objectMapper; + + public void processDeviceInference(String projectId, String scenarioId, + Map> groupedDevices) { + // 遍历每个设备类型,调用对应的模型进行推理 + for (Map.Entry> entry : groupedDevices.entrySet()) { + String deviceType = entry.getKey(); + // algorithmType通过scenarioId获取 + String algorithmType = scenarioService.getAlgorithmType(scenarioId); + if (algorithmType == null) { + throw new IllegalArgumentException("场景 " + scenarioId + " 未配置算法类型"); + } + + //modelPath根据模型类型、设备类型,从algorithm_model获取活动的model_path + String modelPath = algorithmModelService.getCurrentModelPath(algorithmType, deviceType); + System.out.println("modelPath="+modelPath); + if (modelPath == null) { + throw new IllegalArgumentException("未配置 " + algorithmType + " 模型路径"); + } + List devices = entry.getValue(); + System.out.println("devices="+devices); + // 校验设备数据是否完整 + if (devices == null || devices.isEmpty()) { + throw new IllegalArgumentException("设备数据为空,无法进行模拟"); + } + + // 封装推理请求 + InferRequest request = buildInferenceRequest(deviceType,devices,modelPath); + System.out.println("request="+request); + + // 调用Python推理服务 + InferResponse response = infer(request); + System.out.println("推理服务返回结果: " + response); + + // 处理推理结果 + if (response != null && response.getCode() == 0) { + // 重新构建InferResponse对象示例 + // 1. 从response获取数据 + int code = response.getCode(); + String msg = response.getMsg(); + InferResponse.InferData originalData = response.getData(); + + // 2. 重新构建InferData + InferResponse.InferData newData = new InferResponse.InferData(); + newData.setItems(originalData.getItems()); + newData.setMeta(originalData.getMeta()); + newData.setFeatures(originalData.getFeatures()); + newData.setKeff(originalData.getKeff()); + + // 3. 构建新的InferResponse + InferResponse reconstructedResponse = new InferResponse(); + reconstructedResponse.setCode(code); + reconstructedResponse.setMsg(msg); + reconstructedResponse.setData(newData); + + System.out.println("重新构建的response: " + reconstructedResponse); + + // 使用重新构建的response处理结果 + processInferenceResults(projectId, scenarioId, deviceType, devices, reconstructedResponse); + } else { + throw new RuntimeException("推理服务调用失败: " + (response != null ? response.getMsg() : "未知错误")); + } + } + } + + private InferRequest buildInferenceRequest(String deviceType,List devices,String modelPath) { + InferRequest request = new InferRequest(); + request.setModelDir(modelPath); // 设置模型路径 + request.setDeviceType(deviceType); + + // 构建批量推理数据 + List> batchData = new ArrayList<>(); + for (DeviceStepInfo device : devices) { + Map deviceData = new HashMap<>(); + deviceData.put("features", device.getProperties()); + deviceData.put("meta", buildDeviceMeta(device)); + batchData.add(deviceData); + } + + // 设置批量数据到请求中 + request.setBatch(batchData); + + return request; + } + + private Map buildDeviceMeta(DeviceStepInfo device) { + Map meta = new HashMap<>(); + meta.put("deviceId", device.getDeviceId()); + meta.put("deviceType", device.getDeviceType()); + meta.put("step", device.getStep()); + meta.put("time", device.getTime()); + return meta; + } + + private void processInferenceResults(String projectId, String scenarioId, String deviceType, + List devices, InferResponse response) { + // 处理推理结果,例如写入数据库 + List items = response.getData().getItems(); + List inferenceResults = new ArrayList<>(); + + for (int i = 0; i < items.size(); i++) { + InferResponse.InferItem item = items.get(i); + //DeviceStepInfo device = devices.get(i); + + ScenarioResult result = new ScenarioResult(); + //result.put("projectId", projectId); + result.setScenarioId(scenarioId); + //result.put("deviceType", deviceType); + result.setDeviceId((String) item.getMeta().get("deviceId")); + result.setKeffValue(item.getKeff()); + // 将 Map 转为 JSON 字符串后再设置 + try { + Map features = item.getFeatures(); + String featuresJson = objectMapper.writeValueAsString(features); + result.setAttrState(featuresJson); + } catch (Exception e) { + result.setAttrState("{}"); + } + //result.put("meta", item.getMeta()); + result.setStep((Integer) item.getMeta().get("time")); + + inferenceResults.add(result); + } + + + scenarioResultService.saveBatch(inferenceResults); + System.out.println("保存推理结果: " + inferenceResults.size() + " 条记录"); + } + + // 合并的Python推理调用方法 + public InferResponse infer(InferRequest request) { + String url = pythonInferUrl + "/v1/infer" ; + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + ResponseEntity response = restTemplate.postForEntity(url, entity, InferResponse.class); + return response.getBody(); + } +} diff --git a/business-css/src/main/java/com/yfd/business/css/service/ProjectService.java b/business-css/src/main/java/com/yfd/business/css/service/ProjectService.java index 16cdf92..46e5ffc 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/ProjectService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/ProjectService.java @@ -35,4 +35,13 @@ public interface ProjectService extends IService { java.util.List> parseDeviceOrderWithMaterials(String projectId); + /** + * 运行项目模拟 + * @param projectId 项目ID + * @param scenarioId 情景ID + * @param params 模拟参数 + * @return 模拟结果 + */ + java.util.Map runSimulation(String projectId, String scenarioId, java.util.Map params); + } diff --git a/business-css/src/main/java/com/yfd/business/css/service/ScenarioService.java b/business-css/src/main/java/com/yfd/business/css/service/ScenarioService.java index 5b29159..2dd1ab3 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/ScenarioService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/ScenarioService.java @@ -4,4 +4,6 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.business.css.domain.Scenario; public interface ScenarioService extends IService { + //根据场景id,获取算法类型 + String getAlgorithmType(String scenarioId); } diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java index 33d8806..dba7850 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java @@ -1,5 +1,6 @@ package com.yfd.business.css.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.business.css.domain.AlgorithmModel; import com.yfd.business.css.mapper.AlgorithmModelMapper; @@ -8,4 +9,14 @@ import org.springframework.stereotype.Service; @Service public class AlgorithmModelServiceImpl extends ServiceImpl implements AlgorithmModelService { + + @Override + public String getCurrentModelPath(String algorithmType, String deviceType) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("algorithm_type", algorithmType) + .eq("device_type", deviceType) + .eq("is_current", 1); // 当前激活版本 + AlgorithmModel model = getOne(queryWrapper); + return model != null ? model.getModelPath() : null; + } } diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java index a10d134..014fa76 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java @@ -8,6 +8,8 @@ import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import jakarta.annotation.Resource; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.yfd.business.css.domain.Device; import com.yfd.business.css.domain.Material; @@ -17,6 +19,9 @@ import com.yfd.business.css.domain.ScenarioResult; import com.yfd.business.css.service.DeviceService; import com.yfd.business.css.service.MaterialService; import com.yfd.business.css.service.ScenarioService; +import com.yfd.business.css.service.DeviceInferService; +import com.yfd.business.css.utils.DeviceDataParser; +import com.yfd.business.css.model.DeviceStepInfo; import com.yfd.business.css.service.EventService; import com.yfd.business.css.service.ScenarioResultService; import com.yfd.business.css.dto.TopologyParseResult; @@ -31,8 +36,7 @@ import java.util.HashSet; import java.util.Set; import java.util.Objects; import java.util.Comparator; -import java.util.LinkedList; -import java.util.Queue; + import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -55,6 +59,15 @@ public class ProjectServiceImpl private EventService eventService; @Resource private ScenarioResultService scenarioResultService; + @Resource + private DeviceInferService deviceInferService; + // 通过构造函数注入DeviceDataParser + @SuppressWarnings("unused") + private final DeviceDataParser deviceDataParser; + + public ProjectServiceImpl(DeviceDataParser deviceDataParser) { + this.deviceDataParser = deviceDataParser; + } @Override public byte[] exportAllProjectsExcel() { @@ -1221,4 +1234,59 @@ public class ProjectServiceImpl throw new RuntimeException(e); } } + + //运行项目模拟 + @Override + public java.util.Map runSimulation(String projectId, String scenarioId, java.util.Map params) { + //1. 校验项目是否存在 + Project project = this.getOne(new LambdaQueryWrapper().eq(Project::getProjectId, projectId)); + if (project == null) throw new IllegalArgumentException("项目不存在"); + //2. 校验情景是否存在 + Scenario scenario = scenarioService.getOne(new LambdaQueryWrapper().eq(Scenario::getScenarioId, scenarioId)); + if (scenario == null) throw new IllegalArgumentException("情景不存在"); + //获取该情景对应的算法类型 + String algorithmType = scenario.getAlgorithmType(); + if (algorithmType == null) throw new IllegalArgumentException("情景未指定算法类型"); + //3. 校验模拟参数是否完整 + if (params == null || params.isEmpty()) throw new IllegalArgumentException("模拟参数不能为空"); + //4. 解析模拟参数,生成模拟数据 + // 使用 Jackson 将 Map 转为 JSON 字符串 + ObjectMapper objectMapper = new ObjectMapper(); + String jsonParams; + try { + jsonParams = objectMapper.writeValueAsString(params); + } catch (com.fasterxml.jackson.core.JsonProcessingException e) { + throw new RuntimeException("模拟参数序列化失败", e); + } + // 解析并按设备类型分组,后续批量推理将依赖此结果 + System.out.println("projectId="+projectId); + System.out.println("scenarioId="+scenarioId); + System.out.println("params="+params); + System.out.println("jsonParams="+jsonParams); + + jsonParams ="{\"data\":{\"frames\":[{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":160.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":160.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":20.0,\"u_enrichment\":0.01,\"height\":20.0,\"u_concentration\":20.0}},\"step\":0,\"time\":0},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":170.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":170.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":50.0,\"u_enrichment\":0.01,\"height\":50.0,\"u_concentration\":20.0}},\"step\":1,\"time\":2},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":180.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":180.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":70.0,\"u_enrichment\":0.01,\"height\":70.0,\"u_concentration\":20.0}},\"step\":2,\"time\":4},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":190.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":190.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":100.0,\"u_enrichment\":0.01,\"height\":100.0,\"u_concentration\":20.0}},\"step\":3,\"time\":6},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":200.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":200.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":120.0,\"u_enrichment\":0.01,\"height\":120.0,\"u_concentration\":20.0}},\"step\":4,\"time\":8},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":210.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":210.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":150.0,\"u_enrichment\":0.01,\"height\":150.0,\"u_concentration\":20.0}},\"step\":5,\"time\":10},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":220.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":220.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":200.0,\"u_enrichment\":0.01,\"height\":200.0,\"u_concentration\":20.0}},\"step\":6,\"time\":12},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":230.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":230.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":220.0,\"u_enrichment\":0.01,\"height\":220.0,\"u_concentration\":20.0}},\"step\":7,\"time\":14},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":240.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":240.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":250.0,\"u_enrichment\":0.01,\"height\":250.0,\"u_concentration\":20.0}},\"step\":8,\"time\":16},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":250.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":250.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":300.0,\"u_enrichment\":0.01,\"height\":300.0,\"u_concentration\":20.0}},\"step\":9,\"time\":18}],\"generated\":{\"snapshots\":20,\"events\":2},\"projectId\":\"proj-0001-uuid\",\"scenarioId\":\"scen-001-uuid\",\"issues\":[]}}"; + + Map> groupedDevices = DeviceDataParser.parseAndGroupDeviceData(jsonParams); + if (groupedDevices == null || groupedDevices.isEmpty()) { + throw new IllegalArgumentException("解析后的设备数据为空,无法进行模拟"); + } + + // 输出结果 + for (Map.Entry> entry : groupedDevices.entrySet()) { + System.out.println("Device Type: " + entry.getKey()); + for (DeviceStepInfo device : entry.getValue()) { + System.out.println(" " + device); + } + System.out.println(); + } + //6. 调用模型进行推理 + deviceInferService.processDeviceInference(projectId, scenarioId, groupedDevices); + + + //var res = inferenceService.runInference(projectId, scenarioId, params); + //5. 写入结果表 + //scenarioResultMapper.insertBatch(res); + return Map.of(); + } } + diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/ScenarioServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/ScenarioServiceImpl.java index 4dba689..dcef204 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/ScenarioServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/ScenarioServiceImpl.java @@ -10,4 +10,12 @@ import org.springframework.stereotype.Service; public class ScenarioServiceImpl extends ServiceImpl implements ScenarioService { + @Override + public String getAlgorithmType(String scenarioId) { + Scenario scenario = baseMapper.selectById(scenarioId); + if (scenario == null) { + return null; + } + return scenario.getAlgorithmType(); + } } diff --git a/business-css/src/main/java/com/yfd/business/css/utils/DeviceDataParser.java b/business-css/src/main/java/com/yfd/business/css/utils/DeviceDataParser.java new file mode 100644 index 0000000..6ec8525 --- /dev/null +++ b/business-css/src/main/java/com/yfd/business/css/utils/DeviceDataParser.java @@ -0,0 +1,150 @@ +package com.yfd.business.css.utils; + +import java.util.*; +import java.util.stream.Collectors; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.business.css.model.DeviceStepInfo; +import org.springframework.stereotype.Component; + + +@Component // 添加注解 +public class DeviceDataParser { + + + + public static Map> parseAndGroupDeviceData(String jsonData) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = mapper.readTree(jsonData); + JsonNode framesNode = rootNode.get("data").get("frames"); + + List allDevices = new ArrayList<>(); + + for (JsonNode frameNode : framesNode) { + int step = frameNode.get("step").asInt(); + int time = frameNode.get("time").asInt(); + JsonNode devicesNode = frameNode.get("devices"); + + Iterator> devices = devicesNode.fields(); + while (devices.hasNext()) { + Map.Entry deviceEntry = devices.next(); + String deviceId = deviceEntry.getKey(); + JsonNode deviceInfo = deviceEntry.getValue(); + + String deviceType = deviceInfo.get("deviceType").asText(); + Map properties = new HashMap<>(); + + Iterator> fields = deviceInfo.fields(); + while (fields.hasNext()) { + Map.Entry field = fields.next(); + String fieldName = field.getKey(); + JsonNode fieldValue = field.getValue(); + + if (!"deviceType".equals(fieldName)) { + if (fieldValue.isDouble() || fieldValue.isFloat()) { + properties.put(fieldName, fieldValue.asDouble()); + } else if (fieldValue.isInt()) { + properties.put(fieldName, fieldValue.asInt()); + } else { + properties.put(fieldName, fieldValue.asText()); + } + } + } + + allDevices.add(new DeviceStepInfo(deviceId, deviceType, properties, step, time)); + } + } + + // 按设备类型分组 + return allDevices.stream() + .collect(Collectors.groupingBy(DeviceStepInfo::getDeviceType)); + + } catch (Exception e) { + e.printStackTrace(); + return new HashMap<>(); + } + } + + public static void main(String[] args) { + // String jsonData = "{\n" + + // " \"data\": {\n" + + // " \"frames\": [\n" + + // " {\n" + + // " \"devices\": {\n" + + // " \"dev-003-uuid\": {\n" + + // " \"deviceType\": \"AnnularTank\",\n" + + // " \"diameter\": 160.0,\n" + + // " \"pu_isotope\": 0.13,\n" + + // " \"pu_concentration\": 20.0,\n" + + // " \"height\": 160.0\n" + + // " },\n" + + // " \"dev-002-uuid\": {\n" + + // " \"deviceType\": \"CylindricalTank\",\n" + + // " \"diameter\": 20.0,\n" + + // " \"u_enrichment\": 0.01,\n" + + // " \"height\": 20.0,\n" + + // " \"u_concentration\": 20.0\n" + + // " }\n" + + // " },\n" + + // " \"step\": 0,\n" + + // " \"time\": 0\n" + + // " },\n" + + // " {\n" + + // " \"devices\": {\n" + + // " \"dev-003-uuid\": {\n" + + // " \"deviceType\": \"AnnularTank\",\n" + + // " \"diameter\": 170.0,\n" + + // " \"pu_isotope\": 0.13,\n" + + // " \"pu_concentration\": 20.0,\n" + + // " \"height\": 170.0\n" + + // " },\n" + + // " \"dev-002-uuid\": {\n" + + // " \"deviceType\": \"CylindricalTank\",\n" + + // " \"diameter\": 50.0,\n" + + // " \"u_enrichment\": 0.01,\n" + + // " \"height\": 50.0,\n" + + // " \"u_concentration\": 20.0\n" + + // " }\n" + + // " },\n" + + // " \"step\": 1,\n" + + // " \"time\": 2\n" + + // " },\n" + + // " {\n" + + // " \"devices\": {\n" + + // " \"dev-003-uuid\": {\n" + + // " \"deviceType\": \"AnnularTank\",\n" + + // " \"diameter\": 180.0,\n" + + // " \"pu_isotope\": 0.13,\n" + + // " \"pu_concentration\": 20.0,\n" + + // " \"height\": 180.0\n" + + // " },\n" + + // " \"dev-002-uuid\": {\n" + + // " \"deviceType\": \"CylindricalTank\",\n" + + // " \"diameter\": 70.0,\n" + + // " \"u_enrichment\": 0.01,\n" + + // " \"height\": 70.0,\n" + + // " \"u_concentration\": 20.0\n" + + // " }\n" + + // " },\n" + + // " \"step\": 2,\n" + + // " \"time\": 4\n" + + // " }\n" + + // " ]\n" + + // " }\n" + + // "}"; + // String jsonData = "{\"data\":{\"frames\":[{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":160.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":160.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":20.0,\"u_enrichment\":0.01,\"height\":20.0,\"u_concentration\":20.0}},\"step\":0,\"time\":0},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":170.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":170.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":50.0,\"u_enrichment\":0.01,\"height\":50.0,\"u_concentration\":20.0}},\"step\":1,\"time\":2},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":180.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":180.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":70.0,\"u_enrichment\":0.01,\"height\":70.0,\"u_concentration\":20.0}},\"step\":2,\"time\":4},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":190.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":190.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":100.0,\"u_enrichment\":0.01,\"height\":100.0,\"u_concentration\":20.0}},\"step\":3,\"time\":6},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":200.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":200.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":120.0,\"u_enrichment\":0.01,\"height\":120.0,\"u_concentration\":20.0}},\"step\":4,\"time\":8},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":210.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":210.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":150.0,\"u_enrichment\":0.01,\"height\":150.0,\"u_concentration\":20.0}},\"step\":5,\"time\":10},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":220.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":220.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":200.0,\"u_enrichment\":0.01,\"height\":200.0,\"u_concentration\":20.0}},\"step\":6,\"time\":12},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":230.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":230.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":220.0,\"u_enrichment\":0.01,\"height\":220.0,\"u_concentration\":20.0}},\"step\":7,\"time\":14},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":240.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":240.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":250.0,\"u_enrichment\":0.01,\"height\":250.0,\"u_concentration\":20.0}},\"step\":8,\"time\":16},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":250.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":250.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":300.0,\"u_enrichment\":0.01,\"height\":300.0,\"u_concentration\":20.0}},\"step\":9,\"time\":18}],\"generated\":{\"snapshots\":20,\"events\":2},\"projectId\":\"proj-0001-uuid\",\"scenarioId\":\"scen-001-uuid\",\"issues\":[]}}"; + String jsonData = "{\"data\":{\"frames\":[{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":160.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":160.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":20.0,\"u_enrichment\":0.01,\"height\":20.0,\"u_concentration\":20.0}},\"step\":0,\"time\":0},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":170.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":170.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":50.0,\"u_enrichment\":0.01,\"height\":50.0,\"u_concentration\":20.0}},\"step\":1,\"time\":2},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":180.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":180.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":70.0,\"u_enrichment\":0.01,\"height\":70.0,\"u_concentration\":20.0}},\"step\":2,\"time\":4},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":190.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":190.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":100.0,\"u_enrichment\":0.01,\"height\":100.0,\"u_concentration\":20.0}},\"step\":3,\"time\":6},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":200.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":200.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":120.0,\"u_enrichment\":0.01,\"height\":120.0,\"u_concentration\":20.0}},\"step\":4,\"time\":8},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":210.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":210.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":150.0,\"u_enrichment\":0.01,\"height\":150.0,\"u_concentration\":20.0}},\"step\":5,\"time\":10},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":220.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":220.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":200.0,\"u_enrichment\":0.01,\"height\":200.0,\"u_concentration\":20.0}},\"step\":6,\"time\":12},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":230.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":230.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":220.0,\"u_enrichment\":0.01,\"height\":220.0,\"u_concentration\":20.0}},\"step\":7,\"time\":14},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":240.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":240.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":250.0,\"u_enrichment\":0.01,\"height\":250.0,\"u_concentration\":20.0}},\"step\":8,\"time\":16},{\"devices\":{\"dev-003-uuid\":{\"deviceType\":\"AnnularTank\",\"diameter\":250.0,\"pu_isotope\":0.13,\"pu_concentration\":20.0,\"height\":250.0},\"dev-002-uuid\":{\"deviceType\":\"CylindricalTank\",\"diameter\":300.0,\"u_enrichment\":0.01,\"height\":300.0,\"u_concentration\":20.0}},\"step\":9,\"time\":18}],\"generated\":{\"snapshots\":20,\"events\":2},\"projectId\":\"proj-0001-uuid\",\"scenarioId\":\"scen-001-uuid\",\"issues\":[]}} "; + System.out.println("jsonData="+jsonData); + Map> groupedDevices = parseAndGroupDeviceData(jsonData); + + // 输出结果 + for (Map.Entry> entry : groupedDevices.entrySet()) { + System.out.println("Device Type: " + entry.getKey()); + for (DeviceStepInfo device : entry.getValue()) { + System.out.println(" " + device); + } + System.out.println(); + } + } +} diff --git a/business-css/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/business-css/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 69c419d..0000000 --- a/business-css/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.yfd.business.css.config.BusinessCssAutoConfiguration diff --git a/business-css/src/main/resources/application-business.yml b/business-css/src/main/resources/application-business.yml index 0beb7ad..222a435 100644 --- a/business-css/src/main/resources/application-business.yml +++ b/business-css/src/main/resources/application-business.yml @@ -27,3 +27,6 @@ logging: level: root: INFO +python: + api: + url: http://localhost:8000 diff --git a/business-css/src/main/resources/application-local.yml b/business-css/src/main/resources/application-local.yml deleted file mode 100644 index 9cf0fb1..0000000 --- a/business-css/src/main/resources/application-local.yml +++ /dev/null @@ -1,20 +0,0 @@ -server: - port: 8091 -spring: - application: - name: business-css - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:cssdb;MODE=MySQL;DB_CLOSE_DELAY=-1 - username: sa - password: - sql: - init: - mode: never -mybatis-plus: - configuration: - map-underscore-to-camel-case: true - cache-enabled: false - global-config: - db-config: - id-type: assign_uuid diff --git a/business-css/src/test/java/com/yfd/business/css/SimulationInitTest.java b/business-css/src/test/java/com/yfd/business/css/SimulationInitTest.java index 8666b8e..98c5499 100644 --- a/business-css/src/test/java/com/yfd/business/css/SimulationInitTest.java +++ b/business-css/src/test/java/com/yfd/business/css/SimulationInitTest.java @@ -69,9 +69,7 @@ public class SimulationInitTest { .andExpect(MockMvcResultMatchers.status().isOk()); String initBody = "{\"startTime\":0,\"endTime\":18,\"step\":2}"; - String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/simulation/init") - .param("projectId", projectId) - .param("scenarioId", scenarioId) + String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/" + projectId + "/scenarios/" + scenarioId + "/simulation/init") .contentType(MediaType.APPLICATION_JSON) .content(initBody)) .andExpect(MockMvcResultMatchers.status().isOk()) @@ -97,9 +95,7 @@ public class SimulationInitTest { String projectId = "proj-0001-uuid"; String scenarioId = "scen-001-uuid"; String initBody = "{\"startTime\":0,\"endTime\":10,\"step\":2}"; - String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/simulation/init") - .param("projectId", projectId) - .param("scenarioId", scenarioId) + String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/" + projectId + "/scenarios/" + scenarioId + "/simulation/init") .contentType(MediaType.APPLICATION_JSON) .content(initBody)) .andExpect(MockMvcResultMatchers.status().isOk()) @@ -118,35 +114,4 @@ public class SimulationInitTest { .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0)) .andExpect(MockMvcResultMatchers.jsonPath("$.data.records").isArray()); } - - @Test - public void initSimulation_returnData() throws Exception { - String projectId = "proj-0001-uuid"; - String scenarioId = "scen-001-uuid"; - String initBody = "{\"startTime\":0,\"endTime\":12,\"step\":3}"; - String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/simulation/init") - .param("projectId", projectId) - .param("scenarioId", scenarioId) - .contentType(MediaType.APPLICATION_JSON) - .content(initBody)) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0)) - .andReturn().getResponse().getContentAsString(); - System.out.println("initResp=" + initResp); - JsonNode initNode = objectMapper.readTree(initResp).path("data"); - int eventsCount = initNode.path("generated").path("events").asInt(); - int snapshotsCount = initNode.path("generated").path("snapshots").asInt(); - System.out.println("eventsCount=" + eventsCount + ", snapshotsCount=" + snapshotsCount); - String resultResp = mockMvc.perform(MockMvcRequestBuilders.get("/scenario-results/by-scenario") - .param("scenarioId", scenarioId) - .param("pageNum", "1") - .param("pageSize", "20")) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0)) - .andReturn().getResponse().getContentAsString(); - System.out.println("resultResp=" + resultResp); - JsonNode resultNode = objectMapper.readTree(resultResp).path("data").path("records"); - Assertions.assertTrue(resultNode.isArray()); - System.out.println("recordsSize=" + resultNode.size()); - } } diff --git a/framework/src/main/java/com/yfd/platform/config/SecurityConfig.java b/framework/src/main/java/com/yfd/platform/config/SecurityConfig.java index a4b2343..4c9710b 100644 --- a/framework/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/framework/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -4,6 +4,7 @@ import com.yfd.platform.config.bean.LoginProperties; import com.yfd.platform.exception.AccessDeniedHandExcetion; import com.yfd.platform.exception.AuthenticationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -19,7 +20,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic @Configuration public class SecurityConfig { - + // 1. 注入配置项,默认为 false + @Value("${security.dev.permit:false}") + private boolean devPermit; + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); @@ -50,31 +54,36 @@ public class SecurityConfig { http .csrf(csrf -> csrf.disable()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/user/login").anonymous() - .requestMatchers("/user/code").permitAll() - .requestMatchers(HttpMethod.GET, - "/*.html", - "/webSocket/**", - "/assets/**", - "/icon/**").permitAll() - .requestMatchers( - "/swagger-ui.html", - "/swagger-ui/**", - "/v3/api-docs/**", - "/v3/api-docs.yaml", - "/swagger-resources/**", - "/webjars/**", - "/*/api-docs").permitAll() - .requestMatchers( - "/report/**", - "/images/**", - "/pageimage/**", - "/avatar/**", - "/systemurl/**", - "/api/imageserver/upload").permitAll() - .anyRequest().authenticated() - ) + .authorizeHttpRequests(auth -> { + // 如果配置为 true,则允许所有请求 + if (devPermit) { + auth.anyRequest().permitAll(); + } else { + auth.requestMatchers("/user/login").anonymous() + .requestMatchers("/user/code").permitAll() + .requestMatchers(HttpMethod.GET, + "/*.html", + "/webSocket/**", + "/assets/**", + "/icon/**").permitAll() + .requestMatchers( + "/swagger-ui.html", + "/swagger-ui/**", + "/v3/api-docs/**", + "/v3/api-docs.yaml", + "/swagger-resources/**", + "/webjars/**", + "/*/api-docs").permitAll() + .requestMatchers( + "/report/**", + "/images/**", + "/pageimage/**", + "/avatar/**", + "/systemurl/**", + "/api/imageserver/upload").permitAll() + .anyRequest().authenticated(); + } + }) .cors(cors -> {}); http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/framework/src/main/resources/application.yml b/framework/src/main/resources/application.yml index 0d5cced..957f0ed 100644 --- a/framework/src/main/resources/application.yml +++ b/framework/src/main/resources/application.yml @@ -31,3 +31,4 @@ springdoc: swagger-ui: enabled: true path: /swagger-ui.html + diff --git a/logs/business-css.log b/logs/business-css.log new file mode 100644 index 0000000..2b8ec15 --- /dev/null +++ b/logs/business-css.log @@ -0,0 +1,335 @@ +2026-01-05T15:20:12.027+08:00 INFO 2372 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 2372 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:20:12.029+08:00 INFO 2372 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:20:14.178+08:00 INFO 2372 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:20:14.194+08:00 INFO 2372 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:20:14.195+08:00 INFO 2372 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:20:14.331+08:00 INFO 2372 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:20:14.331+08:00 INFO 2372 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2255 ms +2026-01-05T15:20:14.623+08:00 WARN 2372 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisConfig': Injection of resource dependencies failed +2026-01-05T15:20:14.628+08:00 INFO 2372 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:20:14.682+08:00 INFO 2372 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:20:14.708+08:00 ERROR 2372 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +A component required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T15:23:04.333+08:00 INFO 11384 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 11384 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:23:04.336+08:00 INFO 11384 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:23:06.304+08:00 INFO 11384 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:23:06.321+08:00 INFO 11384 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:23:06.321+08:00 INFO 11384 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:23:06.436+08:00 INFO 11384 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:23:06.437+08:00 INFO 11384 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2056 ms +2026-01-05T15:23:06.709+08:00 WARN 11384 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'algorithmController' defined in file [E:\projectJava\JavaProjectRepo\business-css\target\classes\com\yfd\business\css\controller\AlgorithmController.class]: Post-processing of merged bean definition failed +2026-01-05T15:23:06.713+08:00 INFO 11384 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:23:06.758+08:00 INFO 11384 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:23:06.779+08:00 ERROR 11384 --- [business-css] [main] o.s.boot.SpringApplication : Application run failed + +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'algorithmController' defined in file [E:\projectJava\JavaProjectRepo\business-css\target\classes\com\yfd\business\css\controller\AlgorithmController.class]: Post-processing of merged bean definition failed + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:577) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.0.jar:3.3.0] + at com.yfd.business.css.CriticalScenarioApplication.main(CriticalScenarioApplication.java:27) ~[classes/:na] +Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.yfd.business.css.controller.AlgorithmController] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@5e481248] + at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:757) ~[spring-core-6.1.8.jar:6.1.8] + at org.springframework.util.ReflectionUtils.doWithLocalFields(ReflectionUtils.java:689) ~[spring-core-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.buildResourceMetadata(CommonAnnotationBeanPostProcessor.java:431) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.findResourceMetadata(CommonAnnotationBeanPostProcessor.java:412) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:312) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1085) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:574) ~[spring-beans-6.1.8.jar:6.1.8] + ... 15 common frames omitted +Caused by: java.lang.NoClassDefFoundError: com/yfd/platform/system/service/IUserService + at java.base/java.lang.Class.getDeclaredFields0(Native Method) ~[na:na] + at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3299) ~[na:na] + at java.base/java.lang.Class.getDeclaredFields(Class.java:2373) ~[na:na] + at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:752) ~[spring-core-6.1.8.jar:6.1.8] + ... 21 common frames omitted +Caused by: java.lang.ClassNotFoundException: com.yfd.platform.system.service.IUserService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na] + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na] + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na] + ... 25 common frames omitted + +2026-01-05T15:23:25.835+08:00 INFO 10632 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 10632 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:23:25.837+08:00 INFO 10632 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:23:27.674+08:00 INFO 10632 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:23:27.693+08:00 INFO 10632 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:23:27.694+08:00 INFO 10632 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:23:27.807+08:00 INFO 10632 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:23:27.808+08:00 INFO 10632 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1929 ms +2026-01-05T15:23:28.080+08:00 WARN 10632 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'algorithmController' defined in file [E:\projectJava\JavaProjectRepo\business-css\target\classes\com\yfd\business\css\controller\AlgorithmController.class]: Post-processing of merged bean definition failed +2026-01-05T15:23:28.084+08:00 INFO 10632 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:23:28.130+08:00 INFO 10632 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:23:28.150+08:00 ERROR 10632 --- [business-css] [main] o.s.boot.SpringApplication : Application run failed + +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'algorithmController' defined in file [E:\projectJava\JavaProjectRepo\business-css\target\classes\com\yfd\business\css\controller\AlgorithmController.class]: Post-processing of merged bean definition failed + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:577) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.0.jar:3.3.0] + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.0.jar:3.3.0] + at com.yfd.business.css.CriticalScenarioApplication.main(CriticalScenarioApplication.java:27) ~[classes/:na] +Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.yfd.business.css.controller.AlgorithmController] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@5e481248] + at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:757) ~[spring-core-6.1.8.jar:6.1.8] + at org.springframework.util.ReflectionUtils.doWithLocalFields(ReflectionUtils.java:689) ~[spring-core-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.buildResourceMetadata(CommonAnnotationBeanPostProcessor.java:431) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.findResourceMetadata(CommonAnnotationBeanPostProcessor.java:412) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:312) ~[spring-context-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1085) ~[spring-beans-6.1.8.jar:6.1.8] + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:574) ~[spring-beans-6.1.8.jar:6.1.8] + ... 15 common frames omitted +Caused by: java.lang.NoClassDefFoundError: com/yfd/platform/system/service/IUserService + at java.base/java.lang.Class.getDeclaredFields0(Native Method) ~[na:na] + at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3299) ~[na:na] + at java.base/java.lang.Class.getDeclaredFields(Class.java:2373) ~[na:na] + at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:752) ~[spring-core-6.1.8.jar:6.1.8] + ... 21 common frames omitted +Caused by: java.lang.ClassNotFoundException: com.yfd.platform.system.service.IUserService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na] + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na] + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na] + ... 25 common frames omitted + +2026-01-05T15:29:34.916+08:00 INFO 30416 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 30416 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:29:34.919+08:00 INFO 30416 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:29:36.946+08:00 INFO 30416 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:29:36.976+08:00 INFO 30416 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:29:36.976+08:00 INFO 30416 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:29:37.100+08:00 INFO 30416 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:29:37.101+08:00 INFO 30416 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2130 ms +2026-01-05T15:29:37.366+08:00 WARN 30416 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'mybatisPlusInterceptor': No qualifying bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2026-01-05T15:29:37.370+08:00 INFO 30416 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:29:37.410+08:00 INFO 30416 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:29:37.431+08:00 ERROR 30416 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field mybatisPlusInterceptor in com.yfd.business.css.config.MybatisConfig required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T15:29:50.820+08:00 INFO 6636 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 6636 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:29:50.823+08:00 INFO 6636 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:29:52.756+08:00 INFO 6636 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:29:52.771+08:00 INFO 6636 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:29:52.771+08:00 INFO 6636 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:29:52.914+08:00 INFO 6636 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:29:52.915+08:00 INFO 6636 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2049 ms +2026-01-05T15:29:53.173+08:00 WARN 6636 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'mybatisPlusInterceptor': No qualifying bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2026-01-05T15:29:53.177+08:00 INFO 6636 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:29:53.222+08:00 INFO 6636 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:29:53.244+08:00 ERROR 6636 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field mybatisPlusInterceptor in com.yfd.business.css.config.MybatisConfig required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T15:30:03.576+08:00 INFO 22176 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 22176 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:30:03.579+08:00 INFO 22176 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:30:05.318+08:00 INFO 22176 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:30:05.330+08:00 INFO 22176 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:30:05.330+08:00 INFO 22176 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:30:05.460+08:00 INFO 22176 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:30:05.461+08:00 INFO 22176 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1836 ms +2026-01-05T15:30:05.727+08:00 WARN 22176 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'mybatisPlusInterceptor': No qualifying bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2026-01-05T15:30:05.730+08:00 INFO 22176 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:30:05.764+08:00 INFO 22176 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:30:05.782+08:00 ERROR 22176 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field mybatisPlusInterceptor in com.yfd.business.css.config.MybatisConfig required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T15:32:03.378+08:00 INFO 9616 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 9616 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:32:03.381+08:00 INFO 9616 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:32:05.424+08:00 INFO 9616 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:32:05.445+08:00 INFO 9616 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:32:05.446+08:00 INFO 9616 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:32:05.578+08:00 INFO 9616 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:32:05.579+08:00 INFO 9616 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2158 ms +2026-01-05T15:32:05.864+08:00 WARN 9616 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'mybatisPlusInterceptor': No qualifying bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2026-01-05T15:32:05.868+08:00 INFO 9616 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:32:05.918+08:00 INFO 9616 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:32:05.943+08:00 ERROR 9616 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field mybatisPlusInterceptor in com.yfd.business.css.config.MybatisConfig required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T15:34:01.386+08:00 INFO 18712 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 18712 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T15:34:01.389+08:00 INFO 18712 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T15:34:03.377+08:00 INFO 18712 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T15:34:03.392+08:00 INFO 18712 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T15:34:03.393+08:00 INFO 18712 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T15:34:03.536+08:00 INFO 18712 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T15:34:03.537+08:00 INFO 18712 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2109 ms +2026-01-05T15:34:03.793+08:00 WARN 18712 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mybatisConfig': Unsatisfied dependency expressed through field 'mybatisPlusInterceptor': No qualifying bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2026-01-05T15:34:03.797+08:00 INFO 18712 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T15:34:03.838+08:00 INFO 18712 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T15:34:03.860+08:00 ERROR 18712 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field mybatisPlusInterceptor in com.yfd.business.css.config.MybatisConfig required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T17:30:52.702+08:00 INFO 27432 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 27432 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T17:30:52.705+08:00 INFO 27432 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T17:30:54.802+08:00 INFO 27432 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T17:30:54.815+08:00 INFO 27432 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T17:30:54.816+08:00 INFO 27432 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T17:30:54.951+08:00 INFO 27432 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T17:30:54.952+08:00 INFO 27432 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2206 ms +2026-01-05T17:30:55.307+08:00 WARN 27432 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisConfig': Injection of resource dependencies failed +2026-01-05T17:30:55.310+08:00 INFO 27432 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T17:30:55.353+08:00 INFO 27432 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T17:30:55.377+08:00 ERROR 27432 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +A component required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + +2026-01-05T17:31:24.083+08:00 INFO 24748 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : Starting CriticalScenarioApplication using Java 17.0.17 with PID 24748 (E:\projectJava\JavaProjectRepo\business-css\target\classes started by Admin in E:\projectJava\JavaProjectRepo) +2026-01-05T17:31:24.086+08:00 INFO 24748 --- [business-css] [main] c.y.b.css.CriticalScenarioApplication : The following 2 profiles are active: "framework", "business" +2026-01-05T17:31:25.999+08:00 INFO 24748 --- [business-css] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8090 (http) +2026-01-05T17:31:26.013+08:00 INFO 24748 --- [business-css] [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2026-01-05T17:31:26.013+08:00 INFO 24748 --- [business-css] [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.24] +2026-01-05T17:31:26.151+08:00 INFO 24748 --- [business-css] [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2026-01-05T17:31:26.152+08:00 INFO 24748 --- [business-css] [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2022 ms +2026-01-05T17:31:26.407+08:00 WARN 24748 --- [business-css] [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisConfig': Injection of resource dependencies failed +2026-01-05T17:31:26.410+08:00 INFO 24748 --- [business-css] [main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] +2026-01-05T17:31:26.452+08:00 INFO 24748 --- [business-css] [main] .s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-01-05T17:31:26.480+08:00 ERROR 24748 --- [business-css] [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +A component required a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' that could not be found. + + +Action: + +Consider defining a bean of type 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor' in your configuration. + diff --git a/mvn-settings.xml b/mvn-settings.xml deleted file mode 100644 index d2918e8..0000000 --- a/mvn-settings.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - aliyunmaven - Aliyun Maven - central - https://maven.aliyun.com/repository/public - - - - - aliyun - - - central - https://repo.maven.apache.org/maven2 - - true - - - true - - - - - - central - https://repo.maven.apache.org/maven2 - - true - - - true - - - - - - - aliyun - - diff --git a/python-ml/__init__.py b/python-ml/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python-ml/__init__.py @@ -0,0 +1 @@ + diff --git a/python-ml/app.py b/python-ml/app.py new file mode 100644 index 0000000..8f0b92b --- /dev/null +++ b/python-ml/app.py @@ -0,0 +1,72 @@ +from flask import Flask, request, jsonify +import os +try: + from .service import train_one_type, infer_one, infer_batch, train_one_type_from_samples +except ImportError: + import sys + sys.path.append(os.path.dirname(__file__)) + from service import train_one_type, infer_one, infer_batch, train_one_type_from_samples + +app = Flask(__name__) + +def ensure_model_dir(device_type, model_dir): + root = model_dir if model_dir else os.path.join("models", device_type) + return root + +@app.post("/v1/train/") +def train(device_type): + data = request.get_json(force=True) + dataset_path = data.get("dataset_path") + model_dir = data.get("model_dir") + if not dataset_path or not os.path.exists(dataset_path): + return jsonify({"code": 1, "msg": "dataset_path not found"}), 400 + out_dir = ensure_model_dir(device_type, model_dir) + res = train_one_type(device_type, dataset_path, out_dir) + return jsonify({"code": 0, "msg": "ok", "data": res}) + +@app.post("/v1/train//from-samples") +def train_from_samples(device_type): + data = request.get_json(force=True) + samples = data.get("samples") + model_dir = data.get("model_dir") + if not samples or len(samples) == 0: + return jsonify({"code": 1, "msg": "samples required"}), 400 + feats = [] + labels = [] + for s in samples: + feats.append(s.get("features")) + labels.append(s.get("label")) + out_dir = ensure_model_dir(device_type, model_dir) + res = train_one_type_from_samples(device_type, feats, labels, out_dir) + return jsonify({"code": 0, "msg": "ok", "data": res}) + +@app.post("/v1/infer//keff") +def infer(device_type): + data = request.get_json(force=True) + model_dir = data.get("model_dir") + out_dir = ensure_model_dir(device_type, model_dir) + batch = data.get("batch") + features = data.get("features") + meta = data.get("meta") or {} + if batch and len(batch) > 0: + feats_list = [] + metas = [] + for s in batch: + if isinstance(s, dict) and "features" in s: + feats_list.append(s["features"]) + metas.append(s.get("meta") or {}) + else: + feats_list.append(s) + metas.append({}) + ys = infer_batch(device_type, feats_list, out_dir) + items = [] + for i, y in enumerate(ys): + items.append({"meta": metas[i], "features": feats_list[i], "keff": y}) + return jsonify({"code": 0, "msg": "ok", "data": {"items": items}}) + if not features: + return jsonify({"code": 1, "msg": "features required"}), 400 + y = infer_one(device_type, features, out_dir) + return jsonify({"code": 0, "msg": "ok", "data": {"meta": meta, "features": features, "keff": y}}) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/python-ml/service.py b/python-ml/service.py new file mode 100644 index 0000000..ed42fde --- /dev/null +++ b/python-ml/service.py @@ -0,0 +1,149 @@ +import os +import time +import pickle +import numpy as np +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import StandardScaler +from sklearn.gaussian_process import GaussianProcessRegressor +from sklearn.gaussian_process.kernels import RBF, RationalQuadratic, ConstantKernel, WhiteKernel +from sklearn.model_selection import train_test_split +from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, max_error +try: + from .type_config import load_from_excel, features_to_vector +except ImportError: + from type_config import load_from_excel, features_to_vector + +""" +训练与推理服务方法 +- build_gpr_model: 构建 GPR 模型 +- build_pipeline: 构建包含标准化与模型的 Pipeline +- compute_metrics: 计算评估指标 +- train_one_type: 按设备类型训练并保存模型 +- infer_one: 单条推理 +- infer_batch: 批量推理 +""" + +RANDOM_STATE = 42 +TEST_SIZE = 0.2 + +def build_gpr_model(): + """构建高斯过程回归模型,含复合核与输出中心化""" + kernel = ConstantKernel(1.0) * (0.7 * RBF(1.0) + 0.3 * RationalQuadratic(1.0, 1.0)) + WhiteKernel(1e-5) + return GaussianProcessRegressor(kernel=kernel, alpha=0.0, n_restarts_optimizer=5, normalize_y=True, random_state=RANDOM_STATE) + +def build_pipeline(): + """构建训练/推理管线:输入标准化 + 模型""" + return Pipeline([("scaler", StandardScaler()), ("model", build_gpr_model())]) + +def compute_metrics(y_true, y_pred): + """计算回归误差指标:RMSE/MAE/R2/maxe""" + return { + "rmse": float(np.sqrt(mean_squared_error(y_true, y_pred))), + "mae": float(mean_absolute_error(y_true, y_pred)), + "r2": float(r2_score(y_true, y_pred)), + "maxe": float(max_error(y_true, y_pred)) + } + +def train_one_type(device_type, dataset_path, model_dir): + """按设备类型训练模型并保存 Pipeline,返回指标与耗时""" + X, y = load_from_excel(device_type, dataset_path) + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_STATE) + pipe = build_pipeline() + pipe.fit(X_train, y_train) + X_all = np.vstack([X_train, X_test]) + y_all = np.concatenate([y_train, y_test]) + t0 = time.time() + pred_all = pipe.predict(X_all) + t1 = time.time() + os.makedirs(model_dir, exist_ok=True) + model_path = os.path.join(model_dir, "pipeline.pkl") + with open(model_path, "wb") as f: + pickle.dump(pipe, f) + return {"metrics": compute_metrics(y_all, pred_all), "infer_batch_time_sec": float(t1 - t0), "model_path": model_path} + +def infer_one(device_type, features, model_dir): + """加载 Pipeline,按设备类型将特征映射为向量并进行单条推理""" + model_path = os.path.join(model_dir, "pipeline.pkl") + with open(model_path, "rb") as f: + pipe = pickle.load(f) + X = features_to_vector(device_type, features) + y = pipe.predict(X) + return float(y[0]) + +def infer_batch(device_type, features_list, model_dir): + """加载 Pipeline,批量将特征映射为向量并进行推理""" + model_path = os.path.join(model_dir, "pipeline.pkl") + with open(model_path, "rb") as f: + pipe = pickle.load(f) + Xs = [] + for feat in features_list: + Xs.append(features_to_vector(device_type, feat)) + X = np.vstack(Xs) + ys = pipe.predict(X) + return [float(v) for v in ys] + +def train_one_type_from_samples(device_type, features_list, labels, model_dir): + """按设备类型基于样本集合训练模型并保存 Pipeline""" + Xs = [] + for feat in features_list: + Xs.append(features_to_vector(device_type, feat)) + X = np.vstack(Xs) + y = np.array(labels, dtype=float) + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_STATE) + pipe = build_pipeline() + pipe.fit(X_train, y_train) + X_all = np.vstack([X_train, X_test]) + y_all = np.concatenate([y_train, y_test]) + t0 = time.time() + pred_all = pipe.predict(X_all) + t1 = time.time() + os.makedirs(model_dir, exist_ok=True) + model_path = os.path.join(model_dir, "pipeline.pkl") + with open(model_path, "wb") as f: + pickle.dump(pipe, f) + return {"metrics": compute_metrics(y_all, pred_all), "infer_batch_time_sec": float(t1 - t0), "model_path": model_path} + +if __name__ == "__main__": + root = os.getcwd() + tasks = [ + ("cylindrical_tank", os.path.join(root, "circle.xlsx"), os.path.join(root, "models", "cylindrical_tank")), + ("ring_tank", os.path.join(root, "ring.xlsx"), os.path.join(root, "models", "ring_tank")) + ] + results = {} + for dt, ds, md in tasks: + if not os.path.exists(ds): + print(f"[skip] dataset not found: {ds}") + continue + res = train_one_type(dt, ds, md) + results[dt] = res + print(f"[train] {dt} -> {res}") + sample_cyl = {"直径": 160, "高度": 160, "铀浓度": 20, "铀富集度": 0.01} + cyl_dir = os.path.join(root, "models", "cylindrical_tank") + if os.path.exists(os.path.join(cyl_dir, "pipeline.pkl")): + y_cyl = infer_one("cylindrical_tank", sample_cyl, cyl_dir) + print(f"[infer] cylindrical_tank keff={y_cyl}") + sample_ring = {"外径": 70, "高度": 70, "Pu浓度": 40, "Pu240占比": 0.05} + ring_dir = os.path.join(root, "models", "ring_tank") + if os.path.exists(os.path.join(ring_dir, "pipeline.pkl")): + y_ring = infer_one("ring_tank", sample_ring, ring_dir) + print(f"[infer] ring_tank keff={y_ring}") + +def main(): + tasks = [ + ("cylindrical_tank", "circle.xlsx", os.path.join("models", "cylindrical_tank")), + ("ring_tank", "ring.xlsx", os.path.join("models", "ring_tank")) + ] + results = {} + for t, path, out in tasks: + res = train_one_type(t, path, out) + results[t] = res + print({"train_results": results}) + sample_cyl = {"直径": 160, "高度": 160, "铀浓度": 20, "铀富集度": 0.01} + y_cyl = infer_one("cylindrical_tank", sample_cyl, os.path.join("models", "cylindrical_tank")) + sample_ring = {"外径": 70, "高度": 70, "Pu浓度": 40, "Pu240占比": 0.05} + y_ring = infer_one("ring_tank", sample_ring, os.path.join("models", "ring_tank")) + print({"infer_examples": {"cylindrical_tank": {"features": sample_cyl, "keff": y_cyl}, + "ring_tank": {"features": sample_ring, "keff": y_ring}}}) + +if __name__ == "__main__": + main() diff --git a/python-ml/type_config.py b/python-ml/type_config.py new file mode 100644 index 0000000..94cad1a --- /dev/null +++ b/python-ml/type_config.py @@ -0,0 +1,90 @@ +import numpy as np +import pandas as pd + +def cfg_cylindrical(): + return { + "cols": ["diameter", "height", "fissile_concentration", "isotopic_abundance"], + "label": "keff", + "aliases": { + "直径": "diameter", + "高度": "height", + "铀浓度": "fissile_concentration", + "U浓度": "fissile_concentration", + "铀富集度": "isotopic_abundance", + "U富集度": "isotopic_abundance", + # 兼容旧英文键 + "u_concentration": "fissile_concentration", + "u_enrichment": "isotopic_abundance" + } + } + +def cfg_ring(): + return { + "cols": ["diameter", "height", "fissile_concentration", "isotopic_abundance"], + "label": "keff", + "aliases": { + "外径": "diameter", + "高度": "height", + "Pu浓度": "fissile_concentration", + "Pu240占比": "isotopic_abundance", + # 兼容旧英文键 + "pu_concentration": "fissile_concentration", + "pu_isotope": "isotopic_abundance" + } + } + +TYPE_CONFIG = { + "CylindricalTank": cfg_cylindrical(), + "AnnularTank": cfg_ring() +} + +def feature_names(device_type): + cfg = TYPE_CONFIG[device_type] + base = cfg["cols"] + if device_type in ("CylindricalTank", "AnnularTank"): + derived = [f"{base[0]}*{base[1]}", f"{base[2]}*{base[3]}"] + return base + derived + return base + +def derive_features(device_type, X): + if device_type in ("CylindricalTank", "AnnularTank"): + return np.hstack([X, (X[:, 0] * X[:, 1]).reshape(-1, 1), (X[:, 2] * X[:, 3]).reshape(-1, 1)]) + return X + +def _resolve_col(df, key, aliases): + if key in df.columns: + return df[key].values + for a, k in aliases.items(): + if k == key and a in df.columns: + return df[a].values + raise KeyError(key) + +def load_from_excel(device_type, path): + cfg = TYPE_CONFIG[device_type] + df = pd.read_excel(path) + cols = cfg["cols"] + aliases = cfg.get("aliases", {}) + arrs = [] + for k in cols: + v = _resolve_col(df, k, aliases) + arrs.append(v.astype(float)) + X = np.vstack(arrs).T + y = df[cfg["label"]].values.astype(float) if cfg["label"] in df.columns else df[cfg["label"]].values.astype(float) + Xd = derive_features(device_type, X) + return Xd, y + +def features_to_vector(device_type, features): + cfg = TYPE_CONFIG[device_type] + aliases = cfg.get("aliases", {}) + vals = [] + for k in cfg["cols"]: + v = features.get(k) + if v is None: + for a, ck in aliases.items(): + if ck == k and a in features: + v = features[a] + break + vals.append(float(v)) + X = np.array(vals, dtype=float).reshape(1, -1) + Xd = derive_features(device_type, X) + return Xd diff --git a/python_ml/type_config.py b/python_ml/type_config.py new file mode 100644 index 0000000..c6d8434 --- /dev/null +++ b/python_ml/type_config.py @@ -0,0 +1,52 @@ +import numpy as np +import pandas as pd + +""" +类型配置与特征管线 +职责: +- 为不同 device_type 定义输入列与 label 名称 +- 提供特征衍生(原始列→模型向量) +- 支持从 Excel 加载与将字典特征映射为向量 +""" + +def cfg_cylindrical(): + return { + "cols": ["直径", "高度", "铀浓度", "铀富集度"], + "label": "keff" + } + +def cfg_ring(): + return { + "cols": ["外径", "高度", "Pu浓度", "Pu240占比"], + "label": "keff" + } + +TYPE_CONFIG = { + "cylindrical_tank": cfg_cylindrical(), + "ring_tank": cfg_ring() +} + +def derive_features(device_type, X): + if device_type == "cylindrical_tank": + return np.hstack([X, (X[:, 0] * X[:, 1]).reshape(-1, 1), (X[:, 2] * X[:, 3]).reshape(-1, 1)]) + if device_type == "ring_tank": + return np.hstack([X, (X[:, 0] * X[:, 1]).reshape(-1, 1), (X[:, 2] * X[:, 3]).reshape(-1, 1)]) + return X + +def load_from_excel(device_type, path): + cfg = TYPE_CONFIG[device_type] + df = pd.read_excel(path) + X = df[cfg["cols"]].values.astype(float) + y = df[cfg["label"]].values.astype(float) + Xd = derive_features(device_type, X) + return Xd, y + +def features_to_vector(device_type, features): + cfg = TYPE_CONFIG[device_type] + vals = [] + for k in cfg["cols"]: + v = features.get(k) + vals.append(float(v)) + X = np.array(vals, dtype=float).reshape(1, -1) + Xd = derive_features(device_type, X) + return Xd diff --git a/scripts/mvn17.cmd b/scripts/mvn17.cmd deleted file mode 100644 index 73701a2..0000000 --- a/scripts/mvn17.cmd +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -set "JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-17.0.17.10-hotspot" -set "PATH=%JAVA_HOME%\bin;%PATH%" -mvn %*