如何设计数据库周期账单与定时任务
cron 不是业务规则。定时任务只是把“应生成的一期”变成事实。
这页只讲一件事:每月房租为什么不会生成两次,也不会漏生成。
| 事故 | 根因 | 设计动作 |
| 每月房租生成两次。 | 任务超时重试,第一次其实已写入。 | 同一规则同一期必须有唯一 occurrence_key。 |
| 任务停三天,不知道补哪些。 | 只有 next_run_at,没有“应生成实例”。 | 先生成 occurrences,再补跑。 |
| 规则改金额后旧账解释不了。 | 账目没有规则版本快照。 | 每一期保存生成时的规则版本。 |
| 每月 31 日在 2 月生成错。 | 用固定秒数代替业务日历。 | 规则保存时区和月底策略。 |
| 分期提前结清后仍继续生成。 | 未来计划和已发生事实混在一起。 | 分期计划、应生成实例、真实账目分开。 |
判断:规则不是账目,任务不是事实。真正入账前,先有可审计的“本期应生成”。
| 问题 | 最低设计 |
| 用户的长期意图是什么? | recurring_rules 保存金额、账户、分类、周期、状态。 |
| 某一期是否应该生成? | recurring_occurrences 保存应生成实例。 |
| 同一期怎样防重复? | UNIQUE(rule_id, occurrence_key)。 |
| 本期按哪版规则生成? | occurrence 保存规则版本快照。 |
| 任务跑完怎么证明? | 任务记录、生成数量、失败数量、派生对账。 |
recurring_rules:
rule_id, ledger_id, amount_cents, account_id, category_id
recurrence_type, timezone, calendar_policy, status, version
recurring_occurrences:
occurrence_id, rule_id, occurrence_key, due_on
status, generated_entry_id, rule_version_snapshot
scheduled_job_runs:
job_run_id, job_name, started_at, finished_at
status, generated_count, failed_count, checkpoint
| 样本 | 通过标准 |
| 任务超时重试。 | 同一期只生成一条账。 |
| 任务停三天。 | 补跑候选清单准确,不靠猜。 |
| 每月 31 日规则。 | 2 月、闰年、大小月按业务策略生成。 |
| 规则改金额。 | 旧期、新期都能解释。 |
| 分期提前结清。 | 未来期不再生成,历史账目不被删。 |
记住:任务可以重复执行,事实不能重复产生。唯一键守住同一期,occurrence 守住补跑边界。