Merge branch 'develop-business-css' of http://121.37.111.42:3000/ThbTech/JavaProjectRepo into develop-business-css

This commit is contained in:
limengnan 2026-01-13 15:11:45 +08:00
commit bd7292ccbe
42 changed files with 1716 additions and 183 deletions

7
.vscode/launch.json vendored
View File

@ -7,13 +7,16 @@
"hostName": "localhost", "hostName": "localhost",
"port": "5005" "port": "5005"
}, },
java
{ {
"type": "java", "type": "java",
"name": "CriticalScenarioApplication", "name": "CriticalScenarioApplication",
"request": "launch", "request": "launch",
"cwd": "${workspaceFolder}",
"mainClass": "com.yfd.business.css.CriticalScenarioApplication", "mainClass": "com.yfd.business.css.CriticalScenarioApplication",
"projectName": "business-css" "projectName": "business-css",
"args": "",
"envFile": "${workspaceFolder}/.env"
}, },
{ {
"type": "java", "type": "java",

View File

@ -51,6 +51,23 @@
4. 增加 OpenAPI 文档与前端集成接口规范。 4. 增加 OpenAPI 文档与前端集成接口规范。
5. 引入结果持久化与查询报表。 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 Program5005"** 配置(需确保 `.vscode/launch.json` 中已存在相应配置然后点击运行。IDE 将连接到正在运行的 Maven 进程,即可开始断点调试。
## 运维与配置 ## 运维与配置
- 端口默认 `8082`,环境覆盖通过 `application.yml` 与外部化配置。 - 端口默认 `8082`,环境覆盖通过 `application.yml` 与外部化配置。
- 数据库连接按环境注入dev/test/prod - 数据库连接按环境注入dev/test/prod

View File

@ -64,7 +64,14 @@
<artifactId>platform</artifactId> <artifactId>platform</artifactId>
<version>1.0</version> <version>1.0</version>
<classifier>plain</classifier> <classifier>plain</classifier>
</dependency> </dependency>
<!-- 显式添加 MyBatis-Plus 依赖,确保测试环境类加载正确 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.6</version>
</dependency>
<!-- Lombok --> <!-- Lombok -->

View File

@ -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 {
}

View File

@ -14,9 +14,6 @@ import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilde
import org.springdoc.core.models.GroupedOpenApi; import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.time.format.DateTimeFormatter;
import java.io.IOException; import java.io.IOException;
@ -41,9 +38,6 @@ public class OpenApiConfig {
builder.deserializers(new LocalDateTimeDeserializer(fmt)); builder.deserializers(new LocalDateTimeDeserializer(fmt));
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
builder.serializationInclusion(JsonInclude.Include.ALWAYS); 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(); SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() { module.setSerializerModifier(new BeanSerializerModifier() {
@Override @Override

View File

@ -11,11 +11,15 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.authentication.AnonymousAuthenticationToken; 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.util.List;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@RestController @RestController
@RequestMapping("/algorithms") @RequestMapping("/algorithms")
@Tag(name = "算法接口", description = "算法字典的增删改查与搜索")
public class AlgorithmController { public class AlgorithmController {
@Autowired @Autowired
@ -25,11 +29,13 @@ public class AlgorithmController {
@GetMapping("/{id}") @GetMapping("/{id}")
@Operation(summary = "根据算法ID获取算法", description = "路径参数传入算法ID返回算法对象")
public Algorithm getAlgorithmById(@PathVariable String id) { public Algorithm getAlgorithmById(@PathVariable String id) {
return algorithmService.getById(id); return algorithmService.getById(id);
} }
@PostMapping @PostMapping
@Operation(summary = "新增算法", description = "请求体传入算法对象,返回是否新增成功")
public boolean createAlgorithm(@RequestBody Algorithm algorithm) { public boolean createAlgorithm(@RequestBody Algorithm algorithm) {
algorithm.setModifier(currentUsername()); algorithm.setModifier(currentUsername());
algorithm.setCreatedAt(LocalDateTime.now()); algorithm.setCreatedAt(LocalDateTime.now());
@ -38,6 +44,7 @@ public class AlgorithmController {
} }
@PutMapping @PutMapping
@Operation(summary = "修改算法", description = "请求体传入算法对象(需包含主键),返回是否修改成功")
public boolean updateAlgorithm(@RequestBody Algorithm algorithm) { public boolean updateAlgorithm(@RequestBody Algorithm algorithm) {
algorithm.setModifier(currentUsername()); algorithm.setModifier(currentUsername());
algorithm.setUpdatedAt(LocalDateTime.now()); algorithm.setUpdatedAt(LocalDateTime.now());
@ -45,15 +52,57 @@ public class AlgorithmController {
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@Operation(summary = "删除算法(单条)", description = "根据算法ID删除算法")
public boolean deleteAlgorithm(@PathVariable String id) { public boolean deleteAlgorithm(@PathVariable String id) {
return algorithmService.removeById(id); return algorithmService.removeById(id);
} }
@DeleteMapping @DeleteMapping
@Operation(summary = "删除算法(批量)", description = "请求体传入算法ID列表批量删除算法")
public boolean deleteAlgorithms(@RequestBody List<String> ids) { public boolean deleteAlgorithms(@RequestBody List<String> ids) {
return algorithmService.removeByIds(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<Algorithm> getActiveAlgorithms() {
QueryWrapper<Algorithm> qw = new QueryWrapper<>();
qw.eq("status", "1");
return algorithmService.list(qw);
}
/** /**
* 根据算法名称搜索并分页返回 * 根据算法名称搜索并分页返回
* 输入参数查询参数 name算法名称关键词可为空pageNum页码默认1pageSize每页条数默认10 * 输入参数查询参数 name算法名称关键词可为空pageNum页码默认1pageSize每页条数默认10
@ -64,6 +113,7 @@ public class AlgorithmController {
* @return 算法分页列表 * @return 算法分页列表
*/ */
@GetMapping("/search") @GetMapping("/search")
@Operation(summary = "搜索算法并分页返回", description = "按名称关键词模糊查询,返回分页结果")
public Page<Algorithm> searchAlgorithms(@RequestParam(required = false) String name, public Page<Algorithm> searchAlgorithms(@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {

View File

@ -3,7 +3,9 @@ package com.yfd.business.css.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.business.css.domain.AlgorithmModel; 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.AlgorithmModelService;
import com.yfd.business.css.service.AlgorithmService;
import com.yfd.platform.system.service.IUserService; import com.yfd.platform.system.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; 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.core.Authentication;
import org.springframework.security.authentication.AnonymousAuthenticationToken; 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.time.LocalDateTime;
import java.util.List; import java.util.List;
@RestController @RestController
@RequestMapping("/algorithm-models") @RequestMapping("/algorithm-models")
@Tag(name = "算法模型接口", description = "算法模型版本的增删改查、查询当前版本与在线训练")
public class AlgorithmModelController { public class AlgorithmModelController {
@Autowired @Autowired
private AlgorithmModelService algorithmModelService; private AlgorithmModelService algorithmModelService;
@Autowired @Autowired
private IUserService userService; private IUserService userService;
@Autowired
private AlgorithmService algorithmService;
@Autowired
private ObjectMapper objectMapper;
@GetMapping("/{id}") @GetMapping("/{id}")
@Operation(summary = "根据模型ID获取模型版本", description = "路径参数传入模型ID返回模型版本对象")
public AlgorithmModel getById(@PathVariable String id) { public AlgorithmModel getById(@PathVariable String id) {
return algorithmModelService.getById(id); return algorithmModelService.getById(id);
} }
@PostMapping @PostMapping
@Operation(summary = "新增模型版本", description = "请求体传入模型版本对象,返回是否新增成功")
public boolean create(@RequestBody AlgorithmModel model) { public boolean create(@RequestBody AlgorithmModel model) {
model.setModifier(currentUsername()); model.setModifier(currentUsername());
model.setCreatedAt(LocalDateTime.now()); model.setCreatedAt(LocalDateTime.now());
@ -37,6 +61,7 @@ public class AlgorithmModelController {
} }
@PutMapping @PutMapping
@Operation(summary = "修改模型版本", description = "请求体传入模型版本对象(需包含主键),返回是否修改成功")
public boolean update(@RequestBody AlgorithmModel model) { public boolean update(@RequestBody AlgorithmModel model) {
model.setModifier(currentUsername()); model.setModifier(currentUsername());
model.setUpdatedAt(LocalDateTime.now()); model.setUpdatedAt(LocalDateTime.now());
@ -44,24 +69,31 @@ public class AlgorithmModelController {
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@Operation(summary = "删除模型版本(单条)", description = "根据模型ID删除模型版本")
public boolean delete(@PathVariable String id) { public boolean delete(@PathVariable String id) {
return algorithmModelService.removeById(id); return algorithmModelService.removeById(id);
} }
@DeleteMapping @DeleteMapping
@Operation(summary = "删除模型版本(批量)", description = "请求体传入模型ID列表批量删除模型版本")
public boolean deleteBatch(@RequestBody List<String> ids) { public boolean deleteBatch(@RequestBody List<String> ids) {
return algorithmModelService.removeByIds(ids); return algorithmModelService.removeByIds(ids);
} }
//返回该算法+设备类型的版本列表 //返回该算法+设备类型的版本列表
@GetMapping("/search") @GetMapping("/search")
@Operation(summary = "查询模型版本列表", description = "按算法类型与设备类型过滤并分页返回模型版本")
public Page<AlgorithmModel> search(@RequestParam(required = false) String algorithmType, public Page<AlgorithmModel> search(@RequestParam(required = false) String algorithmType,
@RequestParam(required = false) String deviceType, @RequestParam(required = false) String deviceType,
@RequestParam(required = false) String versionTag,
@RequestParam(required = false) String isCurrent,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType); if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType);
if (deviceType != null && !deviceType.isEmpty()) qw.eq("device_type", deviceType); 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"); qw.orderByDesc("updated_at");
Page<AlgorithmModel> page = new Page<>(pageNum, pageSize, true); Page<AlgorithmModel> page = new Page<>(pageNum, pageSize, true);
return algorithmModelService.page(page, qw); return algorithmModelService.page(page, qw);
@ -69,6 +101,7 @@ public class AlgorithmModelController {
//返回该算法+设备类型的当前激活版本 //返回该算法+设备类型的当前激活版本
@GetMapping("/current") @GetMapping("/current")
@Operation(summary = "获取当前激活版本", description = "根据算法类型与设备类型,返回 is_current=1 的模型版本")
public AlgorithmModel getCurrent(@RequestParam String algorithmType, public AlgorithmModel getCurrent(@RequestParam String algorithmType,
@RequestParam String deviceType) { @RequestParam String deviceType) {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
@ -81,6 +114,7 @@ public class AlgorithmModelController {
//版本激活 //版本激活
@PostMapping("/activate") @PostMapping("/activate")
@Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组其他版本设为非当前")
public boolean activate(@RequestParam String algorithmModelId) { public boolean activate(@RequestParam String algorithmModelId) {
AlgorithmModel model = algorithmModelService.getById(algorithmModelId); AlgorithmModel model = algorithmModelService.getById(algorithmModelId);
if (model == null) return false; if (model == null) return false;
@ -98,15 +132,122 @@ public class AlgorithmModelController {
return algorithmModelService.updateById(model); return algorithmModelService.updateById(model);
} }
//在线训练 // 在线训练Excel 数据集
@PostMapping("/train/excel")
@Operation(summary = "在线训练Excel", description = "传入算法类型、设备类型与Excel路径训练完成新增模型版本记录可选激活")
public Map<String, Object> trainExcel(@RequestBody Map<String, Object> 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<String, Object> payload = new HashMap<>();
payload.put("dataset_path", datasetPath);
if (!isBlank(modelDir)) payload.put("model_dir", modelDir);
Map<String, Object> 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<String, Object> 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<AlgorithmModel> 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) { @PostMapping("/train/samples")
AlgorithmModel model = algorithmModelService.getById(algorithmModelId); @Operation(summary = "在线训练(样本集合)", description = "传入算法类型、设备类型与样本集,训练完成新增模型版本记录,可选激活")
if (model == null) return false; public Map<String, Object> trainSamples(@RequestBody Map<String, Object> body) {
// 调用训练接口 String algorithmType = str(body.get("algorithm_type"));
// ... String deviceType = str(body.get("device_type"));
return true; Object samples = body.get("samples"); // 期望为 List<Map>由前端提供
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<String, Object> payload = new HashMap<>();
payload.put("samples", samples);
if (!isBlank(modelDir)) payload.put("model_dir", modelDir);
Map<String, Object> 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<String, Object> 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<AlgorithmModel> 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"; return "anonymous";
} }
} }
private Algorithm getAlgorithmByType(String algorithmType) {
QueryWrapper<Algorithm> qw = new QueryWrapper<>();
qw.eq("algorithm_type", algorithmType);
return algorithmService.getOne(qw);
}
private Map<String, Object> 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<String> res = client.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (res.statusCode() >= 200 && res.statusCode() < 300) {
return objectMapper.readValue(res.body(), new TypeReference<Map<String, Object>>() {});
}
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<String, Object> castMap(Object v) {
if (v instanceof Map) return (Map<String, Object>) v;
return new HashMap<>();
}
} }

View File

@ -285,6 +285,21 @@ public class ProjectController {
)); ));
} }
//输入模拟数据调用推理接口得到推理结果并写入结果表
@PostMapping("/simulation/run")
@Operation(summary = "运行项目模拟", description = "输入模拟数据,调用推理接口,得到推理结果并写入结果表")
public ResponseEntity<Map<String, Object>> runSimulation(@RequestParam @Parameter(description = "项目ID", required = true) String projectId,
@RequestParam @Parameter(description = "情景ID", required = true) String scenarioId,
@RequestBody(required = false) Map<String, Object> params) {
var res = projectService.runSimulation(projectId, scenarioId, params == null ? Map.of() : params);
return ResponseEntity.ok(Map.of(
"code", 0,
"msg", "运行完成",
"data", res
));
}
} }

View File

@ -9,57 +9,80 @@ import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/**
* 算法配置信息表
* 存储算法的基本配置参数和状态信息
*/
@Data @Data
@TableName("algorithm") @TableName("algorithm")
public class Algorithm implements Serializable { public class Algorithm implements Serializable {
/** 序列化版本号 */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 算法ID - 主键UUID生成 */
@TableId(value = "algorithm_id", type = IdType.ASSIGN_UUID) @TableId(value = "algorithm_id", type = IdType.ASSIGN_UUID)
private String algorithmId; private String algorithmId;
/** 算法类型 */
@TableField("algorithm_type") @TableField("algorithm_type")
private String algorithmType; private String algorithmType;
/** 算法名称 */
@TableField("name") @TableField("name")
private String name; private String name;
/** 算法描述 */
@TableField("description") @TableField("description")
private String description; private String description;
/** 算法版本 - 版本号例如v1.0 */
@TableField("version") @TableField("version")
private String version; private String version;
/** 算法原理说明 */
@TableField("principle") @TableField("principle")
private String principle; private String principle;
/** 调用参数 - 以JSON存储参数名称、类型、默认值等 */
@TableField("input_params") @TableField("input_params")
private String inputParams; private String inputParams;
/** 输出参数 - 以JSON存储参数名称、类型、说明等 */
@TableField("output_params") @TableField("output_params")
private String outputParams; private String outputParams;
/** 推理URL */
@TableField("infer_base_url") @TableField("infer_base_url")
private String inferBaseUrl; private String inferBaseUrl;
/** 训练URL */
@TableField("train_base_url") @TableField("train_base_url")
private String trainBaseUrl; private String trainBaseUrl;
/** 支持设备类型 - JSON格式 */
@TableField("supported_device_types") @TableField("supported_device_types")
private String supportedDeviceTypes; private String supportedDeviceTypes;
/** 默认超参 - JSON格式 */
@TableField("default_hyper_params") @TableField("default_hyper_params")
private String defaultHyperParams; private String defaultHyperParams;
/** 激活状态 - 1:激活0关闭 */
@TableField("status") @TableField("status")
private String status; private String status;
/** 更新时间 */
@TableField("updated_at") @TableField("updated_at")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
/** 创建时间 */
@TableField("created_at") @TableField("created_at")
private LocalDateTime createdAt; private LocalDateTime createdAt;
/** 最后修改人 */
@TableField("modifier") @TableField("modifier")
private String modifier; private String modifier;
} }

View File

@ -7,45 +7,65 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 设备临界数据表
* 存储设备的临界数据信息包括几何参数材料特性和k_eff值
*/
@Data @Data
@TableName("critical_data") @TableName("critical_data")
public class CriticalData implements Serializable { public class CriticalData implements Serializable {
/** 序列化版本号 */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 临界数据主键 */
@TableId(value = "critical_id", type = IdType.ASSIGN_UUID) @TableId(value = "critical_id", type = IdType.ASSIGN_UUID)
private String criticalId; private String criticalId;
/** 设备类型 */
@TableField("device_type") @TableField("device_type")
private String deviceType; private String deviceType;
/** 等效直径 */
@TableField("diameter") @TableField("diameter")
private BigDecimal diameter; private BigDecimal diameter;
/** 等效高度 */
@TableField("height") @TableField("height")
private BigDecimal height; private BigDecimal height;
/** 核材料浓度U 或 Pu */
@TableField("fissile_concentration") @TableField("fissile_concentration")
private BigDecimal fissileConcentration; private BigDecimal fissileConcentration;
/** 同位素丰度(铀富集度 或 Pu-240 占比) */
@TableField("isotopic_abundance") @TableField("isotopic_abundance")
private BigDecimal isotopicAbundance; private BigDecimal isotopicAbundance;
/** 扩展物理/算法特征 - JSON格式 */
@TableField("extra_features") @TableField("extra_features")
private String extraFeatures; private String extraFeatures;
/** 对应 k_eff 值 */
@TableField("keff_value") @TableField("keff_value")
private BigDecimal keffValue; private BigDecimal keffValue;
/** 属性状态 - JSON格式 */
@TableField("attr_state")
private String attrState;
/** 创建时间 */
@TableField("created_at") @TableField("created_at")
private LocalDateTime createdAt; private LocalDateTime createdAt;
/** 最后更新时间 */
@TableField("updated_at") @TableField("updated_at")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
/** 最后修改人 */
@TableField("modifier") @TableField("modifier")
private String modifier; private String modifier;
} }

View File

@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@ -32,11 +31,9 @@ public class Project implements Serializable {
private String topology; private String topology;
@TableField("created_at") @TableField("created_at")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@TableField("updated_at") @TableField("updated_at")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@TableField("modifier") @TableField("modifier")

View File

@ -11,6 +11,6 @@ public class EventAttrParseResult {
private Map<String, String> target; private Map<String, String> target;
private String unit; private String unit;
private List<EventAttrSegment> segments; private List<EventAttrSegment> segments;
private List<Map<String, Object>> schedule; // optional derived ramp/step private List<Map<String, Object>> schedule;
private List<String> issues; private List<String> issues;
} }

View File

@ -11,4 +11,4 @@ public class EventAttrSegment {
private double end; private double end;
private String interp; private String interp;
private List<EventAttrPoint> timeline; private List<EventAttrPoint> timeline;
} }

View File

@ -10,6 +10,6 @@ public class TopoEdge {
private String toEntityType; private String toEntityType;
private String toEntityId; private String toEntityId;
private String toProperty; private String toProperty;
private Double coefficient; private double coefficient;
private Long delayMs; private long delayMs;
} }

View File

@ -8,4 +8,4 @@ public class TopoNode {
private String entityId; private String entityId;
private String property; private String property;
private String unit; private String unit;
} }

View File

@ -8,11 +8,11 @@ import java.util.Map;
@Data @Data
public class TopologyParseResult { public class TopologyParseResult {
private String projectId; private String projectId;
private Integer deviceCount;
private Integer nodeCount;
private Integer edgeCount;
private List<TopoNode> nodes; private List<TopoNode> nodes;
private List<TopoEdge> edges; private List<TopoEdge> edges;
private List<Map<String, Object>> plans; private List<Map<String, Object>> plans;
private List<String> issues; private List<String> issues;
} private int deviceCount;
private int nodeCount;
private int edgeCount;
}

View File

@ -2,8 +2,7 @@ package com.yfd.business.css.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.business.css.domain.AlgorithmModel; import com.yfd.business.css.domain.AlgorithmModel;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AlgorithmModelMapper extends BaseMapper<AlgorithmModel> { public interface AlgorithmModelMapper extends BaseMapper<AlgorithmModel> {
}
}

View File

@ -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<String, Object> properties;
private int step;
private int time;
public DeviceStepInfo() {}
public DeviceStepInfo(String deviceId, String deviceType, Map<String, Object> 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<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> 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 +
'}';
}
}

View File

@ -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<Map<String, Object>> batch;
private Map<String, Object> features;
private Map<String, Object> 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<Map<String, Object>> getBatch() { return batch; }
public void setBatch(List<Map<String, Object>> batch) { this.batch = batch; }
public Map<String, Object> getFeatures() { return features; }
public void setFeatures(Map<String, Object> features) { this.features = features; }
public Map<String, Object> getMeta() { return meta; }
public void setMeta(Map<String, Object> meta) { this.meta = meta; }
@Override
public String toString() {
return "InferRequest{" +
"modelDir='" + modelDir + '\'' +
", deviceType='" + deviceType + '\'' +
", batch=" + batch +
", features=" + features +
", meta=" + meta +
'}';
}
}

View File

@ -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<InferItem> items;
private Map<String, Object> meta;
private Map<String, Object> features;
private BigDecimal keff;
// Getter和Setter方法
public List<InferItem> getItems() { return items; }
public void setItems(List<InferItem> items) { this.items = items; }
public Map<String, Object> getMeta() { return meta; }
public void setMeta(Map<String, Object> meta) { this.meta = meta; }
public Map<String, Object> getFeatures() { return features; }
public void setFeatures(Map<String, Object> 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<String, Object> meta;
private Map<String, Object> features;
private BigDecimal keff;
// Getter和Setter方法
public Map<String, Object> getMeta() { return meta; }
public void setMeta(Map<String, Object> meta) { this.meta = meta; }
public Map<String, Object> getFeatures() { return features; }
public void setFeatures(Map<String, Object> 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 +
'}';
}
}

View File

@ -4,4 +4,14 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.business.css.domain.AlgorithmModel; import com.yfd.business.css.domain.AlgorithmModel;
public interface AlgorithmModelService extends IService<AlgorithmModel> { public interface AlgorithmModelService extends IService<AlgorithmModel> {
/**
* 根据算法类型设备类型获取当前激活的模型路径
*
* @param algorithmType 算法类型如GPR/MLP/FastRBF
* @param deviceType 设备类型如CylindricalTank/AnnularTank
* @return 激活版本的模型文件路径如果不存在则返回null
*/
String getCurrentModelPath(String algorithmType, String deviceType) ;
} }

View File

@ -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<String, List<DeviceStepInfo>> groupedDevices) {
// 遍历每个设备类型调用对应的模型进行推理
for (Map.Entry<String, List<DeviceStepInfo>> 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<DeviceStepInfo> 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<DeviceStepInfo> devices,String modelPath) {
InferRequest request = new InferRequest();
request.setModelDir(modelPath); // 设置模型路径
request.setDeviceType(deviceType);
// 构建批量推理数据
List<Map<String, Object>> batchData = new ArrayList<>();
for (DeviceStepInfo device : devices) {
Map<String, Object> deviceData = new HashMap<>();
deviceData.put("features", device.getProperties());
deviceData.put("meta", buildDeviceMeta(device));
batchData.add(deviceData);
}
// 设置批量数据到请求中
request.setBatch(batchData);
return request;
}
private Map<String, Object> buildDeviceMeta(DeviceStepInfo device) {
Map<String, Object> 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<DeviceStepInfo> devices, InferResponse response) {
// 处理推理结果例如写入数据库
List<InferResponse.InferItem> items = response.getData().getItems();
List<ScenarioResult> 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<String,Object> 转为 JSON 字符串后再设置
try {
Map<String, Object> 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<InferRequest> entity = new HttpEntity<>(request, headers);
ResponseEntity<InferResponse> response = restTemplate.postForEntity(url, entity, InferResponse.class);
return response.getBody();
}
}

View File

@ -35,4 +35,13 @@ public interface ProjectService extends IService<Project> {
java.util.List<java.util.Map<String, Object>> parseDeviceOrderWithMaterials(String projectId); java.util.List<java.util.Map<String, Object>> parseDeviceOrderWithMaterials(String projectId);
/**
* 运行项目模拟
* @param projectId 项目ID
* @param scenarioId 情景ID
* @param params 模拟参数
* @return 模拟结果
*/
java.util.Map<String, Object> runSimulation(String projectId, String scenarioId, java.util.Map<String, Object> params);
} }

View File

@ -4,4 +4,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.business.css.domain.Scenario; import com.yfd.business.css.domain.Scenario;
public interface ScenarioService extends IService<Scenario> { public interface ScenarioService extends IService<Scenario> {
//根据场景id获取算法类型
String getAlgorithmType(String scenarioId);
} }

View File

@ -1,5 +1,6 @@
package com.yfd.business.css.service.impl; 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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.business.css.domain.AlgorithmModel; import com.yfd.business.css.domain.AlgorithmModel;
import com.yfd.business.css.mapper.AlgorithmModelMapper; import com.yfd.business.css.mapper.AlgorithmModelMapper;
@ -8,4 +9,14 @@ import org.springframework.stereotype.Service;
@Service @Service
public class AlgorithmModelServiceImpl extends ServiceImpl<AlgorithmModelMapper, AlgorithmModel> implements AlgorithmModelService { public class AlgorithmModelServiceImpl extends ServiceImpl<AlgorithmModelMapper, AlgorithmModel> implements AlgorithmModelService {
@Override
public String getCurrentModelPath(String algorithmType, String deviceType) {
QueryWrapper<AlgorithmModel> 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;
}
} }

View File

@ -8,6 +8,8 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yfd.business.css.domain.Device; import com.yfd.business.css.domain.Device;
import com.yfd.business.css.domain.Material; 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.DeviceService;
import com.yfd.business.css.service.MaterialService; import com.yfd.business.css.service.MaterialService;
import com.yfd.business.css.service.ScenarioService; 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.EventService;
import com.yfd.business.css.service.ScenarioResultService; import com.yfd.business.css.service.ScenarioResultService;
import com.yfd.business.css.dto.TopologyParseResult; import com.yfd.business.css.dto.TopologyParseResult;
@ -31,8 +36,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Objects; import java.util.Objects;
import java.util.Comparator; 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.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
@ -55,6 +59,15 @@ public class ProjectServiceImpl
private EventService eventService; private EventService eventService;
@Resource @Resource
private ScenarioResultService scenarioResultService; private ScenarioResultService scenarioResultService;
@Resource
private DeviceInferService deviceInferService;
// 通过构造函数注入DeviceDataParser
@SuppressWarnings("unused")
private final DeviceDataParser deviceDataParser;
public ProjectServiceImpl(DeviceDataParser deviceDataParser) {
this.deviceDataParser = deviceDataParser;
}
@Override @Override
public byte[] exportAllProjectsExcel() { public byte[] exportAllProjectsExcel() {
@ -1221,4 +1234,59 @@ public class ProjectServiceImpl
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
//运行项目模拟
@Override
public java.util.Map<String, Object> runSimulation(String projectId, String scenarioId, java.util.Map<String, Object> params) {
//1. 校验项目是否存在
Project project = this.getOne(new LambdaQueryWrapper<Project>().eq(Project::getProjectId, projectId));
if (project == null) throw new IllegalArgumentException("项目不存在");
//2. 校验情景是否存在
Scenario scenario = scenarioService.getOne(new LambdaQueryWrapper<Scenario>().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<String, List<DeviceStepInfo>> groupedDevices = DeviceDataParser.parseAndGroupDeviceData(jsonParams);
if (groupedDevices == null || groupedDevices.isEmpty()) {
throw new IllegalArgumentException("解析后的设备数据为空,无法进行模拟");
}
// 输出结果
for (Map.Entry<String, List<DeviceStepInfo>> 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();
}
} }

View File

@ -10,4 +10,12 @@ import org.springframework.stereotype.Service;
public class ScenarioServiceImpl public class ScenarioServiceImpl
extends ServiceImpl<ScenarioMapper, Scenario> extends ServiceImpl<ScenarioMapper, Scenario>
implements ScenarioService { implements ScenarioService {
@Override
public String getAlgorithmType(String scenarioId) {
Scenario scenario = baseMapper.selectById(scenarioId);
if (scenario == null) {
return null;
}
return scenario.getAlgorithmType();
}
} }

View File

@ -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<String, List<DeviceStepInfo>> parseAndGroupDeviceData(String jsonData) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonData);
JsonNode framesNode = rootNode.get("data").get("frames");
List<DeviceStepInfo> 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<Map.Entry<String, JsonNode>> devices = devicesNode.fields();
while (devices.hasNext()) {
Map.Entry<String, JsonNode> deviceEntry = devices.next();
String deviceId = deviceEntry.getKey();
JsonNode deviceInfo = deviceEntry.getValue();
String deviceType = deviceInfo.get("deviceType").asText();
Map<String, Object> properties = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> fields = deviceInfo.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> 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<String, List<DeviceStepInfo>> groupedDevices = parseAndGroupDeviceData(jsonData);
// 输出结果
for (Map.Entry<String, List<DeviceStepInfo>> entry : groupedDevices.entrySet()) {
System.out.println("Device Type: " + entry.getKey());
for (DeviceStepInfo device : entry.getValue()) {
System.out.println(" " + device);
}
System.out.println();
}
}
}

View File

@ -1 +0,0 @@
com.yfd.business.css.config.BusinessCssAutoConfiguration

View File

@ -27,3 +27,6 @@ logging:
level: level:
root: INFO root: INFO
python:
api:
url: http://localhost:8000

View File

@ -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

View File

@ -69,9 +69,7 @@ public class SimulationInitTest {
.andExpect(MockMvcResultMatchers.status().isOk()); .andExpect(MockMvcResultMatchers.status().isOk());
String initBody = "{\"startTime\":0,\"endTime\":18,\"step\":2}"; String initBody = "{\"startTime\":0,\"endTime\":18,\"step\":2}";
String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/simulation/init") String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/" + projectId + "/scenarios/" + scenarioId + "/simulation/init")
.param("projectId", projectId)
.param("scenarioId", scenarioId)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(initBody)) .content(initBody))
.andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.status().isOk())
@ -97,9 +95,7 @@ public class SimulationInitTest {
String projectId = "proj-0001-uuid"; String projectId = "proj-0001-uuid";
String scenarioId = "scen-001-uuid"; String scenarioId = "scen-001-uuid";
String initBody = "{\"startTime\":0,\"endTime\":10,\"step\":2}"; String initBody = "{\"startTime\":0,\"endTime\":10,\"step\":2}";
String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/simulation/init") String initResp = mockMvc.perform(MockMvcRequestBuilders.post("/projects/" + projectId + "/scenarios/" + scenarioId + "/simulation/init")
.param("projectId", projectId)
.param("scenarioId", scenarioId)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(initBody)) .content(initBody))
.andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.status().isOk())
@ -118,35 +114,4 @@ public class SimulationInitTest {
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0)) .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.records").isArray()); .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());
}
} }

View File

@ -4,6 +4,7 @@ import com.yfd.platform.config.bean.LoginProperties;
import com.yfd.platform.exception.AccessDeniedHandExcetion; import com.yfd.platform.exception.AccessDeniedHandExcetion;
import com.yfd.platform.exception.AuthenticationException; import com.yfd.platform.exception.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -19,7 +20,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@Configuration @Configuration
public class SecurityConfig { public class SecurityConfig {
// 1. 注入配置项默认为 false
@Value("${security.dev.permit:false}")
private boolean devPermit;
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
@ -50,31 +54,36 @@ public class SecurityConfig {
http http
.csrf(csrf -> csrf.disable()) .csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> {
.requestMatchers("/user/login").anonymous() // 如果配置为 true则允许所有请求
.requestMatchers("/user/code").permitAll() if (devPermit) {
.requestMatchers(HttpMethod.GET, auth.anyRequest().permitAll();
"/*.html", } else {
"/webSocket/**", auth.requestMatchers("/user/login").anonymous()
"/assets/**", .requestMatchers("/user/code").permitAll()
"/icon/**").permitAll() .requestMatchers(HttpMethod.GET,
.requestMatchers( "/*.html",
"/swagger-ui.html", "/webSocket/**",
"/swagger-ui/**", "/assets/**",
"/v3/api-docs/**", "/icon/**").permitAll()
"/v3/api-docs.yaml", .requestMatchers(
"/swagger-resources/**", "/swagger-ui.html",
"/webjars/**", "/swagger-ui/**",
"/*/api-docs").permitAll() "/v3/api-docs/**",
.requestMatchers( "/v3/api-docs.yaml",
"/report/**", "/swagger-resources/**",
"/images/**", "/webjars/**",
"/pageimage/**", "/*/api-docs").permitAll()
"/avatar/**", .requestMatchers(
"/systemurl/**", "/report/**",
"/api/imageserver/upload").permitAll() "/images/**",
.anyRequest().authenticated() "/pageimage/**",
) "/avatar/**",
"/systemurl/**",
"/api/imageserver/upload").permitAll()
.anyRequest().authenticated();
}
})
.cors(cors -> {}); .cors(cors -> {});
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

View File

@ -31,3 +31,4 @@ springdoc:
swagger-ui: swagger-ui:
enabled: true enabled: true
path: /swagger-ui.html path: /swagger-ui.html

335
logs/business-css.log Normal file
View File

@ -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.

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<mirrors>
<mirror>
<id>aliyunmaven</id>
<name>Aliyun Maven</name>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>aliyun</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>aliyun</activeProfile>
</activeProfiles>
</settings>

1
python-ml/__init__.py Normal file
View File

@ -0,0 +1 @@

72
python-ml/app.py Normal file
View File

@ -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/<device_type>")
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/<device_type>/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/<device_type>/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)

149
python-ml/service.py Normal file
View File

@ -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()

90
python-ml/type_config.py Normal file
View File

@ -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

52
python_ml/type_config.py Normal file
View File

@ -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

View File

@ -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 %*