| | |
| | | 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; |
| | |
| | | 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; |
| | | import com.zy.asrs.entity.StationFlowCapacity; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.asrs.service.StationFlowCapacityService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.service.ConfigService; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.PlatformTransactionManager; |
| | | import org.springframework.transaction.support.TransactionTemplate; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.UUID; |
| | | |
| | | @Service("autoTuneApplyService") |
| | | public class AutoTuneApplyServiceImpl implements AutoTuneApplyService { |
| | | |
| | | private static final Logger LOGGER = LoggerFactory.getLogger(AutoTuneApplyServiceImpl.class); |
| | | private static final String PROMPT_SCENE_CODE = "auto_tune_apply"; |
| | | private static final String DIRECTION_OUT = "OUT"; |
| | | 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, |
| | | WrkStsType.COMPLETE_OUTBOUND.sts, |
| | | WrkStsType.SETTLE_OUTBOUND.sts, |
| | | WrkStsType.COMPLETE_LOC_MOVE.sts, |
| | | WrkStsType.COMPLETE_CRN_MOVE.sts |
| | | ); |
| | | |
| | | @Autowired |
| | | private AiAutoTuneJobService aiAutoTuneJobService; |
| | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private AutoTuneControlModeService autoTuneControlModeService; |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private StationFlowCapacityService stationFlowCapacityService; |
| | | @Autowired(required = false) |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private PlatformTransactionManager transactionManager; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @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); |
| | | aiAutoTuneJobService.save(job); |
| | | |
| | | List<ValidatedChange> validatedChanges = validateChanges(safeRequest, dryRun, now); |
| | | boolean hasRejectedChange = hasRejectedChange(validatedChanges); |
| | | if (!dryRun && hasRejectedChange) { |
| | | markAcceptedChangesAsBatchRejected(validatedChanges); |
| | | if (dryRun) { |
| | | return applyDryRun(safeRequest, job, now, controlMode); |
| | | } |
| | | if (!dryRun && !hasRejectedChange) { |
| | | if (isAnalysisOnly(controlMode)) { |
| | | return rejectRealApplyForAnalysisOnly(safeRequest, job, now, controlMode); |
| | | } |
| | | return applyRealWithLock(safeRequest, job, now, controlMode); |
| | | } |
| | | |
| | | private AutoTuneApplyResult applyDryRun(AutoTuneApplyRequest request, |
| | | AiAutoTuneJob job, |
| | | Date now, |
| | | AutoTuneControlModeSnapshot controlMode) { |
| | | List<ValidatedChange> validatedChanges = validateChanges(request, true, now); |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | job, |
| | | request, |
| | | validatedChanges, |
| | | true, |
| | | now, |
| | | false |
| | | ); |
| | | return buildResult(job, persistenceResult.getAuditChanges(), true, controlMode); |
| | | } |
| | | |
| | | private AutoTuneApplyResult applyRealWithLock(AutoTuneApplyRequest request, |
| | | AiAutoTuneJob job, |
| | | Date now, |
| | | AutoTuneControlModeSnapshot controlMode) { |
| | | if (request.getChanges() == null || request.getChanges().isEmpty()) { |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | job, |
| | | request, |
| | | new ArrayList<>(), |
| | | false, |
| | | now, |
| | | 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, controlMode); |
| | | } |
| | | |
| | | try { |
| | | List<ValidatedChange> validatedChanges = validateChanges(request, false, now); |
| | | boolean hasRejectedChange = hasRejectedChange(validatedChanges); |
| | | if (hasRejectedChange) { |
| | | markAcceptedChangesAsBatchRejected(validatedChanges); |
| | | } |
| | | if (hasRejectedChange) { |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | job, |
| | | request, |
| | | validatedChanges, |
| | | false, |
| | | now, |
| | | false |
| | | ); |
| | | return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode); |
| | | } |
| | | |
| | | try { |
| | | applyValidatedChangesInTransaction(validatedChanges); |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | job, |
| | | request, |
| | | validatedChanges, |
| | | false, |
| | | now, |
| | | true |
| | | ); |
| | | refreshSystemConfigCacheSafely(persistenceResult); |
| | | return buildResult(job, persistenceResult.getAuditChanges(), false, controlMode); |
| | | } catch (RuntimeException exception) { |
| | | markWriteFailure(validatedChanges, exception); |
| | | Date failureNow = new Date(); |
| | | AiAutoTuneJob failureJob = createJob(request, false, failureNow); |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | failureJob, |
| | | request, |
| | | validatedChanges, |
| | | false, |
| | | failureNow, |
| | | false |
| | | ); |
| | | return buildResult(failureJob, persistenceResult.getAuditChanges(), false, controlMode); |
| | | } |
| | | } finally { |
| | | redisUtil.compareAndDelete(lockKey, lockToken); |
| | | } |
| | | } |
| | | |
| | | List<AiAutoTuneChange> auditChanges = buildAuditChanges(job.getId(), validatedChanges, now); |
| | | if (!auditChanges.isEmpty()) { |
| | | aiAutoTuneChangeService.saveBatch(auditChanges); |
| | | private AutoTuneApplyResult rejectRealApplyForUnavailableLock(AutoTuneApplyRequest request, |
| | | AiAutoTuneJob job, |
| | | Date now, |
| | | String lockKey, |
| | | AutoTuneControlModeSnapshot controlMode) { |
| | | boolean lockKeyExists = redisUtil.hasKey(lockKey); |
| | | LOGGER.warn("申请AI自动调参 apply 锁失败,lockKey={}, lockKeyExists={}", lockKey, lockKeyExists); |
| | | List<ValidatedChange> validatedChanges = buildLockBusyChanges(request); |
| | | ApplyPersistenceResult persistenceResult = persistApplyResultInTransaction( |
| | | job, |
| | | request, |
| | | validatedChanges, |
| | | false, |
| | | now, |
| | | 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) { |
| | | List<ValidatedChange> validatedChanges = new ArrayList<>(); |
| | | if (request.getChanges() == null || request.getChanges().isEmpty()) { |
| | | return validatedChanges; |
| | | } |
| | | finishJob(job, safeRequest, auditChanges, dryRun, now); |
| | | aiAutoTuneJobService.updateById(job); |
| | | return buildResult(job, auditChanges, dryRun); |
| | | for (AutoTuneChangeCommand command : request.getChanges()) { |
| | | ValidatedChange validatedChange = new ValidatedChange(command); |
| | | validatedChange.fail(APPLY_LOCK_BUSY_REASON); |
| | | validatedChanges.add(validatedChange); |
| | | } |
| | | return validatedChanges; |
| | | } |
| | | |
| | | @Override |
| | | public AutoTuneApplyResult rollbackLastSuccessfulJob(String reason) { |
| | | AutoTuneControlModeSnapshot controlMode = currentControlModeSnapshot(); |
| | | Date now = new Date(); |
| | | AiAutoTuneJob rollbackJob = createRollbackJob(reason, now); |
| | | aiAutoTuneJobService.save(rollbackJob); |
| | | |
| | | List<AiAutoTuneChange> sourceChanges = findLatestSuccessfulChanges(); |
| | | if (sourceChanges.isEmpty()) { |
| | | rollbackJob.setStatus(AutoTuneJobStatus.REJECTED.getCode()); |
| | | rollbackJob.setFinishTime(now); |
| | | rollbackJob.setRejectCount(0); |
| | | rollbackJob.setSuccessCount(0); |
| | | rollbackJob.setSummary("未找到可回滚的成功调参记录"); |
| | | rollbackJob.setErrorMessage("未找到可回滚的成功调参记录"); |
| | | aiAutoTuneJobService.updateById(rollbackJob); |
| | | return buildResult(rollbackJob, new ArrayList<>(), false); |
| | | if (isAnalysisOnly(controlMode)) { |
| | | return rejectRollbackForAnalysisOnly(rollbackJob, now, controlMode); |
| | | } |
| | | |
| | | List<AiAutoTuneChange> rollbackChanges = new ArrayList<>(); |
| | | 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, controlMode); |
| | | } |
| | | |
| | | try { |
| | | rollbackChanges = rollbackChangesInTransaction(rollbackJob.getId(), sourceChanges, now); |
| | | } catch (RuntimeException exception) { |
| | | rollbackChanges = buildFailedRollbackChanges(rollbackJob.getId(), sourceChanges, exception, now); |
| | | List<AiAutoTuneChange> sourceChanges = findLatestSuccessfulChanges(); |
| | | if (sourceChanges.isEmpty()) { |
| | | persistNoRollbackSourceJobInTransaction(rollbackJob, now); |
| | | return buildResult(rollbackJob, new ArrayList<>(), false, controlMode); |
| | | } |
| | | try { |
| | | RollbackPersistenceResult persistenceResult = persistRollbackResultInTransaction( |
| | | rollbackJob, |
| | | sourceChanges, |
| | | now |
| | | ); |
| | | refreshRollbackConfigCacheSafely(persistenceResult); |
| | | return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false, controlMode); |
| | | } catch (RuntimeException exception) { |
| | | Date failureNow = new Date(); |
| | | AiAutoTuneJob failureJob = createRollbackJob(reason, failureNow); |
| | | RollbackPersistenceResult persistenceResult = persistFailedRollbackResultInTransaction( |
| | | failureJob, |
| | | sourceChanges, |
| | | exception, |
| | | failureNow |
| | | ); |
| | | return buildResult(failureJob, persistenceResult.getRollbackChanges(), false, controlMode); |
| | | } |
| | | } finally { |
| | | redisUtil.compareAndDelete(lockKey, lockToken); |
| | | } |
| | | aiAutoTuneChangeService.saveBatch(rollbackChanges); |
| | | finishRollbackJob(rollbackJob, rollbackChanges, now); |
| | | aiAutoTuneJobService.updateById(rollbackJob); |
| | | return buildResult(rollbackJob, rollbackChanges, false); |
| | | } |
| | | |
| | | private AutoTuneApplyResult rejectRollbackForUnavailableLock(String reason, |
| | | Date now, |
| | | String lockKey, |
| | | AutoTuneControlModeSnapshot controlMode) { |
| | | boolean lockKeyExists = redisUtil.hasKey(lockKey); |
| | | LOGGER.warn("申请AI自动调参 rollback 锁失败,lockKey={}, lockKeyExists={}", lockKey, lockKeyExists); |
| | | AiAutoTuneJob rollbackJob = createRollbackJob(reason, now); |
| | | RollbackPersistenceResult persistenceResult = persistRollbackLockFailureInTransaction( |
| | | rollbackJob, |
| | | now |
| | | ); |
| | | 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) { |
| | |
| | | |
| | | 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); |
| | | if (maxValue == null) { |
| | | return validatedChange.reject("站点 " + command.getTargetId() + " 缺少 OUT 方向 bufferCapacity,无法证明 outTaskLimit 上限"); |
| | | Integer maxValue = resolveMaxValue(validatedChange, rule, requestedValue); |
| | | 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) { |
| | | return validatedChange.reject(command.getTargetKey() + " 必须在 " + rule.getMinValue() + "~" + maxValue + " 范围内"); |
| | | if (maxValue != null && requestedValue > maxValue) { |
| | | return validatedChange.reject(validatedChange.getTargetKey() + " 必须在 " |
| | | + rule.getMinValue() + "~" + maxValue + " 范围内"); |
| | | } |
| | | |
| | | if (Objects.equals(currentValue.getNumericValue(), requestedValue)) { |
| | |
| | | } |
| | | 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); |
| | |
| | | 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(), 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 stationOutTaskLimitCurrentValue(station.getOutTaskLimit(), 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), 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), validatedChange.getTargetKey()); |
| | | } |
| | | |
| | | private CurrentValue numericCurrentValue(String oldValue, boolean nullOrNegativeAsZero, String targetKey) { |
| | | private CurrentValue stationOutTaskLimitCurrentValue(Integer outTaskLimit, String targetKey) { |
| | | if (outTaskLimit == null || outTaskLimit < 0) { |
| | | return CurrentValue.rejected(targetKey + " 当前为不限制,需要人工先初始化为有限值后才能自动调参"); |
| | | } |
| | | return numericCurrentValue(toText(outTaskLimit), targetKey); |
| | | } |
| | | |
| | | private CurrentValue numericCurrentValue(String oldValue, String targetKey) { |
| | | if (oldValue == null || oldValue.trim().isEmpty()) { |
| | | if (nullOrNegativeAsZero) { |
| | | return CurrentValue.accepted(null, 0); |
| | | } |
| | | return CurrentValue.rejected(targetKey + " 当前值为空,无法计算步长"); |
| | | } |
| | | try { |
| | | Integer parsedValue = Integer.valueOf(oldValue.trim()); |
| | | if (nullOrNegativeAsZero && parsedValue < 0) { |
| | | return CurrentValue.accepted(oldValue, 0); |
| | | } |
| | | return CurrentValue.accepted(oldValue, parsedValue); |
| | | } catch (Exception exception) { |
| | | return CurrentValue.rejected(targetKey + " 当前值不是整数,无法计算步长"); |
| | | } |
| | | } |
| | | |
| | | 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()); |
| | | StationFlowCapacity capacity = stationFlowCapacityService.getOne( |
| | | new QueryWrapper<StationFlowCapacity>() |
| | | .eq("station_id", targetId) |
| | | .eq("direction_code", DIRECTION_OUT) |
| | | .last("limit 1") |
| | | ); |
| | | if (capacity == null || capacity.getBufferCapacity() == null) { |
| | | Integer targetId = parseTargetId(validatedChange.getTargetId(), rule.getTargetType()); |
| | | BasStation station = basStationService.getById(targetId); |
| | | if (station == null || station.getOutBufferCapacity() == null) { |
| | | return requestedValue == 0 ? 0 : null; |
| | | } |
| | | return Math.max(0, capacity.getBufferCapacity()); |
| | | return Math.max(0, station.getOutBufferCapacity()); |
| | | } |
| | | |
| | | 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") |
| | |
| | | return cooldownExpireTime; |
| | | } |
| | | |
| | | private void applyValidatedChangesInTransaction(List<ValidatedChange> validatedChanges) { |
| | | if (transactionManager == null) { |
| | | applyValidatedChanges(validatedChanges); |
| | | return; |
| | | } |
| | | private ApplyPersistenceResult persistApplyResultInTransaction(AiAutoTuneJob job, |
| | | AutoTuneApplyRequest request, |
| | | List<ValidatedChange> validatedChanges, |
| | | boolean dryRun, |
| | | Date now, |
| | | boolean writeTargets) { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | transactionTemplate.executeWithoutResult(status -> applyValidatedChanges(validatedChanges)); |
| | | return transactionTemplate.execute(status -> persistApplyResult( |
| | | job, |
| | | request, |
| | | validatedChanges, |
| | | dryRun, |
| | | now, |
| | | writeTargets |
| | | )); |
| | | } |
| | | |
| | | private void applyValidatedChanges(List<ValidatedChange> validatedChanges) { |
| | | private ApplyPersistenceResult persistApplyResult(AiAutoTuneJob job, |
| | | AutoTuneApplyRequest request, |
| | | List<ValidatedChange> validatedChanges, |
| | | boolean dryRun, |
| | | Date now, |
| | | boolean writeTargets) { |
| | | saveJob(job); |
| | | boolean refreshConfigCache = false; |
| | | if (writeTargets) { |
| | | refreshConfigCache = applyValidatedChanges(validatedChanges); |
| | | } |
| | | List<AiAutoTuneChange> auditChanges = buildAuditChanges(job.getId(), validatedChanges, now); |
| | | saveAuditChanges(auditChanges); |
| | | finishJob(job, request, auditChanges, dryRun, now); |
| | | updateJob(job); |
| | | return new ApplyPersistenceResult(auditChanges, refreshConfigCache); |
| | | } |
| | | |
| | | private void saveJob(AiAutoTuneJob job) { |
| | | if (!aiAutoTuneJobService.save(job)) { |
| | | throw new IllegalStateException("保存调参任务失败"); |
| | | } |
| | | } |
| | | |
| | | private boolean applyValidatedChanges(List<ValidatedChange> validatedChanges) { |
| | | boolean refreshConfigCache = false; |
| | | for (ValidatedChange validatedChange : validatedChanges) { |
| | | 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; |
| | | } |
| | | } |
| | | if (refreshConfigCache) { |
| | | configService.refreshSystemConfigCache(); |
| | | return refreshConfigCache; |
| | | } |
| | | |
| | | private void saveAuditChanges(List<AiAutoTuneChange> auditChanges) { |
| | | if (auditChanges.isEmpty()) { |
| | | return; |
| | | } |
| | | if (!aiAutoTuneChangeService.saveBatch(auditChanges)) { |
| | | throw new IllegalStateException("保存调参审计失败"); |
| | | } |
| | | } |
| | | |
| | | private List<AiAutoTuneChange> rollbackChangesInTransaction(Long rollbackJobId, |
| | | List<AiAutoTuneChange> sourceChanges, |
| | | Date now) { |
| | | if (transactionManager == null) { |
| | | return rollbackChanges(rollbackJobId, sourceChanges, now); |
| | | private void updateJob(AiAutoTuneJob job) { |
| | | if (!aiAutoTuneJobService.updateById(job)) { |
| | | throw new IllegalStateException("更新调参任务状态失败"); |
| | | } |
| | | } |
| | | |
| | | private void refreshSystemConfigCacheSafely(ApplyPersistenceResult persistenceResult) { |
| | | if (persistenceResult != null && persistenceResult.isRefreshConfigCache()) { |
| | | refreshSystemConfigCacheSafely("apply"); |
| | | } |
| | | } |
| | | |
| | | private void persistNoRollbackSourceJobInTransaction(AiAutoTuneJob rollbackJob, Date now) { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | return transactionTemplate.execute(status -> rollbackChanges(rollbackJobId, sourceChanges, now)); |
| | | transactionTemplate.executeWithoutResult(status -> { |
| | | saveJob(rollbackJob); |
| | | rollbackJob.setStatus(AutoTuneJobStatus.REJECTED.getCode()); |
| | | rollbackJob.setFinishTime(now); |
| | | rollbackJob.setRejectCount(0); |
| | | rollbackJob.setSuccessCount(0); |
| | | rollbackJob.setSummary("未找到可回滚的成功调参记录"); |
| | | rollbackJob.setErrorMessage("未找到可回滚的成功调参记录"); |
| | | updateJob(rollbackJob); |
| | | }); |
| | | } |
| | | |
| | | private List<AiAutoTuneChange> rollbackChanges(Long rollbackJobId, List<AiAutoTuneChange> sourceChanges, Date now) { |
| | | private RollbackPersistenceResult persistRollbackResultInTransaction(AiAutoTuneJob rollbackJob, |
| | | List<AiAutoTuneChange> sourceChanges, |
| | | Date now) { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | return transactionTemplate.execute(status -> { |
| | | saveJob(rollbackJob); |
| | | RollbackPersistenceResult rollbackResult = rollbackChanges(rollbackJob.getId(), sourceChanges, now); |
| | | saveAuditChanges(rollbackResult.getRollbackChanges()); |
| | | finishRollbackJob(rollbackJob, rollbackResult.getRollbackChanges(), now); |
| | | updateJob(rollbackJob); |
| | | return rollbackResult; |
| | | }); |
| | | } |
| | | |
| | | private RollbackPersistenceResult persistFailedRollbackResultInTransaction(AiAutoTuneJob rollbackJob, |
| | | List<AiAutoTuneChange> sourceChanges, |
| | | RuntimeException exception, |
| | | Date now) { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | return transactionTemplate.execute(status -> { |
| | | saveJob(rollbackJob); |
| | | List<AiAutoTuneChange> rollbackChanges = buildFailedRollbackChanges( |
| | | rollbackJob.getId(), |
| | | sourceChanges, |
| | | exception, |
| | | now |
| | | ); |
| | | saveAuditChanges(rollbackChanges); |
| | | finishRollbackJob(rollbackJob, rollbackChanges, now); |
| | | updateJob(rollbackJob); |
| | | return new RollbackPersistenceResult(rollbackChanges, false); |
| | | }); |
| | | } |
| | | |
| | | private RollbackPersistenceResult persistRollbackLockFailureInTransaction(AiAutoTuneJob rollbackJob, Date now) { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | return transactionTemplate.execute(status -> { |
| | | saveJob(rollbackJob); |
| | | List<AiAutoTuneChange> rollbackChanges = buildRollbackLockFailureChanges(rollbackJob.getId(), now); |
| | | saveAuditChanges(rollbackChanges); |
| | | finishRollbackJob(rollbackJob, rollbackChanges, now); |
| | | updateJob(rollbackJob); |
| | | return new RollbackPersistenceResult(rollbackChanges, false); |
| | | }); |
| | | } |
| | | |
| | | 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; |
| | | for (AiAutoTuneChange sourceChange : sourceChanges) { |
| | |
| | | rollbackChange.setResultStatus(ChangeStatus.SUCCESS.getCode()); |
| | | rollbackChanges.add(rollbackChange); |
| | | } |
| | | if (refreshConfigCache) { |
| | | configService.refreshSystemConfigCache(); |
| | | return new RollbackPersistenceResult(rollbackChanges, refreshConfigCache); |
| | | } |
| | | |
| | | private List<AiAutoTuneChange> buildRollbackLockFailureChanges(Long rollbackJobId, Date now) { |
| | | AiAutoTuneChange rollbackChange = new AiAutoTuneChange(); |
| | | rollbackChange.setJobId(rollbackJobId); |
| | | rollbackChange.setTargetType(AutoTuneTriggerType.ROLLBACK.getCode()); |
| | | rollbackChange.setTargetId(""); |
| | | rollbackChange.setTargetKey("latest_successful_job"); |
| | | rollbackChange.setResultStatus(ChangeStatus.FAILED.getCode()); |
| | | 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; |
| | | } |
| | | return rollbackChanges; |
| | | 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) { |
| | | if (persistenceResult != null && persistenceResult.isRefreshConfigCache()) { |
| | | refreshSystemConfigCacheSafely("rollback"); |
| | | } |
| | | } |
| | | |
| | | private void refreshSystemConfigCacheSafely(String scene) { |
| | | try { |
| | | configService.refreshSystemConfigCache(); |
| | | } catch (RuntimeException exception) { |
| | | LOGGER.warn("AI自动调参{}已提交,但刷新系统配置缓存失败", scene, exception); |
| | | } |
| | | } |
| | | |
| | | 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)) { |
| | |
| | | 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) { |
| | |
| | | job.setTriggerType(AutoTuneTriggerType.normalize(request.getTriggerType())); |
| | | job.setStatus(AutoTuneJobStatus.RUNNING.getCode()); |
| | | job.setStartTime(now); |
| | | job.setHasActiveTasks(0); |
| | | job.setHasActiveTasks(resolveHasActiveTasksForAudit()); |
| | | job.setPromptSceneCode(PROMPT_SCENE_CODE); |
| | | job.setSummary(dryRun ? "AI自动调参 dry-run: " + safeReason(request.getReason()) : safeReason(request.getReason())); |
| | | job.setIntervalBefore(readIntervalMinutes()); |
| | |
| | | job.setTriggerType(AutoTuneTriggerType.ROLLBACK.getCode()); |
| | | job.setStatus(AutoTuneJobStatus.RUNNING.getCode()); |
| | | job.setStartTime(now); |
| | | job.setHasActiveTasks(0); |
| | | job.setHasActiveTasks(resolveHasActiveTasksForAudit()); |
| | | job.setPromptSceneCode(PROMPT_SCENE_CODE); |
| | | job.setSummary(safeReason(reason)); |
| | | job.setIntervalBefore(readIntervalMinutes()); |
| | |
| | | job.setTotalTokens(0); |
| | | job.setCreateTime(now); |
| | | return job; |
| | | } |
| | | |
| | | private int resolveHasActiveTasksForAudit() { |
| | | if (wrkMastService == null) { |
| | | LOGGER.warn("AI自动调参审计无法获取 WrkMastService,hasActiveTasks 按 0 记录"); |
| | | return 0; |
| | | } |
| | | try { |
| | | QueryWrapper<WrkMast> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.and(wrapper -> wrapper.notIn("wrk_sts", FINAL_WRK_STS_LIST).or().isNull("wrk_sts")); |
| | | return wrkMastService.count(queryWrapper) > 0 ? 1 : 0; |
| | | } catch (RuntimeException exception) { |
| | | LOGGER.warn("AI自动调参审计查询未完成任务失败,hasActiveTasks 按 0 记录", exception); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | private void finishJob(AiAutoTuneJob job, |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | |
| | | 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)); |
| | |
| | | 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()); |
| | |
| | | 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()); |
| | |
| | | } |
| | | } |
| | | |
| | | 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() { |
| | |
| | | } catch (Exception exception) { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | 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 null; |
| | | } |
| | | |
| | | private static class ApplyPersistenceResult { |
| | | private final List<AiAutoTuneChange> auditChanges; |
| | | private final boolean refreshConfigCache; |
| | | |
| | | private ApplyPersistenceResult(List<AiAutoTuneChange> auditChanges, boolean refreshConfigCache) { |
| | | this.auditChanges = auditChanges == null ? new ArrayList<>() : auditChanges; |
| | | this.refreshConfigCache = refreshConfigCache; |
| | | } |
| | | |
| | | public List<AiAutoTuneChange> getAuditChanges() { |
| | | return auditChanges; |
| | | } |
| | | |
| | | public boolean isRefreshConfigCache() { |
| | | return refreshConfigCache; |
| | | } |
| | | } |
| | | |
| | | private static class RollbackPersistenceResult { |
| | | private final List<AiAutoTuneChange> rollbackChanges; |
| | | private final boolean refreshConfigCache; |
| | | |
| | | private RollbackPersistenceResult(List<AiAutoTuneChange> rollbackChanges, boolean refreshConfigCache) { |
| | | this.rollbackChanges = rollbackChanges == null ? new ArrayList<>() : rollbackChanges; |
| | | this.refreshConfigCache = refreshConfigCache; |
| | | } |
| | | |
| | | public List<AiAutoTuneChange> getRollbackChanges() { |
| | | return rollbackChanges; |
| | | } |
| | | |
| | | public boolean isRefreshConfigCache() { |
| | | return refreshConfigCache; |
| | | } |
| | | } |
| | | |
| | | 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; |
| | |
| | | |
| | | 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) { |
| | |
| | | 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; |
| | | } |