diff --git a/src/main/java/com/project/AiEvaluatorApplication.java b/src/main/java/com/project/AiEvaluatorApplication.java index f00b458..4d1fd76 100644 --- a/src/main/java/com/project/AiEvaluatorApplication.java +++ b/src/main/java/com/project/AiEvaluatorApplication.java @@ -5,11 +5,13 @@ import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableAsync @MapperScan({ "com.project.*.mapper"}) +@EnableScheduling public class AiEvaluatorApplication { public static void main( String[] args ) { SpringApplication.run(AiEvaluatorApplication.class , args); diff --git a/src/main/java/com/project/base/config/ScheduledTaskProperties.java b/src/main/java/com/project/base/config/ScheduledTaskProperties.java new file mode 100644 index 0000000..f818c8f --- /dev/null +++ b/src/main/java/com/project/base/config/ScheduledTaskProperties.java @@ -0,0 +1,14 @@ +package com.project.base.config; + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "scheduled-task") +public class ScheduledTaskProperties { + private String owner = "dev"; + +} diff --git a/src/main/java/com/project/question/config/QuestionReplenishProperties.java b/src/main/java/com/project/question/config/QuestionReplenishProperties.java new file mode 100644 index 0000000..fe1fba6 --- /dev/null +++ b/src/main/java/com/project/question/config/QuestionReplenishProperties.java @@ -0,0 +1,19 @@ +package com.project.question.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "question.replenish-inventory") +public class QuestionReplenishProperties { + // 闲时开始 + private int offPeakStart = 21; + // 闲时结束 + private int offPeakEnd = 8; + // 忙时间隔 + private int busyInterval = 30; + // 闲时间隔 + private int offPeakInterval = 180; +} \ No newline at end of file diff --git a/src/main/java/com/project/question/domain/job/InventoryCheckJob.java b/src/main/java/com/project/question/domain/job/InventoryCheckJob.java new file mode 100644 index 0000000..53c0b39 --- /dev/null +++ b/src/main/java/com/project/question/domain/job/InventoryCheckJob.java @@ -0,0 +1,104 @@ +package com.project.question.domain.job; + +import com.project.base.config.ScheduledTaskProperties; +import com.project.question.config.QuestionReplenishProperties; +import com.project.question.domain.service.QuestionInventoryDomainService; +import com.project.task.domain.entity.TaskEntity; +import com.project.task.mapper.TaskMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalTime; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicLong; + +@Component +@Slf4j +public class InventoryCheckJob { + + @Autowired + private ScheduledTaskProperties taskProps; // 基础配置:判定 owner + + @Autowired + private QuestionReplenishProperties replenishProps; // 业务配置:判定频率 + + @Autowired + private TaskMapper taskMapper; + + @Autowired + private QuestionInventoryDomainService questionInventoryDomainService; + + @Resource(name = "questionGenExecutor") + private Executor questionGenExecutor; + + // 记录累计经过的总分钟数 + private final AtomicLong elapsedMinutes = new AtomicLong(0); + + /** + * 每 10 分钟心跳一次 + */ + @Scheduled(fixedRate = 600000) + public void run() { + + if ("dev".equalsIgnoreCase(taskProps.getOwner())) { + return; + } + + long currentTotalMinutes = elapsedMinutes.addAndGet(10); + int requiredInterval = getRequiredInterval(); + + + if (currentTotalMinutes % requiredInterval != 0) { + return; + } + + log.info(">>> [定时补库] 周期命中。当前时间段策略: {}min/次, 开始扫描任务", requiredInterval); + + List activeTasks = taskMapper.selectList( + new LambdaQueryWrapper() + .select(TaskEntity::getId) // 保护 Inode: 只查 ID + .gt(TaskEntity::getEndTime, new Date()) + .eq(TaskEntity::getDeleted, false) + ); + + if (activeTasks.isEmpty()) return; + + activeTasks.forEach(task -> + CompletableFuture.runAsync(() -> { + try { + questionInventoryDomainService.checkAndReplenish(task.getId()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, questionGenExecutor).exceptionally(ex -> { + log.error(">>> [定时补库] 任务[{}]检查失败", task.getId(), ex); + return null; + }) + ); + } + + /** + * 判定忙闲时逻辑 + */ + private int getRequiredInterval() { + int hour = LocalTime.now().getHour(); + int start = replenishProps.getOffPeakStart(); + int end = replenishProps.getOffPeakEnd(); + + boolean isOffPeak; + if (start > end) { // 跨天情况,如 23点到次日8点 + isOffPeak = (hour >= start || hour < end); + } else { // 当天情况,如 1点到5点 + isOffPeak = (hour >= start && hour < end); + } + + return isOffPeak ? replenishProps.getOffPeakInterval() : replenishProps.getBusyInterval(); + } +} \ No newline at end of file