package com.zy.ai.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.zy.ai.domain.autotune.AutoTuneJobStatus;
|
import com.zy.ai.domain.autotune.AutoTuneTriggerType;
|
import com.zy.ai.entity.AiAutoTuneJob;
|
import com.zy.ai.service.AiAutoTuneJobService;
|
import com.zy.ai.service.AutoTuneAgentService;
|
import com.zy.ai.service.AutoTuneCoordinatorService;
|
import com.zy.asrs.entity.WrkMast;
|
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.OperateLog;
|
import com.zy.system.service.ConfigService;
|
import com.zy.system.service.OperateLogService;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import java.util.Arrays;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.UUID;
|
|
@Slf4j
|
@Service("autoTuneCoordinatorService")
|
@RequiredArgsConstructor
|
public class AutoTuneCoordinatorServiceImpl implements AutoTuneCoordinatorService {
|
|
private static final String CONFIG_ENABLED = "aiAutoTuneEnabled";
|
private static final String CONFIG_INTERVAL_MINUTES = "aiAutoTuneIntervalMinutes";
|
private static final String DEFAULT_ENABLED = "N";
|
private static final int DEFAULT_INTERVAL_MINUTES = 10;
|
private static final int MIN_INTERVAL_MINUTES = 5;
|
private static final int MAX_INTERVAL_MINUTES = 60;
|
private static final int RUNNING_LOCK_SECONDS = 20 * 60;
|
private static final long SYSTEM_USER_ID = 9527L;
|
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
|
);
|
|
private final ConfigService configService;
|
private final WrkMastService wrkMastService;
|
private final AiAutoTuneJobService aiAutoTuneJobService;
|
private final AutoTuneAgentService autoTuneAgentService;
|
private final RedisUtil redisUtil;
|
private final OperateLogService operateLogService;
|
|
@Override
|
public AutoTuneCoordinatorResult runAutoTuneIfEligible() {
|
if (!isEnabled()) {
|
return AutoTuneCoordinatorResult.skipped("disabled");
|
}
|
|
int intervalMinutes = resolveIntervalMinutes();
|
if (!hasActiveTasks()) {
|
return AutoTuneCoordinatorResult.skipped("no_active_tasks");
|
}
|
if (isLastTriggerGuardActive()) {
|
return AutoTuneCoordinatorResult.skipped("last_trigger_guard_active");
|
}
|
|
AiAutoTuneJob latestSuccessfulJob = latestSuccessfulAutoJob();
|
if (!isIntervalReached(latestSuccessfulJob, intervalMinutes)) {
|
return AutoTuneCoordinatorResult.skipped("interval_not_reached");
|
}
|
|
String lockKey = RedisKeyType.AI_AUTO_TUNE_RUNNING_LOCK.key;
|
String lockToken = UUID.randomUUID().toString();
|
if (!redisUtil.trySetStringIfAbsent(lockKey, lockToken, RUNNING_LOCK_SECONDS)) {
|
return AutoTuneCoordinatorResult.skipped("running_lock_not_acquired");
|
}
|
|
AutoTuneAgentService.AutoTuneAgentResult agentResult = null;
|
markLastTriggerGuard(intervalMinutes);
|
try {
|
agentResult = autoTuneAgentService.runAutoTune(AutoTuneTriggerType.AUTO.getCode());
|
safeWriteOperateLog(agentResult);
|
return AutoTuneCoordinatorResult.triggered(agentResult);
|
} catch (Exception exception) {
|
log.error("Auto tune coordinator failed to run agent", exception);
|
agentResult = failedAgentResult(exception);
|
safeWriteOperateLog(agentResult);
|
return AutoTuneCoordinatorResult.triggered(agentResult);
|
} finally {
|
redisUtil.compareAndDelete(lockKey, lockToken);
|
}
|
}
|
|
private boolean isEnabled() {
|
String enabled = configService.getConfigValue(CONFIG_ENABLED, DEFAULT_ENABLED);
|
if (enabled == null) {
|
return false;
|
}
|
String normalized = enabled.trim();
|
return "Y".equalsIgnoreCase(normalized)
|
|| "true".equalsIgnoreCase(normalized)
|
|| "1".equals(normalized);
|
}
|
|
private int resolveIntervalMinutes() {
|
String value = configService.getConfigValue(CONFIG_INTERVAL_MINUTES, String.valueOf(DEFAULT_INTERVAL_MINUTES));
|
try {
|
int intervalMinutes = Integer.parseInt(value.trim());
|
if (intervalMinutes < MIN_INTERVAL_MINUTES || intervalMinutes > MAX_INTERVAL_MINUTES) {
|
return DEFAULT_INTERVAL_MINUTES;
|
}
|
return intervalMinutes;
|
} catch (Exception exception) {
|
return DEFAULT_INTERVAL_MINUTES;
|
}
|
}
|
|
private boolean hasActiveTasks() {
|
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;
|
}
|
|
private boolean isLastTriggerGuardActive() {
|
Object guardValue = redisUtil.get(RedisKeyType.AI_AUTO_TUNE_LAST_TRIGGER_GUARD.key);
|
return guardValue != null;
|
}
|
|
private AiAutoTuneJob latestSuccessfulAutoJob() {
|
QueryWrapper<AiAutoTuneJob> queryWrapper = new QueryWrapper<>();
|
queryWrapper.eq("status", AutoTuneJobStatus.SUCCESS.getCode());
|
queryWrapper.eq("trigger_type", AutoTuneTriggerType.AUTO.getCode());
|
queryWrapper.last("order by coalesce(finish_time, create_time) desc limit 1");
|
List<AiAutoTuneJob> jobs = aiAutoTuneJobService.list(queryWrapper);
|
if (jobs == null || jobs.isEmpty()) {
|
return null;
|
}
|
return jobs.get(0);
|
}
|
|
private boolean isIntervalReached(AiAutoTuneJob latestSuccessfulJob, int intervalMinutes) {
|
if (latestSuccessfulJob == null) {
|
return true;
|
}
|
Date latestFinishTime = latestSuccessfulJob.getFinishTime();
|
if (latestFinishTime == null) {
|
latestFinishTime = latestSuccessfulJob.getCreateTime();
|
}
|
if (latestFinishTime == null) {
|
return true;
|
}
|
long intervalMillis = intervalMinutes * 60L * 1000L;
|
return System.currentTimeMillis() - latestFinishTime.getTime() >= intervalMillis;
|
}
|
|
private void markLastTriggerGuard(int intervalMinutes) {
|
long expireSeconds = intervalMinutes * 60L;
|
redisUtil.set(RedisKeyType.AI_AUTO_TUNE_LAST_TRIGGER_GUARD.key,
|
String.valueOf(System.currentTimeMillis()), expireSeconds);
|
}
|
|
private AutoTuneAgentService.AutoTuneAgentResult failedAgentResult(Exception exception) {
|
AutoTuneAgentService.AutoTuneAgentResult result = new AutoTuneAgentService.AutoTuneAgentResult();
|
result.setSuccess(false);
|
result.setTriggerType(AutoTuneTriggerType.AUTO.getCode());
|
result.setSummary("自动调参后台任务执行异常: " + exception.getMessage());
|
result.setToolCallCount(0);
|
result.setLlmCallCount(0);
|
result.setPromptTokens(0L);
|
result.setCompletionTokens(0L);
|
result.setTotalTokens(0L);
|
result.setMaxRoundsReached(false);
|
return result;
|
}
|
|
private void safeWriteOperateLog(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
try {
|
writeOperateLog(agentResult);
|
} catch (Exception exception) {
|
log.warn("Auto tune coordinator failed to write operate log", exception);
|
}
|
}
|
|
private void writeOperateLog(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
if (agentResult == null) {
|
return;
|
}
|
OperateLog operateLog = new OperateLog();
|
operateLog.setAction("ai_auto_tune_background_scheduler");
|
operateLog.setUserId(SYSTEM_USER_ID);
|
operateLog.setIp("system");
|
operateLog.setRequest(JSON.toJSONString(buildRequestSummary(agentResult)));
|
operateLog.setResponse(JSON.toJSONString(buildResponseSummary(agentResult)));
|
operateLog.setCreateTime(new Date());
|
operateLogService.save(operateLog);
|
}
|
|
private Map<String, Object> buildRequestSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
Map<String, Object> request = new LinkedHashMap<>();
|
request.put("trigger", agentResult.getTriggerType());
|
return request;
|
}
|
|
private Map<String, Object> buildResponseSummary(AutoTuneAgentService.AutoTuneAgentResult agentResult) {
|
Map<String, Object> response = new LinkedHashMap<>();
|
response.put("success", agentResult.getSuccess());
|
response.put("summary", agentResult.getSummary());
|
response.put("toolCallCount", agentResult.getToolCallCount());
|
response.put("llmCallCount", agentResult.getLlmCallCount());
|
response.put("promptTokens", agentResult.getPromptTokens());
|
response.put("completionTokens", agentResult.getCompletionTokens());
|
response.put("totalTokens", agentResult.getTotalTokens());
|
response.put("maxRoundsReached", agentResult.getMaxRoundsReached());
|
return response;
|
}
|
}
|