Junjie
3 天以前 63b01db83d9aad8a15276b4236a9a22e4aeef065
src/main/java/com/zy/ai/service/impl/AutoTuneApplyServiceImpl.java
@@ -5,6 +5,7 @@
import com.zy.ai.domain.autotune.AutoTuneApplyRequest;
import com.zy.ai.domain.autotune.AutoTuneApplyResult;
import com.zy.ai.domain.autotune.AutoTuneChangeCommand;
import com.zy.ai.domain.autotune.AutoTuneControlModeSnapshot;
import com.zy.ai.domain.autotune.AutoTuneJobStatus;
import com.zy.ai.domain.autotune.AutoTuneRuleDefinition;
import com.zy.ai.domain.autotune.AutoTuneTargetType;
@@ -14,6 +15,7 @@
import com.zy.ai.service.AiAutoTuneChangeService;
import com.zy.ai.service.AiAutoTuneJobService;
import com.zy.ai.service.AutoTuneApplyService;
import com.zy.ai.service.AutoTuneControlModeService;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.BasDualCrnp;
import com.zy.asrs.entity.BasStation;
@@ -50,6 +52,7 @@
    private static final String PROMPT_SCENE_CODE = "auto_tune_apply";
    private static final long APPLY_LOCK_SECONDS = 120L;
    private static final String APPLY_LOCK_BUSY_REASON = "申请调参锁失败,锁不可用,可能已有任务或 Redis 异常";
    private static final String ANALYSIS_ONLY_REJECT_REASON = "仅分析模式禁止实际应用/回滚,未修改运行参数";
    private static final List<Long> FINAL_WRK_STS_LIST = Arrays.asList(
            WrkStsType.COMPLETE_INBOUND.sts,
            WrkStsType.SETTLE_INBOUND.sts,
@@ -66,6 +69,8 @@
    @Autowired
    private ConfigService configService;
    @Autowired
    private AutoTuneControlModeService autoTuneControlModeService;
    @Autowired
    private BasStationService basStationService;
    @Autowired
    private BasCrnpService basCrnpService;
@@ -81,17 +86,24 @@
    @Override
    public AutoTuneApplyResult apply(AutoTuneApplyRequest request) {
        AutoTuneApplyRequest safeRequest = request == null ? new AutoTuneApplyRequest() : request;
        AutoTuneControlModeSnapshot controlMode = currentControlModeSnapshot();
        boolean dryRun = Boolean.TRUE.equals(safeRequest.getDryRun());
        Date now = new Date();
        AiAutoTuneJob job = createJob(safeRequest, dryRun, now);
        if (dryRun) {
            return applyDryRun(safeRequest, job, now);
            return applyDryRun(safeRequest, job, now, controlMode);
        }
        return applyRealWithLock(safeRequest, job, now);
        if (isAnalysisOnly(controlMode)) {
            return rejectRealApplyForAnalysisOnly(safeRequest, job, now, controlMode);
        }
        return applyRealWithLock(safeRequest, job, now, controlMode);
    }
    private AutoTuneApplyResult applyDryRun(AutoTuneApplyRequest request, AiAutoTuneJob job, Date now) {
    private AutoTuneApplyResult applyDryRun(AutoTuneApplyRequest request,
                                            AiAutoTuneJob job,
                                            Date now,
                                            AutoTuneControlModeSnapshot controlMode) {
        List<ValidatedChange> validatedChanges = validateChanges(request, true, now);
        ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction(
                job,
@@ -101,10 +113,13 @@
                now,
                false
        );
        return buildResult(job, persistenceResult.getAuditChanges(), true);
        return buildResult(job, persistenceResult.getAuditChanges(), true, controlMode);
    }
    private AutoTuneApplyResult applyRealWithLock(AutoTuneApplyRequest request, AiAutoTuneJob job, Date now) {
    private AutoTuneApplyResult applyRealWithLock(AutoTuneApplyRequest request,
                                                  AiAutoTuneJob job,
                                                  Date now,
                                                  AutoTuneControlModeSnapshot controlMode) {
        if (request.getChanges() == null || request.getChanges().isEmpty()) {
            ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction(
                    job,
@@ -114,13 +129,13 @@
                    now,
                    false
            );
            return buildResult(job, persistenceResult.getAuditChanges(), false);
            return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode);
        }
        String lockKey = RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key;
        String lockToken = UUID.randomUUID().toString();
        if (!redisUtil.trySetStringIfAbsent(lockKey, lockToken, APPLY_LOCK_SECONDS)) {
            return rejectRealApplyForUnavailableLock(request, job, now, lockKey);
            return rejectRealApplyForUnavailableLock(request, job, now, lockKey, controlMode);
        }
        try {
@@ -138,7 +153,7 @@
                        now,
                        false
                );
                return buildResult(job, persistenceResult.getAuditChanges(), false);
                return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode);
            }
            try {
@@ -151,7 +166,7 @@
                        true
                );
                refreshSystemConfigCacheSafely(persistenceResult);
                return buildResult(job, persistenceResult.getAuditChanges(), false);
                return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode);
            } catch (RuntimeException exception) {
                markWriteFailure(validatedChanges, exception);
                Date failureNow = new Date();
@@ -164,7 +179,7 @@
                        failureNow,
                        false
                );
                return buildResult(failureJob, persistenceResult.getAuditChanges(), false);
                return buildResult(failureJob, persistenceResult.getAuditChanges(), false, controlMode);
            }
        } finally {
            redisUtil.compareAndDelete(lockKey, lockToken);
@@ -174,7 +189,8 @@
    private AutoTuneApplyResult rejectRealApplyForUnavailableLock(AutoTuneApplyRequest request,
                                                                  AiAutoTuneJob job,
                                                                  Date now,
                                                                  String lockKey) {
                                                                  String lockKey,
                                                                  AutoTuneControlModeSnapshot controlMode) {
        boolean lockKeyExists = redisUtil.hasKey(lockKey);
        LOGGER.warn("申请AI自动调参 apply 锁失败,lockKey={}, lockKeyExists={}", lockKey, lockKeyExists);
        List<ValidatedChange> validatedChanges = buildLockBusyChanges(request);
@@ -186,7 +202,18 @@
                now,
                false
        );
        return buildResult(job, persistenceResult.getAuditChanges(), false);
        return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode);
    }
    private AutoTuneApplyResult rejectRealApplyForAnalysisOnly(AutoTuneApplyRequest request,
                                                               AiAutoTuneJob job,
                                                               Date now,
                                                               AutoTuneControlModeSnapshot controlMode) {
        ApplyPersistenceResult persistenceResult = persistAnalysisOnlyApplyRejectionInTransaction(job, request, now);
        AutoTuneApplyResult result = buildResult(job, persistenceResult.getAuditChanges(), false, controlMode);
        result.setAnalysisOnly(true);
        result.setNoApply(true);
        return result;
    }
    private List<ValidatedChange> buildLockBusyChanges(AutoTuneApplyRequest request) {
@@ -204,20 +231,24 @@
    @Override
    public AutoTuneApplyResult rollbackLastSuccessfulJob(String reason) {
        AutoTuneControlModeSnapshot controlMode = currentControlModeSnapshot();
        Date now = new Date();
        AiAutoTuneJob rollbackJob = createRollbackJob(reason, now);
        if (isAnalysisOnly(controlMode)) {
            return rejectRollbackForAnalysisOnly(rollbackJob, now, controlMode);
        }
        String lockKey = RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key;
        String lockToken = UUID.randomUUID().toString();
        if (!redisUtil.trySetStringIfAbsent(lockKey, lockToken, APPLY_LOCK_SECONDS)) {
            return rejectRollbackForUnavailableLock(reason, now, lockKey);
            return rejectRollbackForUnavailableLock(reason, now, lockKey, controlMode);
        }
        try {
            List<AiAutoTuneChange> sourceChanges = findLatestSuccessfulChanges();
            if (sourceChanges.isEmpty()) {
                persistNoRollbackSourceJobInTransaction(rollbackJob, now);
                return buildResult(rollbackJob, new ArrayList<>(), false);
                return buildResult(rollbackJob, new ArrayList<>(), false, controlMode);
            }
            try {
                RollbackPersistenceResult persistenceResult = persistRollbackResultInTransaction(
@@ -226,7 +257,7 @@
                        now
                );
                refreshRollbackConfigCacheSafely(persistenceResult);
                return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false);
                return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false, controlMode);
            } catch (RuntimeException exception) {
                Date failureNow = new Date();
                AiAutoTuneJob failureJob = createRollbackJob(reason, failureNow);
@@ -236,7 +267,7 @@
                        exception,
                        failureNow
                );
                return buildResult(failureJob, persistenceResult.getRollbackChanges(), false);
                return buildResult(failureJob, persistenceResult.getRollbackChanges(), false, controlMode);
            }
        } finally {
            redisUtil.compareAndDelete(lockKey, lockToken);
@@ -245,7 +276,8 @@
    private AutoTuneApplyResult rejectRollbackForUnavailableLock(String reason,
                                                                 Date now,
                                                                 String lockKey) {
                                                                 String lockKey,
                                                                 AutoTuneControlModeSnapshot controlMode) {
        boolean lockKeyExists = redisUtil.hasKey(lockKey);
        LOGGER.warn("申请AI自动调参 rollback 锁失败,lockKey={}, lockKeyExists={}", lockKey, lockKeyExists);
        AiAutoTuneJob rollbackJob = createRollbackJob(reason, now);
@@ -253,7 +285,20 @@
                rollbackJob,
                now
        );
        return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false);
        return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false, controlMode);
    }
    private AutoTuneApplyResult rejectRollbackForAnalysisOnly(AiAutoTuneJob rollbackJob,
                                                              Date now,
                                                              AutoTuneControlModeSnapshot controlMode) {
        RollbackPersistenceResult persistenceResult = persistAnalysisOnlyRollbackRejectionInTransaction(
                rollbackJob,
                now
        );
        AutoTuneApplyResult result = buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false, controlMode);
        result.setAnalysisOnly(true);
        result.setNoApply(true);
        return result;
    }
    private List<ValidatedChange> validateChanges(AutoTuneApplyRequest request, boolean dryRun, Date now) {
@@ -293,11 +338,14 @@
        validatedChange.setOldValue(currentValue.getOldValue());
        Integer maxValue = resolveMaxValue(validatedChange, rule, requestedValue);
        if (maxValue == null) {
            return validatedChange.reject("站点 " + validatedChange.getTargetId()
                    + " 缺少 outBufferCapacity,无法证明 outTaskLimit 上限");
        if (requestedValue < rule.getMinValue()) {
            if (maxValue != null) {
                return validatedChange.reject(validatedChange.getTargetKey() + " 必须在 "
                        + rule.getMinValue() + "~" + maxValue + " 范围内");
            }
            return validatedChange.reject(validatedChange.getTargetKey() + " 必须不小于 " + rule.getMinValue());
        }
        if (requestedValue < rule.getMinValue() || requestedValue > maxValue) {
        if (maxValue != null && requestedValue > maxValue) {
            return validatedChange.reject(validatedChange.getTargetKey() + " 必须在 "
                    + rule.getMinValue() + "~" + maxValue + " 范围内");
        }
@@ -555,6 +603,33 @@
        });
    }
    private ApplyPersistenceResult persistAnalysisOnlyApplyRejectionInTransaction(AiAutoTuneJob job,
                                                                                  AutoTuneApplyRequest request,
                                                                                  Date now) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        return transactionTemplate.execute(status -> {
            saveJob(job);
            List<AiAutoTuneChange> auditChanges = buildAnalysisOnlyApplyChanges(job.getId(), request, now);
            saveAuditChanges(auditChanges);
            finishAnalysisOnlyRejectedJob(job, auditChanges, now);
            updateJob(job);
            return new ApplyPersistenceResult(auditChanges, false);
        });
    }
    private RollbackPersistenceResult persistAnalysisOnlyRollbackRejectionInTransaction(AiAutoTuneJob rollbackJob,
                                                                                       Date now) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        return transactionTemplate.execute(status -> {
            saveJob(rollbackJob);
            List<AiAutoTuneChange> rollbackChanges = buildAnalysisOnlyRollbackChanges(rollbackJob.getId(), now);
            saveAuditChanges(rollbackChanges);
            finishAnalysisOnlyRejectedJob(rollbackJob, rollbackChanges, now);
            updateJob(rollbackJob);
            return new RollbackPersistenceResult(rollbackChanges, false);
        });
    }
    private RollbackPersistenceResult rollbackChanges(Long rollbackJobId, List<AiAutoTuneChange> sourceChanges, Date now) {
        List<AiAutoTuneChange> rollbackChanges = new ArrayList<>();
        boolean refreshConfigCache = false;
@@ -587,6 +662,58 @@
        rollbackChange.setRejectReason(APPLY_LOCK_BUSY_REASON);
        rollbackChange.setCreateTime(now);
        return List.of(rollbackChange);
    }
    private List<AiAutoTuneChange> buildAnalysisOnlyApplyChanges(Long jobId,
                                                                 AutoTuneApplyRequest request,
                                                                 Date now) {
        List<AiAutoTuneChange> changes = new ArrayList<>();
        List<AutoTuneChangeCommand> commands = request == null ? null : request.getChanges();
        if (commands == null || commands.isEmpty()) {
            changes.add(buildAnalysisOnlyChange(jobId, "analysis_only", "", "apply", null, now));
            return changes;
        }
        for (AutoTuneChangeCommand command : commands) {
            changes.add(buildAnalysisOnlyChange(
                    jobId,
                    normalizeText(command == null ? null : command.getTargetType()),
                    normalizeText(command == null ? null : command.getTargetId()),
                    normalizeText(command == null ? null : command.getTargetKey()),
                    command == null ? null : command.getNewValue(),
                    now
            ));
        }
        return changes;
    }
    private List<AiAutoTuneChange> buildAnalysisOnlyRollbackChanges(Long jobId, Date now) {
        AiAutoTuneChange rollbackChange = buildAnalysisOnlyChange(
                jobId,
                "analysis_only",
                "",
                "rollback",
                null,
                now
        );
        return List.of(rollbackChange);
    }
    private AiAutoTuneChange buildAnalysisOnlyChange(Long jobId,
                                                     String targetType,
                                                     String targetId,
                                                     String targetKey,
                                                     String requestedValue,
                                                     Date now) {
        AiAutoTuneChange change = new AiAutoTuneChange();
        change.setJobId(jobId);
        change.setTargetType(targetType);
        change.setTargetId(targetId);
        change.setTargetKey(targetKey);
        change.setRequestedValue(requestedValue);
        change.setResultStatus(ChangeStatus.REJECTED.getCode());
        change.setRejectReason(ANALYSIS_ONLY_REJECT_REASON);
        change.setCreateTime(now);
        return change;
    }
    private void refreshRollbackConfigCacheSafely(RollbackPersistenceResult persistenceResult) {
@@ -749,6 +876,17 @@
        }
    }
    private void finishAnalysisOnlyRejectedJob(AiAutoTuneJob job, List<AiAutoTuneChange> changes, Date now) {
        int rejectCount = Math.max(1, countRejected(changes));
        job.setFinishTime(now);
        job.setSuccessCount(0);
        job.setRejectCount(rejectCount);
        job.setIntervalAfter(readIntervalMinutes());
        job.setStatus(AutoTuneJobStatus.REJECTED.getCode());
        job.setSummary(ANALYSIS_ONLY_REJECT_REASON);
        job.setErrorMessage(ANALYSIS_ONLY_REJECT_REASON);
    }
    private void finishRollbackJob(AiAutoTuneJob job, List<AiAutoTuneChange> changes, Date now) {
        int successCount = countAccepted(changes);
        int rejectCount = countRejected(changes);
@@ -876,11 +1014,16 @@
        return !AutoTuneTriggerType.ROLLBACK.getCode().equals(job.getTriggerType());
    }
    private AutoTuneApplyResult buildResult(AiAutoTuneJob job, List<AiAutoTuneChange> changes, boolean dryRun) {
    private AutoTuneApplyResult buildResult(AiAutoTuneJob job,
                                            List<AiAutoTuneChange> changes,
                                            boolean dryRun,
                                            AutoTuneControlModeSnapshot controlMode) {
        AutoTuneApplyResult result = new AutoTuneApplyResult();
        result.setDryRun(dryRun);
        result.setSuccess(AutoTuneJobStatus.SUCCESS.getCode().equals(job.getStatus())
                || AutoTuneJobStatus.NO_CHANGE.getCode().equals(job.getStatus()));
        result.setAnalysisOnly(isAnalysisOnly(controlMode));
        result.setNoApply(false);
        result.setJobId(job.getId());
        result.setSummary(job.getSummary());
        result.setSuccessCount(job.getSuccessCount());
@@ -984,6 +1127,14 @@
        }
    }
    private AutoTuneControlModeSnapshot currentControlModeSnapshot() {
        return autoTuneControlModeService.currentMode();
    }
    private boolean isAnalysisOnly(AutoTuneControlModeSnapshot controlMode) {
        return controlMode == null || Boolean.TRUE.equals(controlMode.getAnalysisOnly());
    }
    private String toText(Integer value) {
        return value == null ? null : String.valueOf(value);
    }