Files
SubsTracker/tests
wangwangit 0bb7e7670d refactor(scheduler): v3 重写 - TZ 感知 + 多规则 + 结构化日志(修复 #91/#52/#166)
src/services/notify/reminder-engine.js(新):
  - 纯函数 shouldFire(rule, ctx)
  - 三种 rule type 显式分支(before_expiry / on_expiry / after_expiry)
  - 24 条表驱动单测覆盖各种边界

src/services/scheduler.js(重写):
1. 时区基准统一:通过 getNowInTimezone(config.TIMEZONE) 取用户 TZ 下
   hourString,与 NOTIFICATION_HOURS 比对(修复 #91 / #52 根因——
   v2 把 UTC 小时当作"用户本地小时")
2. 多提醒规则:从 reminders.repo 加载每订阅的规则数组,逐条调
   reminder-engine.shouldFire;老订阅没有规则时现场用
   legacyFieldToRule 转一条
3. 去重粒度:dedup key = notify_dedupe:{subId}:{ruleId}:{ymdh-local}
   不再让一条订阅的多规则相互打架
4. 结构化日志:
   - 每次执行写 sched_log:{iso}(含命中/去重/发送/续订统计 + 候选明细)
   - 每条通知发送(成功/失败)写 notify_log(dispatch.js 自动落库)
5. 用户日历"剩余天数"基于 getDaysBetween(now, expiry, tz),
   修复 #166 跨日界场景

测试 tests/services/scheduler.test.js 7 条覆盖:
- 场景 1:UTC 0点 + 北京 8 点 + 配置 [08] → 发送
- 场景 2:同上配 [00] → 跳过 inWindow=false
- 场景 3:4 条规则订阅(7/3/1/0),距 3 天 → 仅命中 value=3
- 场景 4:同 sub+rule+ymdh 重复调用 → 去重 dedupedCount=1
- 自动续订:cycle 模式补齐 + auto 支付记录
- 写 sched_log
- 成功发送时写 notify_log

总计 150 条测试全绿;wrangler dry-run 415 KiB / gzip 86 KiB。

Refs Task 6 of refactor/v3-product-grade plan.
2026-05-24 18:11:47 +08:00
..