diff --git a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanInspectServiceImpl.java b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanInspectServiceImpl.java index a64625f..cb5869a 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanInspectServiceImpl.java +++ b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanInspectServiceImpl.java @@ -265,12 +265,12 @@ public class DmsPlanInspectServiceImpl implements IDmsPlanInspectService } /** - * 定时任务:每天0点根据巡检计划生成巡检工单 + * 定时任务:每天凌晨1点一次性生成当天整天的巡检工单 * 使用Mapper替代JdbcTemplate,生成已完成状态工单,工单中所有设备默认正常 * 若有异常可在网页端维护工单明细中的设备状态 * cron表达式:秒 分 时 日 月 周 */ - @Scheduled(cron = "0 0 0 * * ?") // 原生产策略:每天 0 点执行 + @Scheduled(cron = "0 0 1 * * ?") // 生产策略:每天凌晨 1 点执行 // @Scheduled(cron = "0 * * * * ?") // 测试策略:每分钟执行一次 // @Transactional(rollbackFor = Exception.class) public void generateDailyInspectWorkOrders() { @@ -278,7 +278,8 @@ public class DmsPlanInspectServiceImpl implements IDmsPlanInspectService int count = 0; int skipCount = 0; try { - // 先把“到期的计划”全部拉出来,后续逐条处理。 + // 先把“今天整天内应生成”的计划全部拉出来,后续逐条处理。 + // 这里依赖SQL使用“小于次日0点”的上界,避免把次日00:00计划提前捞出来。 // 注意:这里不做事务包裹,避免某一条失败影响整批调度。 List plans = dmsPlanInspectMapper.selectPendingInspectPlans(); diff --git a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java index 3b50774..c60d143 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java +++ b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java @@ -12,6 +12,7 @@ import com.aucma.dms.mapper.DmsPlanMaintMapper; import com.aucma.dms.service.IDmsBillsMaintInstanceService; import com.aucma.dms.service.IDmsPlanMaintService; import com.aucma.quartz.util.CronUtils; +import org.quartz.CronExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +20,7 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -313,18 +315,19 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { } /** - * 定时任务:每天0点根据保养计划生成保养工单 + * 定时任务:每天凌晨1点一次性生成当天整天的保养工单 * 使用Mapper替代JdbcTemplate,生成已完成状态工单,工单中所有设备默认保养完成 * 若有异常可在网页端维护工单明细中的设备状态 * cron表达式:秒 分 时 日 月 周 */ - @Scheduled(cron = "0 0 0 * * ?") + @Scheduled(cron = "0 0 1 * * ?") public void generateDailyMaintWorkOrders() { log.info("========== 开始执行保养工单生成任务 =========="); int count = 0; int skipCount = 0; try { - // 通过Mapper查询今天需要执行的保养计划(maint_time <= 今天) + // 通过Mapper查询“今天整天内应生成”的保养计划。 + // 这里依赖SQL使用“小于次日0点”的上界,避免把次日00:00计划提前捞出来。 List plans = dmsPlanMaintMapper.selectPendingMaintPlans(); for (DmsPlanMaint plan : plans) { @@ -343,7 +346,8 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { if (insertRows > 0) { count++; log.info("已为保养计划[{}]生成已完成工单", planMaintCode); - // 更新计划的下次执行时间(根据cron表达式或默认+1天) + // 基于“当前计划时间”推进下次执行时间, + // 避免凌晨1点批量生成当天工单后,又把计划推进回当天稍晚时间,导致次日被重复捞取。 updateMaintPlanNextTime(plan); } else { skipCount++; @@ -372,7 +376,9 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { Date nextTime = null; String cronExpression = plan.getCronExpression(); if (cronExpression != null && !cronExpression.isEmpty()) { - nextTime = CronUtils.getNextExecution(cronExpression); + // 这里必须基于计划当前 maintTime 推进,而不能基于“当前系统时间”直接算下一次, + // 否则当天未来时点的计划会被错误推进到“今天稍后”,第二天凌晨仍会再次命中。 + nextTime = calculateNextMaintTime(cronExpression, plan.getMaintTime()); } if (nextTime == null) { log.warn("保养计划下次执行时间计算为null, 使用默认+1天 | planMaintId={}, planMaintCode={}, cronExpression={}", @@ -387,4 +393,25 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { e.getMessage(), e); } } + + /** + * 计算保养计划下次执行时间: + * 1) 基于计划当前 maintTime 推进; + * 2) 若算出的时间仍不晚于当前时间,则继续推进,直到晚于当前时间。 + */ + private Date calculateNextMaintTime(String cronExpression, Date currentMaintTime) { + try { + CronExpression cron = new CronExpression(cronExpression); + Date now = new Date(); + Date cursor = currentMaintTime != null ? currentMaintTime : now; + Date next = cron.getNextValidTimeAfter(cursor); + while (next != null && !next.after(now)) { + next = cron.getNextValidTimeAfter(next); + } + return next; + } catch (ParseException e) { + log.error("cron表达式非法,无法计算下次执行时间 | cronExpression={}, 异常信息: {}", cronExpression, e.getMessage(), e); + return null; + } + } } diff --git a/aucma-dms/src/main/resources/mapper/dms/DmsPlanInspectMapper.xml b/aucma-dms/src/main/resources/mapper/dms/DmsPlanInspectMapper.xml index 38d7c29..0301fa8 100644 --- a/aucma-dms/src/main/resources/mapper/dms/DmsPlanInspectMapper.xml +++ b/aucma-dms/src/main/resources/mapper/dms/DmsPlanInspectMapper.xml @@ -160,7 +160,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" a.cron_expression FROM dms_plan_inspect a WHERE (a.is_flag = '1' OR a.is_flag IS NULL) - AND a.plan_time <= TRUNC(SYSDATE) + 1 + + AND a.plan_time < TRUNC(SYSDATE) + 1 diff --git a/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml b/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml index 4725e61..0b6c8d3 100644 --- a/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml +++ b/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml @@ -234,7 +234,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" a.cron_expression FROM dms_plan_maint a WHERE (a.is_flag = 0 OR a.is_flag IS NULL) - AND a.maint_time <= TRUNC(SYSDATE) + 1 + + AND a.maint_time < TRUNC(SYSDATE) + 1