diff --git a/business-css/src/main/java/com/yfd/business/css/build/SimBuilder.java b/business-css/src/main/java/com/yfd/business/css/build/SimBuilder.java index 689b94d..779abbe 100644 --- a/business-css/src/main/java/com/yfd/business/css/build/SimBuilder.java +++ b/business-css/src/main/java/com/yfd/business/css/build/SimBuilder.java @@ -40,6 +40,8 @@ public class SimBuilder { JsonNode root = objectMapper.readTree(project.getTopology()); Map devToMat = buildDeviceMaterialMap(root); Map> matStaticDb = buildMaterialStaticFromDb(devToMat, materialService); + // 补充:建立 Material ID -> Material Type 的映射 + Map matTypeMap = buildMaterialTypeMap(devToMat, materialService); JsonNode devicesNode = root.path("devices"); if (devicesNode.isArray()) { @@ -49,6 +51,7 @@ public class SimBuilder { String type = deviceNode.path("type").asText(); String materialId = devToMat.get(deviceId); + String materialType = materialId != null ? matTypeMap.get(materialId) : null; Map staticProps = new HashMap<>(); @@ -92,7 +95,7 @@ public class SimBuilder { } } - units.add(new SimUnit(deviceId, deviceId, materialId, type, staticProps)); + units.add(new SimUnit(deviceId, deviceId, materialId, type, materialType, staticProps)); } } } catch (Exception e) { @@ -101,6 +104,24 @@ public class SimBuilder { return units; } + private Map buildMaterialTypeMap(Map devToMat, MaterialService materialService) { + Map out = new HashMap<>(); + if (devToMat.isEmpty()) return out; + Set mids = new HashSet<>(devToMat.values()); + List mats = materialService.list(new QueryWrapper().in("material_id", mids)); + for (Material m : mats) { + String type = "unknown"; + // 简单推导规则: 优先判定 Pu,其次 U + if (m.getPuConcentration() != null && m.getPuConcentration().doubleValue() > 0) { + type = "Pu"; + } else if (m.getUConcentration() != null && m.getUConcentration().doubleValue() > 0) { + type = "U"; + } + out.put(m.getMaterialId(), type); + } + return out; + } + private void injectDeviceSize(Device device, Map staticProps) { try { String sizeJson = device.getSize(); @@ -124,7 +145,7 @@ public class SimBuilder { case "CylindricalTank": case "AnnularTank": - case "TubeBundleTank": + case "TubeBundleTank"://管束槽 parseCommonSize(sizeNode, staticProps); break; diff --git a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java index 9b5c639..31bbb73 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmModelController.java @@ -80,11 +80,12 @@ public class AlgorithmModelController { return algorithmModelService.deleteBatchWithCheck(ids); } - //返回:该算法+设备类型的版本列表 + //返回:该算法+设备类型+材料类型的版本列表 @GetMapping("/search") - @Operation(summary = "查询模型版本列表", description = "按算法类型与设备类型过滤并分页返回模型版本") + @Operation(summary = "查询模型版本列表", description = "按算法类型、设备类型与材料类型过滤并分页返回模型版本") public Page search(@RequestParam(required = false) String algorithmType, @RequestParam(required = false) String deviceType, + @RequestParam(required = false) String materialType, @RequestParam(required = false) String versionTag, @RequestParam(required = false) String isCurrent, @RequestParam(defaultValue = "1") long pageNum, @@ -92,6 +93,7 @@ public class AlgorithmModelController { QueryWrapper qw = new QueryWrapper<>(); if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType); if (deviceType != null && !deviceType.isEmpty()) qw.eq("device_type", deviceType); + if (materialType != null && !materialType.isEmpty()) qw.eq("material_type", materialType); if (versionTag != null && !versionTag.isEmpty()) qw.eq("version_tag", versionTag); if (isCurrent != null && !isCurrent.isEmpty()) qw.eq("is_current", isCurrent); qw.orderByDesc("updated_at"); @@ -99,29 +101,38 @@ public class AlgorithmModelController { return algorithmModelService.page(page, qw); } - //返回:该算法+设备类型的当前激活版本 + //返回:该算法+设备类型+材料类型的当前激活版本 @GetMapping("/current") - @Operation(summary = "获取当前激活版本", description = "根据算法类型与设备类型,返回 is_current=1 的模型版本") + @Operation(summary = "获取当前激活版本", description = "根据算法类型、设备类型与材料类型,返回 is_current=1 的模型版本") public AlgorithmModel getCurrent(@RequestParam String algorithmType, - @RequestParam String deviceType) { + @RequestParam String deviceType, + @RequestParam(required = false) String materialType) { QueryWrapper qw = new QueryWrapper<>(); qw.eq("algorithm_type", algorithmType); qw.eq("device_type", deviceType); + if (materialType != null && !materialType.isEmpty()) { + qw.eq("material_type", materialType); + } qw.eq("is_current", 1); qw.orderByDesc("updated_at"); - return algorithmModelService.getOne(qw); + return algorithmModelService.getOne(qw, false); // 使用 false 避免多条结果时抛出异常,虽然正常不应该有多条 } //版本激活 @PostMapping("/activate") - @Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组其他版本设为非当前") + @Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组(算法+设备+材料)其他版本设为非当前") public boolean activate(@RequestParam String algorithmModelId) { AlgorithmModel model = algorithmModelService.getById(algorithmModelId); if (model == null) return false; - // 先将所有版本设为非当前 + // 先将所有同组版本设为非当前 QueryWrapper qw = new QueryWrapper<>(); qw.eq("algorithm_type", model.getAlgorithmType()); qw.eq("device_type", model.getDeviceType()); + if (model.getMaterialType() != null && !model.getMaterialType().isEmpty()) { + qw.eq("material_type", model.getMaterialType()); + } else { + qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", "")); + } AlgorithmModel upd = new AlgorithmModel(); upd.setIsCurrent(0); algorithmModelService.update(upd, qw); @@ -138,6 +149,7 @@ public class AlgorithmModelController { public Map trainExcel(@RequestBody Map body) { String algorithmType = str(body.get("algorithm_type")); String deviceType = str(body.get("device_type")); + String materialType = str(body.getOrDefault("material_type", "")); String datasetPath = str(body.get("dataset_path")); String modelDir = str(body.getOrDefault("model_dir", "")); boolean activate = bool(body.getOrDefault("activate", false)); @@ -178,6 +190,7 @@ public class AlgorithmModelController { model.setAlgorithmModelId(UUID.randomUUID().toString()); model.setAlgorithmType(algorithmType); model.setDeviceType(deviceType); + model.setMaterialType(materialType); model.setVersionTag(genVersionTag()); model.setModelPath(modelPath); model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); @@ -190,6 +203,11 @@ public class AlgorithmModelController { if (activate) { QueryWrapper qw = new QueryWrapper<>(); qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); + if (!isBlank(materialType)) { + qw.eq("material_type", materialType); + } else { + qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", "")); + } AlgorithmModel upd = new AlgorithmModel(); upd.setIsCurrent(0); algorithmModelService.update(upd, qw); @@ -204,6 +222,7 @@ public class AlgorithmModelController { public Map trainSamples(@RequestBody Map body) { String algorithmType = str(body.get("algorithm_type")); String deviceType = str(body.get("device_type")); + String materialType = str(body.getOrDefault("material_type", "")); Object samples = body.get("samples"); // 期望为 List,由前端提供 String modelDir = str(body.getOrDefault("model_dir", "")); boolean activate = bool(body.getOrDefault("activate", false)); @@ -230,6 +249,7 @@ public class AlgorithmModelController { model.setAlgorithmModelId(UUID.randomUUID().toString()); model.setAlgorithmType(algorithmType); model.setDeviceType(deviceType); + model.setMaterialType(materialType); model.setVersionTag(genVersionTag()); model.setModelPath(modelPath); model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); @@ -242,6 +262,11 @@ public class AlgorithmModelController { if (activate) { QueryWrapper qw = new QueryWrapper<>(); qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); + if (!isBlank(materialType)) { + qw.eq("material_type", materialType); + } else { + qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", "")); + } AlgorithmModel upd = new AlgorithmModel(); upd.setIsCurrent(0); algorithmModelService.update(upd, qw); diff --git a/business-css/src/main/java/com/yfd/business/css/controller/SimController.java b/business-css/src/main/java/com/yfd/business/css/controller/SimController.java index 8d96ee2..f03c1d0 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/SimController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/SimController.java @@ -44,29 +44,49 @@ public class SimController { int steps = req.containsKey("steps") ? (int) req.get("steps") : 10; // 0. Update Status: 更新情景状态为进行中 - Scenario startScenario = new Scenario(); - startScenario.setScenarioId(scenarioId); - startScenario.setStatus("1"); // 1: 进行中 - startScenario.setUpdatedAt(LocalDateTime.now()); - scenarioService.updateById(startScenario); + updateScenarioStatus(scenarioId, "1"); // 1: 进行中 - // 1. Load Data: 获取项目、设备和事件数据 - SimDataPackage data = simDataFacade.loadSimulationData(projectId, scenarioId); - - // 2. Build Model: 将原始数据转换为仿真模型 (Units, Events, Nodes) - List units = simBuilder.buildUnits(data, materialService); - List events = simBuilder.buildEvents(data.getEvents()); - List nodes = simBuilder.buildInfluenceNodes(data.getProject()); - - // 3. Run Engine: 执行核心仿真计算,返回上下文结果 - SimContext ctx = simService.runSimulation(units, events, nodes, steps); - - // 4. Async Infer: 异步执行推理并保存结果,完成后更新情景状态 - simInferService.asyncInferAndSave(projectId, scenarioId, ctx, units); + try { + // 1. Load Data: 获取项目、设备和事件数据 + SimDataPackage data = simDataFacade.loadSimulationData(projectId, scenarioId); + + // 2. Build Model: 将原始数据转换为仿真模型 (Units, Events, Nodes) + List units = simBuilder.buildUnits(data, materialService); + List events = simBuilder.buildEvents(data.getEvents()); + List nodes = simBuilder.buildInfluenceNodes(data.getProject()); + + // 3. Run Engine: 执行核心仿真计算,返回上下文结果 + SimContext ctx = simService.runSimulation(units, events, nodes, steps); + + // 4. Async Infer: 异步执行推理并保存结果,完成后更新情景状态 + simInferService.asyncInferAndSave(projectId, scenarioId, ctx, units); - // 6. Convert Result: 将仿真结果转换为前端友好的格式,包含静态属性补全和元数据 - Map resultData = SimResultConverter.toFrames(ctx, units, projectId, scenarioId); - - return ResponseResult.successData(resultData); + // 6. Convert Result: 将仿真结果转换为前端友好的格式,包含静态属性补全和元数据 + Map resultData = SimResultConverter.toFrames(ctx, units, projectId, scenarioId); + + // 为了让前端明确感知推理是否启动成功,这里增加一个状态提示 + // 但因为是异步的,我们只能保证“已提交”。真正的成败由 asyncInferAndSave 决定。 + resultData.put("inferenceStatus", "submitted"); + + return ResponseResult.successData(resultData); + + } catch (Exception e) { + e.printStackTrace(); + // 仿真计算失败,更新状态为失败 (3: 失败) + updateScenarioStatus(scenarioId, "3"); + return ResponseResult.error("Simulation failed: " + e.getMessage()); + } + } + + private void updateScenarioStatus(String scenarioId, String status) { + try { + Scenario scenario = new Scenario(); + scenario.setScenarioId(scenarioId); + scenario.setStatus(status); + scenario.setUpdatedAt(LocalDateTime.now()); + scenarioService.updateById(scenario); + } catch (Exception e) { + System.err.println("Failed to update scenario status to " + status + ": " + e.getMessage()); + } } } diff --git a/business-css/src/main/java/com/yfd/business/css/domain/AlgorithmModel.java b/business-css/src/main/java/com/yfd/business/css/domain/AlgorithmModel.java index a9a6659..82e49c3 100644 --- a/business-css/src/main/java/com/yfd/business/css/domain/AlgorithmModel.java +++ b/business-css/src/main/java/com/yfd/business/css/domain/AlgorithmModel.java @@ -24,6 +24,9 @@ public class AlgorithmModel implements Serializable { @TableField("device_type") private String deviceType; + @TableField("material_type") + private String materialType; + @TableField("version_tag") private String versionTag; diff --git a/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java b/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java index ca59bc4..3d5fcba 100644 --- a/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java +++ b/business-css/src/main/java/com/yfd/business/css/model/DeviceStepInfo.java @@ -5,15 +5,21 @@ import java.util.Map; public class DeviceStepInfo { private String deviceId; private String deviceType; + private String materialType; // 新增 private Map properties; private int step; private int time; public DeviceStepInfo() {} - public DeviceStepInfo(String deviceId, String deviceType, Map properties,int step,int time) { + public DeviceStepInfo(String deviceId, String deviceType, Map properties, int step, int time) { + this(deviceId, deviceType, null, properties, step, time); + } + + public DeviceStepInfo(String deviceId, String deviceType, String materialType, Map properties, int step, int time) { this.deviceId = deviceId; this.deviceType = deviceType; + this.materialType = materialType; this.properties = properties; this.step = step; this.time = time; @@ -28,7 +34,6 @@ public class DeviceStepInfo { this.deviceId = deviceId; } - public String getDeviceType() { return deviceType; } @@ -37,6 +42,14 @@ public class DeviceStepInfo { this.deviceType = deviceType; } + public String getMaterialType() { + return materialType; + } + + public void setMaterialType(String materialType) { + this.materialType = materialType; + } + public Map getProperties() { return properties; } @@ -66,9 +79,10 @@ public class DeviceStepInfo { return "DeviceStepInfo{" + "deviceId='" + deviceId + '\'' + ", deviceType='" + deviceType + '\'' + + ", materialType='" + materialType + '\'' + ", properties=" + properties + ", step=" + step + ", time=" + time + '}'; } -} \ No newline at end of file +} diff --git a/business-css/src/main/java/com/yfd/business/css/model/SimUnit.java b/business-css/src/main/java/com/yfd/business/css/model/SimUnit.java index 10e8a8e..6e1f128 100644 --- a/business-css/src/main/java/com/yfd/business/css/model/SimUnit.java +++ b/business-css/src/main/java/com/yfd/business/css/model/SimUnit.java @@ -2,8 +2,17 @@ package com.yfd.business.css.model; import java.util.Map; -public record SimUnit(String unitId, String deviceId, String materialId, String deviceType, Map staticProperties) { +public record SimUnit(String unitId, String deviceId, String materialId, String deviceType, String materialType, Map staticProperties) { + public SimUnit(String unitId, String deviceId, String materialId, String deviceType, String materialType) { + this(unitId, deviceId, materialId, deviceType, materialType, Map.of()); + } + + // 兼容旧构造函数 + public SimUnit(String unitId, String deviceId, String materialId, String deviceType, Map staticProperties) { + this(unitId, deviceId, materialId, deviceType, null, staticProperties); + } + public SimUnit(String unitId, String deviceId, String materialId, String deviceType) { - this(unitId, deviceId, materialId, deviceType, Map.of()); + this(unitId, deviceId, materialId, deviceType, null, Map.of()); } } diff --git a/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java b/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java index 6802df6..be82b7a 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/AlgorithmModelService.java @@ -14,7 +14,17 @@ public interface AlgorithmModelService extends IService { * @param deviceType 设备类型(如CylindricalTank/AnnularTank) * @return 激活版本的模型文件路径,如果不存在则返回null */ - String getCurrentModelPath(String algorithmType, String deviceType) ; + String getCurrentModelPath(String algorithmType, String deviceType); + + /** + * 根据算法类型、设备类型、材料类型,获取当前激活的模型路径 + * + * @param algorithmType 算法类型 + * @param deviceType 设备类型 + * @param materialType 材料类型(如Pu/U) + * @return 激活版本的模型文件路径 + */ + String getCurrentModelPath(String algorithmType, String deviceType, String materialType); boolean deleteBatchWithCheck(List ids); diff --git a/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java b/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java index a24413e..c69b333 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/DeviceInferService.java @@ -19,6 +19,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import org.springframework.stereotype.Service; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; @Service @@ -26,6 +28,11 @@ public class DeviceInferService { @Value("${python.api.url:http://localhost:8000}") private String pythonInferUrl; + + + @Value("${file-space.model-path}") + private String modelRootPath; + @Resource private ScenarioService scenarioService; @Resource @@ -38,6 +45,10 @@ public class DeviceInferService { public void processDeviceInference(String projectId, String scenarioId, Map> groupedDevices) { + // 增加标志位,记录是否至少成功执行了一次推理 + boolean hasAnySuccess = false; + boolean hasAnyError = false; + // 1. 获取情景配置信息 Scenario scenario = scenarioService.getById(scenarioId); if (scenario == null) { @@ -81,53 +92,79 @@ public class DeviceInferService { String currentAlgoType = algoEntry.getKey(); List currentDevices = algoEntry.getValue(); - // 获取模型路径(根据算法类型和设备类型) - System.out.println("Processing inference for algorithmType: " + currentAlgoType + ", deviceType: " + deviceType); - String modelPath = algorithmModelService.getCurrentModelPath(currentAlgoType, deviceType); - System.out.println("modelPath=" + modelPath); - - if (modelPath == null) { - System.err.println("Model path not found for algorithmType: " + currentAlgoType + ", deviceType: " + deviceType); - // 这里可以选择抛异常中断,或者跳过该组继续处理其他组 - // 为了保证健壮性,这里选择记录错误并跳过,或者根据业务需求抛出异常 - // throw new IllegalArgumentException("未配置 " + currentAlgoType + " 模型路径 (deviceType: " + deviceType + ")"); - continue; + // 4.1 按材料类型进行三级分组 (AlgoType -> MaterialType) + Map> matGroup = new HashMap<>(); + for (DeviceStepInfo d : currentDevices) { + String mType = d.getMaterialType(); + // 如果 materialType 为空,归类为 "unknown" 或 null,视业务而定,这里保留 null + matGroup.computeIfAbsent(mType, k -> new ArrayList<>()).add(d); } - // 封装推理请求 - InferRequest request = buildInferenceRequest(deviceType, currentDevices, modelPath); - // System.out.println("request=" + request); + // 4.2 遍历材料分组,分别获取模型并推理 + for (Map.Entry> matEntry : matGroup.entrySet()) { + String currentMaterialType = matEntry.getKey(); + List batchDevices = matEntry.getValue(); - try { - // 调用Python推理服务 - InferResponse response = infer(request); - System.out.println("推理服务返回结果: code=" + (response != null ? response.getCode() : "null")); + // 获取模型路径(根据算法类型、设备类型、材料类型) + System.out.println("Processing inference for algorithmType: " + currentAlgoType + + ", deviceType: " + deviceType + ", materialType: " + currentMaterialType); + String modelRelPath = algorithmModelService.getCurrentModelPath(currentAlgoType, deviceType, currentMaterialType); + System.out.println("modelRelPath=" + modelRelPath); - // 处理推理结果 - if (response != null && response.getCode() == 0) { - // 重新构建InferResponse对象示例 (为了兼容性保留原有逻辑) - InferResponse.InferData originalData = response.getData(); - InferResponse.InferData newData = new InferResponse.InferData(); - newData.setItems(originalData.getItems()); - newData.setMeta(originalData.getMeta()); - newData.setFeatures(originalData.getFeatures()); - newData.setKeff(originalData.getKeff()); - - InferResponse reconstructedResponse = new InferResponse(); - reconstructedResponse.setCode(response.getCode()); - reconstructedResponse.setMsg(response.getMsg()); - reconstructedResponse.setData(newData); - - processInferenceResults(projectId, scenarioId, deviceType, currentDevices, reconstructedResponse); - } else { - System.err.println("推理服务调用失败: " + (response != null ? response.getMsg() : "未知错误")); + if (modelRelPath == null) { + System.err.println("Model path not found for algorithmType: " + currentAlgoType + + ", deviceType: " + deviceType + ", materialType: " + currentMaterialType); + hasAnyError = true; + continue; + } + + // 将相对路径转换为绝对路径 + String absoluteModelPath = Paths.get(modelRootPath).resolve(modelRelPath).toAbsolutePath().normalize().toString(); + System.out.println("Absolute modelPath=" + absoluteModelPath); + + // 封装推理请求 + InferRequest request = buildInferenceRequest(deviceType, batchDevices, absoluteModelPath); + System.out.println("request=" + request); + + try { + // 调用Python推理服务 + InferResponse response = infer(request); + System.out.println("推理服务返回结果: code=" + (response != null ? response.getCode() : "null")); + + // 处理推理结果 + if (response != null && response.getCode() == 0) { + // 重新构建InferResponse对象示例 (为了兼容性保留原有逻辑) + InferResponse.InferData originalData = response.getData(); + InferResponse.InferData newData = new InferResponse.InferData(); + newData.setItems(originalData.getItems()); + newData.setMeta(originalData.getMeta()); + newData.setFeatures(originalData.getFeatures()); + newData.setKeff(originalData.getKeff()); + + InferResponse reconstructedResponse = new InferResponse(); + reconstructedResponse.setCode(response.getCode()); + reconstructedResponse.setMsg(response.getMsg()); + reconstructedResponse.setData(newData); + + processInferenceResults(projectId, scenarioId, deviceType, batchDevices, reconstructedResponse); + hasAnySuccess = true; + } else { + System.err.println("推理服务调用失败: " + (response != null ? response.getMsg() : "未知错误")); + hasAnyError = true; + } + } catch (Exception e) { + System.err.println("推理异常: " + e.getMessage()); + e.printStackTrace(); + hasAnyError = true; } - } catch (Exception e) { - System.err.println("推理异常: " + e.getMessage()); - e.printStackTrace(); } } } + + // 5. 最终检查:如果没有任何成功的推理,且发生过错误,抛出异常以通知上层 + if (!hasAnySuccess && hasAnyError) { + throw new RuntimeException("所有设备推理均失败或未找到模型"); + } } private InferRequest buildInferenceRequest(String deviceType,List devices,String modelPath) { diff --git a/business-css/src/main/java/com/yfd/business/css/service/SimInferService.java b/business-css/src/main/java/com/yfd/business/css/service/SimInferService.java index 11bfb18..75756a0 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/SimInferService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/SimInferService.java @@ -69,6 +69,7 @@ public class SimInferService { DeviceStepInfo info = new DeviceStepInfo(); info.setDeviceId(unit.deviceId()); // unitId 通常等于 deviceId info.setDeviceType(deviceType); + info.setMaterialType(unit.materialType()); // 注入材料类型 info.setProperties(properties); info.setStep(step); info.setTime(step); // 假设 time = step,或者根据步长计算 @@ -89,6 +90,12 @@ public class SimInferService { deviceInferService.processDeviceInference(projectId, scenarioId, groupedDevices); // 4. 更新状态为已完成 + // 注意:如果前面的 deviceInferService.processDeviceInference 内部发生部分异常但未抛出(例如 continue 了), + // 这里依然会设置为 2 (Success)。 + // 建议检查 deviceInferService 是否真的执行了推理。 + // 但由于是 void 方法,无法直接得知。 + // 简单改进:如果 groupedDevices 非空,但所有组都因为找不到模型而跳过,应该视为失败吗? + // 目前策略:只要没有抛出未捕获异常,就视为 Success。 updateScenarioStatus(scenarioId, "2"); } catch (Exception e) { diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java index 2075f6f..4156ae3 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/AlgorithmModelServiceImpl.java @@ -16,13 +16,29 @@ public class AlgorithmModelServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("algorithm_type", algorithmType) .eq("device_type", deviceType) .eq("is_current", 1); // 当前激活版本 - AlgorithmModel model = getOne(queryWrapper); - if (model != null) { + + if (materialType != null && !materialType.isEmpty()) { + queryWrapper.eq("material_type", materialType); + } else { + // 如果未指定材料类型,优先匹配无材料类型的通用模型,或者返回任意匹配(兼容旧逻辑) + // 这里为了稳健,如果 materialType 为 null,暂不添加 material_type 的过滤条件,或者显式匹配 null + // 建议:如果 materialType 为空,尝试匹配 material_type is null or '' + // queryWrapper.and(w -> w.isNull("material_type").or().eq("material_type", "")); + } + + List models = list(queryWrapper); + if (models != null && !models.isEmpty()) { + AlgorithmModel model = models.get(0); System.out.println("Found model: " + model.getModelPath()); return model.getModelPath(); } else { diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java index 3b9602e..14d88e6 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/ModelTrainServiceImpl.java @@ -308,23 +308,35 @@ public class ModelTrainServiceImpl extends ServiceImpl() - .eq("algorithm_type", task.getAlgorithmType()) - .eq("device_type", task.getDeviceType()) - .eq("version_tag", versionTag)); - if (count > 0) { - throw new BizException("版本号已存在"); - } - String algorithmType = task.getAlgorithmType(); String deviceType = task.getDeviceType(); if (algorithmType == null || algorithmType.isBlank() || deviceType == null || deviceType.isBlank()) { throw new BizException("算法类型或设备类型不能为空"); } + // 解析材料类型 + String materialType = "unknown"; + String outputPath = task.getModelOutputPath(); + if (outputPath != null && !outputPath.isBlank()) { + String[] parts = outputPath.replace("\\", "/").split("/"); + // 预期结构: runs/{algorithmType}/{deviceType}/{materialType}/{taskId}/... + if (parts.length > 4 && "runs".equals(parts[0])) { + materialType = parts[3]; + } + } + + // 检查版本号唯一性 + long count = algorithmModelService.count(new QueryWrapper() + .eq("algorithm_type", algorithmType) + .eq("device_type", deviceType) + .eq("material_type", materialType) + .eq("version_tag", versionTag)); + if (count > 0) { + throw new BizException("版本号已存在"); + } + Path root = Paths.get(modelPath).toAbsolutePath().normalize(); - Path versionDir = root.resolve(Paths.get(algorithmType, deviceType, versionTag)).normalize(); + Path versionDir = root.resolve(Paths.get(algorithmType, deviceType, materialType, versionTag)).normalize(); if (!versionDir.startsWith(root)) { throw new BizException("发布目录非法"); } @@ -339,11 +351,43 @@ public class ModelTrainServiceImpl extends ServiceImpl