From 2ee8b22ce2c5ee03cf952a19d1a7a65847ec9286 Mon Sep 17 00:00:00 2001 From: wanxiaoli Date: Wed, 5 Aug 2026 11:51:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=AD=E7=BB=83=E4=BB=BB=E5=8A=A1=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E9=99=90=E5=88=B6=EF=BC=88training=3D2,pending=3D5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../css/controller/ModelTrainController.java | 6 + .../css/service/ModelTrainService.java | 2 + .../css/service/impl/DeviceServiceImpl.java | 136 ++++++++++++++++-- .../service/impl/ModelTrainServiceImpl.java | 53 ++++++- .../impl/SysOrganizationServiceImpl.java | 13 +- 5 files changed, 195 insertions(+), 15 deletions(-) diff --git a/business-css/src/main/java/com/yfd/business/css/controller/ModelTrainController.java b/business-css/src/main/java/com/yfd/business/css/controller/ModelTrainController.java index f56f297..32b2d83 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/ModelTrainController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/ModelTrainController.java @@ -29,6 +29,12 @@ public class ModelTrainController { @Autowired private ObjectMapper objectMapper; + @PreAuthorize("hasAuthority('modelTrain:add')") + @GetMapping("/capacity/check") + public ResponseResult capacityCheck() { + return ResponseResult.successData(modelTrainService.checkCapacity()); + } + /** * 接收 Python 端的训练状态回调 */ diff --git a/business-css/src/main/java/com/yfd/business/css/service/ModelTrainService.java b/business-css/src/main/java/com/yfd/business/css/service/ModelTrainService.java index 131db13..36a045d 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/ModelTrainService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/ModelTrainService.java @@ -47,4 +47,6 @@ public interface ModelTrainService extends IService { * @return 是否成功 */ boolean publishModel(String taskId, String versionTag); + + Map checkCapacity(); } diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java index 3cb930d..039b513 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java @@ -283,6 +283,7 @@ public class DeviceServiceImpl continue; } + List sizeCols = getSizeColumnsByDeviceType(rowType); String sizeJson = null; if (hasSizeJson) { String raw = cleanString(getString(row, idx.get("size"))); @@ -292,10 +293,12 @@ public class DeviceServiceImpl errors.add(err(r, "size 非法JSON")); continue; } + if (!validateSizeJsonPositive(sizeJson, sizeCols, errors, r)) { + continue; + } } } if (sizeJson == null) { - List sizeCols = getSizeColumnsByDeviceType(rowType); if (sizeCols.isEmpty()) { errors.add(err(r, "未知设备类型: " + rowType)); continue; @@ -303,28 +306,46 @@ public class DeviceServiceImpl Map m = new LinkedHashMap<>(); for (String k : sizeCols) { Integer i = idx.get(k); - if (i == null) continue; - Double v = getDoubleFlexible(row, i, evaluator, formatter); - if (v != null) { - m.put(k, v); + if (i == null) { + errors.add(err(r, "缺少尺寸列: " + k)); + m.clear(); + break; } + Double v = getDoubleFlexible(row, i, evaluator, formatter); + if (v == null || v <= 0) { + errors.add(err(r, "尺寸 " + k + " 必须大于0")); + m.clear(); + break; + } + m.put(k, v); } if (m.isEmpty()) { - errors.add(err(r, "尺寸列为空")); continue; } sizeJson = objectMapper.writeValueAsString(m); } + int errSizeBefore = errors.size(); + Double volume = readPositiveOptional(row, idx.get("volume"), + evaluator, formatter, errors, r, "容量"); + Double flowRate = readPositiveOptional(row, idx.get("flow_rate"), + evaluator, formatter, errors, r, "流量"); + Double pulseVelocity = + readPositiveOptional(row, idx.get("pulse_velocity"), + evaluator, formatter, errors, r, "脉冲速度"); + if (errors.size() > errSizeBefore) { + continue; + } + Device d = new Device(); d.setType(rowType); d.setProjectId(projectId == null || projectId.isBlank() ? "-1" : projectId); d.setCode(code); d.setName(name); d.setSize(sizeJson); - if (idx.containsKey("volume")) d.setVolume(getDoubleFlexible(row, idx.get("volume"), evaluator, formatter)); - if (idx.containsKey("flow_rate")) d.setFlowRate(getDoubleFlexible(row, idx.get("flow_rate"), evaluator, formatter)); - if (idx.containsKey("pulse_velocity")) d.setPulseVelocity(getDoubleFlexible(row, idx.get("pulse_velocity"), evaluator, formatter)); + d.setVolume(volume); + d.setFlowRate(flowRate); + d.setPulseVelocity(pulseVelocity); d.setCreatedAt(LocalDateTime.now()); d.setUpdatedAt(LocalDateTime.now()); d.setModifier(currentUsername()); @@ -359,6 +380,103 @@ public class DeviceServiceImpl } } + private Double readPositiveOptional(Row row, Integer i, + FormulaEvaluator evaluator, + DataFormatter formatter, + List> errors, int r, + String label) { + if (i == null) return null; + Cell c = row.getCell(i); + if (c == null) return null; + String s = formatter.formatCellValue(c, evaluator); + if (s == null) return null; + String t = s.trim(); + if (t.isEmpty()) return null; + try { + double v = Double.parseDouble(t); + if (v <= 0) { + errors.add(err(r, label + " 必须大于0")); + return null; + } + return v; + } catch (Exception e) { + errors.add(err(r, label + " 必须为数字")); + return null; + } + } + + private boolean validateSizeJsonPositive(String sizeJson, + List schemaKeys, + List> errors, + int r) { + try { + JsonNode node = objectMapper.readTree(sizeJson); + if (node == null || !node.isObject()) { + errors.add(err(r, "size 必须为JSON对象")); + return false; + } + if (schemaKeys != null && !schemaKeys.isEmpty()) { + for (String k : schemaKeys) { + JsonNode v = node.get(k); + if (v == null || v.isNull()) { + errors.add(err(r, "尺寸 " + k + " 不能为空")); + return false; + } + Double dv = parseJsonDouble(v); + if (dv == null) { + errors.add(err(r, "尺寸 " + k + " 必须为数字")); + return false; + } + if (dv <= 0) { + errors.add(err(r, "尺寸 " + k + " 必须大于0")); + return false; + } + } + } + var it = node.fields(); + while (it.hasNext()) { + var e = it.next(); + JsonNode v = e.getValue(); + Double dv = parseJsonDouble(v); + if (dv == null) { + if (v != null && !v.isNull() && v.isTextual()) { + String t = v.asText(); + if (t != null && !t.trim().isEmpty()) { + errors.add(err(r, "尺寸 " + e.getKey() + " 必须为数字")); + return false; + } + } + continue; + } + if (dv <= 0) { + errors.add(err(r, "尺寸 " + e.getKey() + " 必须大于0")); + return false; + } + } + return true; + } catch (Exception e) { + errors.add(err(r, "size 解析失败")); + return false; + } + } + + private Double parseJsonDouble(JsonNode v) { + if (v == null || v.isNull()) return null; + if (v.isNumber()) return v.numberValue().doubleValue(); + if (v.isTextual()) { + String t = v.asText(); + if (t == null) return null; + String s = t.trim(); + if (s.isEmpty()) return null; + try { + return Double.parseDouble(s); + } catch (Exception e) { + return null; + } + } + return null; + } + private String cleanString(String s) { if (s == null) return null; String t = s.trim(); 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 e738174..fc89a60 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 @@ -76,6 +76,8 @@ public class ModelTrainServiceImpl extends ServiceImpl checkCapacity() { + long training = countByStatus(List.of("Training", "TRAINING")); + long pending = countByStatus(List.of("Pending", "PENDING")); + + boolean canSubmit = !(training >= MAX_TRAINING && pending >= MAX_PENDING); + + Map data = new HashMap<>(); + data.put("canSubmit", canSubmit); + data.put("limits", Map.of( + "maxTraining", MAX_TRAINING, + "maxPending", MAX_PENDING + )); + data.put("counts", Map.of( + "training", training, + "pending", pending + )); + + if (!canSubmit) { + data.put("blockReason", buildCapacityBlockReason(training, pending)); + data.put("suggest", Map.of("retryAfterSeconds", 30)); + } + return data; + } + + private void assertCapacityForSubmit() { + long training = countByStatus(List.of("Training", "TRAINING")); + long pending = countByStatus(List.of("Pending", "PENDING")); + if (training >= MAX_TRAINING && pending >= MAX_PENDING) { + throw new BizException(buildCapacityBlockReason(training, pending)); + } + } + + private long countByStatus(List statuses) { + QueryWrapper q = new QueryWrapper<>(); + q.in("status", statuses); + return this.count(q); + } + + private String buildCapacityBlockReason(long training, long pending) { + return "当前训练中(" + training + ")且等待中(" + pending + ")已达上限,请稍后再试"; + } @Async public void asyncCallTrain(ModelTrainTask task) { try { // 更新状态为 Training - task.setStatus("Training"); - this.updateById(task); + // task.setStatus("Training"); + // this.updateById(task); // 构建请求参数 Map request = new HashMap<>(); @@ -557,7 +603,8 @@ public class ModelTrainServiceImpl extends ServiceImpl max = this.listObjs(queryWrapper); //判断查询是否存在 存在转换成int类型并给codeMax替换值 - if (max.size() > 0) { - codeMax = - Integer.parseInt(max.get(0).toString().substring(max.get(0).toString().length() - 2)); + if (max.size() > 0 && max.get(0) != null) { + String maxCode = max.get(0).toString(); + if (maxCode.length() >= 2) { + String lastTwo = maxCode.substring(maxCode.length() - 2); + try { + codeMax = Integer.parseInt(lastTwo); + } catch (Exception e) { + codeMax = 0; + } + } } //2位数字编号 DecimalFormat df = new DecimalFormat("00");