paper-follower/app/cli.py(already_done_today 第 534-553 行、cmd_health 第 556-666 行)\+ install_task.bat / run_daily.bat
如果只注册一个时间点,电脑那时没开机就永远不跑。但多注册几个触发点,
又可能重复执行、重复花钱。
多触发点 + 守卫(install_task.bat 注册早/晚/开机后三个任务;
每次执行先问守卫):
def already_done_today() -> tuple[bool, str]:
"""今天是否**完整**跑过一轮,返回 (是否完成, 说明)。
"完整" = 抓取和日报两件都做了。只做了一半(比如抓完中途挂了)不算完成 ——
否则晚上那次重试就被自己挡掉了,而那正是最需要它的时候。
判断用的是两处独立证据,不猜:
- runs 表今天的记录 → 抓取成功了
- 今天的 digest 文件 → 日报生成成功了
"""
fetched = _fetched_today()
digested = _digested_today()
if fetched and digested:
return True, "今天已完成(抓取 + 日报都在)"
missing = []
if not fetched:
missing.append("抓取")
if not digested:
missing.append("日报")
return False, "今天还缺:" + "、".join(missing)
可观测性(为什么需要 health,第 556-561 行的注释):
"""体检:一条命令回答「它到底在正常跑吗」。
存在的理由很具体:定时任务没生效时,系统不会有任何报错 ——
它只是安静地什么都不做。没有这个命令,只能靠"感觉最近没收到日报"来发现。"""
--skip-if-done + already_done_today(),重复触发无害schtasks(平台专属);守卫逻辑本身是跨平台的tests/test_daily_guard.py(258 行,覆盖守卫四种组合、坏库、bat 约束)idempotent-daily 小工具:should_run(state_files, db_marks) -> (bool, reason)systemd timer)的对应实现,扩大适用面解决的是"进程没起来时也要能补跑"的问题——两者互补
第 12 课(工程化)、第 17 课(数据流水线)