From 1080e8e862fcfb9492fd3eed78c34b7ba3abfe32 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期一, 27 四月 2026 11:44:08 +0800
Subject: [PATCH] fix: strengthen auto tune apply transaction safety

---
 src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java |  177 ++++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 114 insertions(+), 63 deletions(-)

diff --git a/src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java b/src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java
index 98a588a..e6a1e20 100644
--- a/src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java
+++ b/src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java
@@ -56,7 +56,7 @@
     private BasDualCrnpService basDualCrnpService;
     @Autowired
     private StationFlowCapacityService stationFlowCapacityService;
-    @Autowired(required = false)
+    @Autowired
     private PlatformTransactionManager transactionManager;
 
     @Override
@@ -132,31 +132,37 @@
 
     private ValidatedChange validateChange(AutoTuneChangeCommand command, boolean dryRun, Date now) {
         ValidatedChange validatedChange = new ValidatedChange(command);
-        AutoTuneRuleDefinition.Rule rule = AutoTuneRuleDefinition.findRule(command.getTargetType(), command.getTargetKey());
+        AutoTuneRuleDefinition.Rule rule = AutoTuneRuleDefinition.findRule(
+                validatedChange.getTargetType(),
+                validatedChange.getTargetKey()
+        );
         if (rule == null) {
-            return validatedChange.reject("涓嶆敮鎸佺殑璋冨弬鐩爣: " + command.getTargetType() + "/" + command.getTargetKey());
+            return validatedChange.reject("涓嶆敮鎸佺殑璋冨弬鐩爣: "
+                    + validatedChange.getTargetType() + "/" + validatedChange.getTargetKey());
         }
         validatedChange.setRule(rule);
 
-        Integer requestedValue = parseRequestedInt(command.getNewValue());
+        Integer requestedValue = parseRequestedInt(validatedChange.getRawRequestedValue());
         if (requestedValue == null) {
-            return validatedChange.reject(command.getTargetKey() + " 蹇呴』涓烘暣鏁�");
+            return validatedChange.reject(validatedChange.getTargetKey() + " 蹇呴』涓烘暣鏁�");
         }
         validatedChange.setRequestedIntValue(requestedValue);
         validatedChange.setRequestedValue(String.valueOf(requestedValue));
 
-        CurrentValue currentValue = readCurrentValueForValidation(command, rule);
+        CurrentValue currentValue = readCurrentValueForValidation(validatedChange, rule);
         if (currentValue.getRejectReason() != null) {
             return validatedChange.reject(currentValue.getRejectReason());
         }
         validatedChange.setOldValue(currentValue.getOldValue());
 
-        Integer maxValue = resolveMaxValue(command, rule, requestedValue);
+        Integer maxValue = resolveMaxValue(validatedChange, rule, requestedValue);
         if (maxValue == null) {
-            return validatedChange.reject("绔欑偣 " + command.getTargetId() + " 缂哄皯 OUT 鏂瑰悜 bufferCapacity锛屾棤娉曡瘉鏄� outTaskLimit 涓婇檺");
+            return validatedChange.reject("绔欑偣 " + validatedChange.getTargetId()
+                    + " 缂哄皯 OUT 鏂瑰悜 bufferCapacity锛屾棤娉曡瘉鏄� outTaskLimit 涓婇檺");
         }
         if (requestedValue < rule.getMinValue() || requestedValue > maxValue) {
-            return validatedChange.reject(command.getTargetKey() + " 蹇呴』鍦� " + rule.getMinValue() + "~" + maxValue + " 鑼冨洿鍐�");
+            return validatedChange.reject(validatedChange.getTargetKey() + " 蹇呴』鍦� "
+                    + rule.getMinValue() + "~" + maxValue + " 鑼冨洿鍐�");
         }
 
         if (Objects.equals(currentValue.getNumericValue(), requestedValue)) {
@@ -164,10 +170,10 @@
         }
         int step = Math.abs(requestedValue - currentValue.getNumericValue());
         if (step > rule.getMaxStep()) {
-            return validatedChange.reject(command.getTargetKey() + " 鍗曟璋冩暣姝ラ暱涓嶈兘瓒呰繃 " + rule.getMaxStep());
+            return validatedChange.reject(validatedChange.getTargetKey() + " 鍗曟璋冩暣姝ラ暱涓嶈兘瓒呰繃 " + rule.getMaxStep());
         }
 
-        Date cooldownExpireTime = findCooldownExpireTime(command, now);
+        Date cooldownExpireTime = findCooldownExpireTime(validatedChange, now);
         if (cooldownExpireTime != null) {
             validatedChange.setCooldownExpireTime(cooldownExpireTime);
             return validatedChange.reject("鐩爣浠嶅湪鍐峰嵈鏈燂紝鍐峰嵈鎴鏃堕棿: " + cooldownExpireTime);
@@ -178,40 +184,40 @@
         return validatedChange.accept(dryRun ? ChangeStatus.DRY_RUN : ChangeStatus.PENDING, null);
     }
 
-    private CurrentValue readCurrentValueForValidation(AutoTuneChangeCommand command, AutoTuneRuleDefinition.Rule rule) {
+    private CurrentValue readCurrentValueForValidation(ValidatedChange validatedChange, AutoTuneRuleDefinition.Rule rule) {
         AutoTuneTargetType targetType = rule.getTargetType();
-        Integer targetId = parseTargetId(command.getTargetId(), targetType);
+        Integer targetId = parseTargetId(validatedChange.getTargetId(), targetType);
         if (!AutoTuneTargetType.SYS_CONFIG.equals(targetType) && targetId == null) {
             return CurrentValue.rejected("targetId 蹇呴』涓烘暣鏁�");
         }
         if (AutoTuneTargetType.SYS_CONFIG.equals(targetType)) {
-            Config config = configService.getOne(new QueryWrapper<Config>().eq("code", command.getTargetKey()).last("limit 1"));
+            Config config = configService.getOne(new QueryWrapper<Config>().eq("code", validatedChange.getTargetKey()).last("limit 1"));
             if (config == null) {
-                return CurrentValue.rejected("杩愯鍙傛暟涓嶅瓨鍦�: " + command.getTargetKey());
+                return CurrentValue.rejected("杩愯鍙傛暟涓嶅瓨鍦�: " + validatedChange.getTargetKey());
             }
-            return numericCurrentValue(config.getValue(), false, command.getTargetKey());
+            return numericCurrentValue(config.getValue(), false, validatedChange.getTargetKey());
         }
         if (AutoTuneTargetType.STATION.equals(targetType)) {
             BasStation station = basStationService.getById(targetId);
             if (station == null) {
-                return CurrentValue.rejected("绔欑偣涓嶅瓨鍦�: " + command.getTargetId());
+                return CurrentValue.rejected("绔欑偣涓嶅瓨鍦�: " + validatedChange.getTargetId());
             }
-            return numericCurrentValue(toText(station.getOutTaskLimit()), true, command.getTargetKey());
+            return numericCurrentValue(toText(station.getOutTaskLimit()), true, validatedChange.getTargetKey());
         }
         if (AutoTuneTargetType.CRN.equals(targetType)) {
             BasCrnp crnp = basCrnpService.getById(targetId);
             if (crnp == null) {
-                return CurrentValue.rejected("鍫嗗灈鏈轰笉瀛樺湪: " + command.getTargetId());
+                return CurrentValue.rejected("鍫嗗灈鏈轰笉瀛樺湪: " + validatedChange.getTargetId());
             }
-            Integer value = "maxOutTask".equals(command.getTargetKey()) ? crnp.getMaxOutTask() : crnp.getMaxInTask();
-            return numericCurrentValue(toText(value), false, command.getTargetKey());
+            Integer value = "maxOutTask".equals(validatedChange.getTargetKey()) ? crnp.getMaxOutTask() : crnp.getMaxInTask();
+            return numericCurrentValue(toText(value), false, validatedChange.getTargetKey());
         }
         BasDualCrnp dualCrnp = basDualCrnpService.getById(targetId);
         if (dualCrnp == null) {
-            return CurrentValue.rejected("鍙屽伐浣嶅爢鍨涙満涓嶅瓨鍦�: " + command.getTargetId());
+            return CurrentValue.rejected("鍙屽伐浣嶅爢鍨涙満涓嶅瓨鍦�: " + validatedChange.getTargetId());
         }
-        Integer value = "maxOutTask".equals(command.getTargetKey()) ? dualCrnp.getMaxOutTask() : dualCrnp.getMaxInTask();
-        return numericCurrentValue(toText(value), false, command.getTargetKey());
+        Integer value = "maxOutTask".equals(validatedChange.getTargetKey()) ? dualCrnp.getMaxOutTask() : dualCrnp.getMaxInTask();
+        return numericCurrentValue(toText(value), false, validatedChange.getTargetKey());
     }
 
     private CurrentValue numericCurrentValue(String oldValue, boolean nullOrNegativeAsZero, String targetKey) {
@@ -232,13 +238,13 @@
         }
     }
 
-    private Integer resolveMaxValue(AutoTuneChangeCommand command,
+    private Integer resolveMaxValue(ValidatedChange validatedChange,
                                     AutoTuneRuleDefinition.Rule rule,
                                     Integer requestedValue) {
         if (!rule.isDynamicMaxValue()) {
             return rule.getMaxValue();
         }
-        Integer targetId = parseTargetId(command.getTargetId(), rule.getTargetType());
+        Integer targetId = parseTargetId(validatedChange.getTargetId(), rule.getTargetType());
         StationFlowCapacity capacity = stationFlowCapacityService.getOne(
                 new QueryWrapper<StationFlowCapacity>()
                         .eq("station_id", targetId)
@@ -251,13 +257,12 @@
         return Math.max(0, capacity.getBufferCapacity());
     }
 
-    private Date findCooldownExpireTime(AutoTuneChangeCommand command, Date now) {
-        String targetId = normalizeTargetId(command.getTargetType(), command.getTargetId());
+    private Date findCooldownExpireTime(ValidatedChange validatedChange, Date now) {
         List<AiAutoTuneChange> recentChanges = aiAutoTuneChangeService.list(
                 new QueryWrapper<AiAutoTuneChange>()
-                        .eq("target_type", command.getTargetType())
-                        .eq("target_id", targetId)
-                        .eq("target_key", command.getTargetKey())
+                        .eq("target_type", validatedChange.getTargetType())
+                        .eq("target_id", validatedChange.getTargetId())
+                        .eq("target_key", validatedChange.getTargetKey())
                         .eq("result_status", ChangeStatus.SUCCESS.getCode())
                         .gt("cooldown_expire_time", now)
                         .orderByDesc("create_time")
@@ -275,10 +280,6 @@
     }
 
     private void applyValidatedChangesInTransaction(List<ValidatedChange> validatedChanges) {
-        if (transactionManager == null) {
-            applyValidatedChanges(validatedChanges);
-            return;
-        }
         TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
         transactionTemplate.executeWithoutResult(status -> applyValidatedChanges(validatedChanges));
     }
@@ -289,11 +290,15 @@
             if (ChangeStatus.NO_CHANGE.equals(validatedChange.getStatus())) {
                 continue;
             }
-            AutoTuneChangeCommand command = validatedChange.getCommand();
-            writeValue(command.getTargetType(), command.getTargetId(), command.getTargetKey(), validatedChange.getRequestedValue());
+            writeValue(
+                    validatedChange.getTargetType(),
+                    validatedChange.getTargetId(),
+                    validatedChange.getTargetKey(),
+                    validatedChange.getRequestedValue()
+            );
             validatedChange.accept(ChangeStatus.SUCCESS, null);
             validatedChange.setAppliedValue(validatedChange.getRequestedValue());
-            if (AutoTuneTargetType.SYS_CONFIG.getCode().equals(command.getTargetType())) {
+            if (AutoTuneTargetType.SYS_CONFIG.getCode().equals(validatedChange.getTargetType())) {
                 refreshConfigCache = true;
             }
         }
@@ -305,9 +310,6 @@
     private List<AiAutoTuneChange> rollbackChangesInTransaction(Long rollbackJobId,
                                                                 List<AiAutoTuneChange> sourceChanges,
                                                                 Date now) {
-        if (transactionManager == null) {
-            return rollbackChanges(rollbackJobId, sourceChanges, now);
-        }
         TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
         return transactionTemplate.execute(status -> rollbackChanges(rollbackJobId, sourceChanges, now));
     }
@@ -338,48 +340,60 @@
     }
 
     private void writeValue(String targetType, String targetId, String targetKey, String value) {
-        AutoTuneTargetType parsedTargetType = AutoTuneTargetType.fromCode(targetType);
+        String normalizedTargetType = normalizeText(targetType);
+        String normalizedTargetKey = normalizeText(targetKey);
+        String normalizedTargetId = normalizeTargetId(normalizedTargetType, targetId);
+        AutoTuneTargetType parsedTargetType = AutoTuneTargetType.fromCode(normalizedTargetType);
+        if (parsedTargetType == null) {
+            throw new IllegalArgumentException("涓嶆敮鎸佺殑璋冨弬鐩爣: " + normalizedTargetType + "/" + normalizedTargetKey);
+        }
         if (AutoTuneTargetType.SYS_CONFIG.equals(parsedTargetType)) {
-            if (!configService.saveConfigValue(targetKey, value)) {
-                throw new IllegalStateException("淇濆瓨杩愯鍙傛暟澶辫触: " + targetKey);
+            if (!configService.saveConfigValue(normalizedTargetKey, value)) {
+                throw new IllegalStateException("淇濆瓨杩愯鍙傛暟澶辫触: " + normalizedTargetKey);
             }
             return;
         }
-        Integer parsedTargetId = parseTargetId(targetId, parsedTargetType);
+        Integer parsedTargetId = parseTargetId(normalizedTargetId, parsedTargetType);
         Integer intValue = value == null ? null : Integer.valueOf(value);
         if (AutoTuneTargetType.STATION.equals(parsedTargetType)) {
             boolean updated = basStationService.update(new UpdateWrapper<BasStation>()
                     .eq("station_id", parsedTargetId)
                     .set("out_task_limit", intValue));
             if (!updated) {
-                throw new IllegalStateException("淇濆瓨绔欑偣鍙傛暟澶辫触: " + targetId + "/" + targetKey);
+                throw new IllegalStateException("淇濆瓨绔欑偣鍙傛暟澶辫触: " + normalizedTargetId + "/" + normalizedTargetKey);
             }
             return;
         }
         if (AutoTuneTargetType.CRN.equals(parsedTargetType)) {
-            String column = "maxOutTask".equals(targetKey) ? "max_out_task" : "max_in_task";
+            String column = "maxOutTask".equals(normalizedTargetKey) ? "max_out_task" : "max_in_task";
             boolean updated = basCrnpService.update(new UpdateWrapper<BasCrnp>()
                     .eq("crn_no", parsedTargetId)
                     .set(column, intValue));
             if (!updated) {
-                throw new IllegalStateException("淇濆瓨鍫嗗灈鏈哄弬鏁板け璐�: " + targetId + "/" + targetKey);
+                throw new IllegalStateException("淇濆瓨鍫嗗灈鏈哄弬鏁板け璐�: " + normalizedTargetId + "/" + normalizedTargetKey);
             }
             return;
         }
-        String column = "maxOutTask".equals(targetKey) ? "max_out_task" : "max_in_task";
+        String column = "maxOutTask".equals(normalizedTargetKey) ? "max_out_task" : "max_in_task";
         boolean updated = basDualCrnpService.update(new UpdateWrapper<BasDualCrnp>()
                 .eq("crn_no", parsedTargetId)
                 .set(column, intValue));
         if (!updated) {
-            throw new IllegalStateException("淇濆瓨鍙屽伐浣嶅爢鍨涙満鍙傛暟澶辫触: " + targetId + "/" + targetKey);
+            throw new IllegalStateException("淇濆瓨鍙屽伐浣嶅爢鍨涙満鍙傛暟澶辫触: " + normalizedTargetId + "/" + normalizedTargetKey);
         }
     }
 
     private String readCurrentValue(String targetType, String targetId, String targetKey) {
-        AutoTuneTargetType parsedTargetType = AutoTuneTargetType.fromCode(targetType);
-        Integer parsedTargetId = parseTargetId(targetId, parsedTargetType);
+        String normalizedTargetType = normalizeText(targetType);
+        String normalizedTargetKey = normalizeText(targetKey);
+        String normalizedTargetId = normalizeTargetId(normalizedTargetType, targetId);
+        AutoTuneTargetType parsedTargetType = AutoTuneTargetType.fromCode(normalizedTargetType);
+        if (parsedTargetType == null) {
+            throw new IllegalArgumentException("涓嶆敮鎸佺殑璋冨弬鐩爣: " + normalizedTargetType + "/" + normalizedTargetKey);
+        }
+        Integer parsedTargetId = parseTargetId(normalizedTargetId, parsedTargetType);
         if (AutoTuneTargetType.SYS_CONFIG.equals(parsedTargetType)) {
-            Config config = configService.getOne(new QueryWrapper<Config>().eq("code", targetKey).last("limit 1"));
+            Config config = configService.getOne(new QueryWrapper<Config>().eq("code", normalizedTargetKey).last("limit 1"));
             return config == null ? null : config.getValue();
         }
         if (AutoTuneTargetType.STATION.equals(parsedTargetType)) {
@@ -391,13 +405,13 @@
             if (crnp == null) {
                 return null;
             }
-            return toText("maxOutTask".equals(targetKey) ? crnp.getMaxOutTask() : crnp.getMaxInTask());
+            return toText("maxOutTask".equals(normalizedTargetKey) ? crnp.getMaxOutTask() : crnp.getMaxInTask());
         }
         BasDualCrnp dualCrnp = basDualCrnpService.getById(parsedTargetId);
         if (dualCrnp == null) {
             return null;
         }
-        return toText("maxOutTask".equals(targetKey) ? dualCrnp.getMaxOutTask() : dualCrnp.getMaxInTask());
+        return toText("maxOutTask".equals(normalizedTargetKey) ? dualCrnp.getMaxOutTask() : dualCrnp.getMaxInTask());
     }
 
     private AiAutoTuneJob createJob(AutoTuneApplyRequest request, boolean dryRun, Date now) {
@@ -463,7 +477,15 @@
         job.setSuccessCount(successCount);
         job.setRejectCount(rejectCount);
         job.setIntervalAfter(readIntervalMinutes());
-        job.setStatus(rejectCount == 0 ? AutoTuneJobStatus.SUCCESS.getCode() : AutoTuneJobStatus.PARTIAL_SUCCESS.getCode());
+        if (rejectCount == 0) {
+            job.setStatus(AutoTuneJobStatus.SUCCESS.getCode());
+        } else if (successCount == 0 && hasFailedChange(changes)) {
+            job.setStatus(AutoTuneJobStatus.FAILED.getCode());
+        } else if (successCount == 0) {
+            job.setStatus(AutoTuneJobStatus.REJECTED.getCode());
+        } else {
+            job.setStatus(AutoTuneJobStatus.PARTIAL_SUCCESS.getCode());
+        }
         job.setSummary("鍥炴粴鏈�杩戜竴娆℃垚鍔熻皟鍙傦紝鎴愬姛 " + successCount + " 椤癸紝澶辫触 " + rejectCount + " 椤�");
         if (rejectCount > 0) {
             job.setErrorMessage(firstRejectReason(changes));
@@ -502,13 +524,14 @@
         List<AiAutoTuneChange> auditChanges = new ArrayList<>();
         for (ValidatedChange validatedChange : validatedChanges) {
             AiAutoTuneChange change = new AiAutoTuneChange();
-            AutoTuneChangeCommand command = validatedChange.getCommand();
             change.setJobId(jobId);
-            change.setTargetType(command.getTargetType());
-            change.setTargetId(normalizeTargetId(command.getTargetType(), command.getTargetId()));
-            change.setTargetKey(command.getTargetKey());
+            change.setTargetType(validatedChange.getTargetType());
+            change.setTargetId(validatedChange.getTargetId());
+            change.setTargetKey(validatedChange.getTargetKey());
             change.setOldValue(validatedChange.getOldValue());
-            change.setRequestedValue(validatedChange.getRequestedValue() == null ? command.getNewValue() : validatedChange.getRequestedValue());
+            change.setRequestedValue(validatedChange.getRequestedValue() == null
+                    ? validatedChange.getRawRequestedValue()
+                    : validatedChange.getRequestedValue());
             change.setAppliedValue(validatedChange.getAppliedValue());
             change.setResultStatus(validatedChange.getStatus().getCode());
             change.setRejectReason(validatedChange.getRejectReason());
@@ -658,12 +681,16 @@
         }
     }
 
-    private String normalizeTargetId(String targetType, String targetId) {
+    private static String normalizeTargetId(String targetType, String targetId) {
         AutoTuneTargetType parsedTargetType = AutoTuneTargetType.fromCode(targetType);
         if (AutoTuneTargetType.SYS_CONFIG.equals(parsedTargetType)) {
             return "";
         }
-        return targetId == null ? "" : targetId.trim();
+        return normalizeText(targetId);
+    }
+
+    private static String normalizeText(String value) {
+        return value == null ? "" : value.trim();
     }
 
     private int readIntervalMinutes() {
@@ -734,6 +761,10 @@
 
     private static class ValidatedChange {
         private final AutoTuneChangeCommand command;
+        private final String targetType;
+        private final String targetId;
+        private final String targetKey;
+        private final String rawRequestedValue;
         private AutoTuneRuleDefinition.Rule rule;
         private String oldValue;
         private String requestedValue;
@@ -745,6 +776,10 @@
 
         private ValidatedChange(AutoTuneChangeCommand command) {
             this.command = command == null ? new AutoTuneChangeCommand() : command;
+            this.targetType = normalizeText(this.command.getTargetType());
+            this.targetId = normalizeTargetId(this.targetType, this.command.getTargetId());
+            this.targetKey = normalizeText(this.command.getTargetKey());
+            this.rawRequestedValue = this.command.getNewValue();
         }
 
         private ValidatedChange reject(String reason) {
@@ -773,6 +808,22 @@
             return command;
         }
 
+        public String getTargetType() {
+            return targetType;
+        }
+
+        public String getTargetId() {
+            return targetId;
+        }
+
+        public String getTargetKey() {
+            return targetKey;
+        }
+
+        public String getRawRequestedValue() {
+            return rawRequestedValue;
+        }
+
         public AutoTuneRuleDefinition.Rule getRule() {
             return rule;
         }

--
Gitblit v1.9.1