Kaynağa Gözat

fix(reminder): 限制提醒窗口与单次发送量,避免历史积压批量误发

- 仅提醒今日到期 + 近3天逾期(production存在2026-08-18历史积压与报告叙述类任务)
- 单次调度最多发送50条,防止选数异常形成短信轰炸
Xiaogang Liao 3 gün önce
ebeveyn
işleme
fc48163e4e

+ 21 - 1
cfc-backend/src/main/java/com/etotem/cfc/task/TaskReminderScheduledTask.java

@@ -32,6 +32,15 @@ public class TaskReminderScheduledTask {
     private static final String TYPE = "task_reminder";
     private static final String REF_TYPE = "task";
 
+    /**
+     * 逾期宽限天数:只提醒"今日到期 + 近 N 天逾期"的任务。
+     * 不加下限时,生产库有 2026-08-18 的历史积压与报告叙述类任务会被一次性批量误发。
+     */
+    private static final int OVERDUE_GRACE_DAYS = 3;
+
+    /** 单次调度最多发送条数,避免选数异常时形成短信轰炸 */
+    private static final int MAX_SEND_PER_RUN = 50;
+
     @Resource
     private TaskMapper taskMapper;
 
@@ -63,11 +72,18 @@ public class TaskReminderScheduledTask {
             Date dayEnd = endCal.getTime();
 
             // 今日到期 + 已逾期,均为 pending(未打卡/未审核通过)
+            // 提醒下限 = 今日0点 - 宽限天数,剔除早已废弃的历史积压
+            Calendar floorCal = Calendar.getInstance();
+            floorCal.setTime(dayStart);
+            floorCal.add(Calendar.DAY_OF_MONTH, -OVERDUE_GRACE_DAYS);
+            Date remindFloor = floorCal.getTime();
+
             List<Task> tasks = taskMapper.selectList(new LambdaQueryWrapper<Task>()
                 .in(Task::getStatus, "pending", "pending_review")
                 .isNotNull(Task::getDeadline)
                 .isNotNull(Task::getFamilyId)
-                .le(Task::getDeadline, dayEnd)
+                .ge(Task::getDeadline, remindFloor)
+                .lt(Task::getDeadline, dayEnd)
                 .orderByAsc(Task::getDeadline));
             if (tasks == null || tasks.isEmpty()) {
                 log.info("任务提醒:今日无待打卡任务");
@@ -76,6 +92,10 @@ public class TaskReminderScheduledTask {
 
             int sent = 0;
             for (Task task : tasks) {
+                if (sent >= MAX_SEND_PER_RUN) {
+                    log.warn("任务提醒达到单次上限{}条,本轮剩余{}条顺延下次", MAX_SEND_PER_RUN, tasks.size() - sent);
+                    break;
+                }
                 try {
                     Long userId = resolveReceiver(task);
                     if (userId == null) continue;