---
title: 自动缺口巡查重构方案
doc_type: feature-design
domain: data-sync
keywords:
  - 自动补录
  - 缺口巡查
  - startup sync
  - 数据自愈
summary: 定义把启动自检升级为运行时自动缺口巡查任务的目标口径，统一行情与公告的补录窗口和触发方式。
when_to_read:
  - 要重构启动补录、自动补偿或手动补数入口
  - 要确认行情和公告缺口巡查的正式窗口规则
read_next:
  - ../operations/startup-sync-self-check.md
  - ../business/data-fetch-tasks.md
  - ../business/notice-market-pipeline.md
---
# 自动缺口巡查重构方案

这篇文档定义“已确认、待实现”的目标口径：把当前偏启动期的补数能力升级成运行时自动缺口巡查任务。

如果你要看当前已经上线的行为，先读 [启动自检补数](../operations/startup-sync-self-check.md)。如果你要落实现方案，这篇文档就是后续代码和测试应收口的目标设计。

## 要解决的问题

当前补数链路有 3 个结构性问题：

- 自动补偿过度绑定“应用启动”这一时机，长驻进程修好配置后不一定能自行恢复。
- 行情和公告的历史窗口规则不统一，存在“最早 T 日前推 3 个开市日”和“空表最近 14 个开市日”两套口径。
- 正式产品语义仍然保留多条人工补数路径，和“系统应自动自愈”的目标冲突。

这次重构的目标只有一个：

`让系统自己判断是否需要补录，并在统一的运行时任务里自动完成。`

## 已确认的正式决策

### 1. 单一自动任务

- 保留一套 runtime-managed 任务实现。
- 任务键沿用 `startup.sync.self_check`，但正式语义升级为“自动缺口巡查”。
- 它不再被理解成“只能在启动时运行”的一次性自检。

### 2. 触发方式

- 应用启动后立即执行一次。
- 运行时每 `6` 小时自动巡查一次。
- 启动首轮失败时，当前进程内仍可保留有限恢复重试，但恢复逻辑必须复用同一套巡查实现。

### 3. 行情与公告分离

- 行情链和公告链独立判定、独立补录。
- 公告链失败不能阻断行情链自愈。
- 行情链失败也不能阻断公告链推进。

### 4. 单一命名契约

这次重构先统一命名，再改缺口判定。

以后同一条 Tushare 单日快照链路只保留一套正式名字，不再混用 `daily`、`provider-daily`、`stock-basic`、`provider 日线`、`统一日线` 这类抽象简称。

正式命名表如下：

| 语义 | 正式任务键 | 单日快照状态键 | 正式落库表 |
| --- | --- | --- | --- |
| 股票基本信息 + `daily_basic` 市值标签单日快照 | `tushare.stock_basic_daily_basic.sync` | `tushare.stock_basic_daily_basic.snapshot:<market>:<trade_date>` | `stock_basic_info` + `api_tushare_daily_basic` |
| `stk_factor_pro` 单日快照 | `tushare.stk_factor_pro.sync` | `tushare.stk_factor_pro.snapshot:<market>:<trade_date>` | `api_tushare_stk_factor_pro` |
| `limit_list_d` 单日快照 | `tushare.limit_list_d.sync` | `tushare.limit_list_d.snapshot:<market>:<trade_date>` | `tushare_limit_list` |
| `suspend_d` 单日快照 | `tushare.suspend_d.sync` | `tushare.suspend_d.snapshot:<market>:<trade_date>` | `tushare_suspend_d` |

配套硬规则：

- 文档、日志、任务通知、测试名和 Rust 枚举都使用这套正式名字。
- 不再把 `api_tushare_stk_factor_pro` 写成模糊的 `daily`。
- 不再把 `stock_basic_info + api_tushare_daily_basic` 写成模糊的 `stock basic`。
- 不再把 `api_tushare_stk_factor_pro / api_tushare_daily_basic / api_akshare_price_daily` 的统一查询层写成 `provider daily` 或“统一日线真值”；需要表达查询层时，明确写“多来源日线查询层”。
- 用户能看到的说明默认写“任务键 + API 名 + 落库表名”，不再靠缩写猜语义。

## 正式窗口规则

### 1. 行情巡查窗口

- 上界：`最新已收盘交易日`
- 下界：上界往前 `120` 个开市日
- 适用范围：
  - `tushare.trade_calendar.sync`
  - `tushare.stock_basic_daily_basic.sync`
  - `tushare.stk_factor_pro.sync`
  - `tushare.limit_list_d.sync`
  - `tushare.suspend_d.sync`

这里的关键变化是：

- 不再使用“最早 T 日前推 3 个开市日”。
- 不再使用“空表最近 14 个开市日”。
- 无论空表、半空表还是历史断档，行情链都统一在固定窗口内巡查缺口。

### 2. 公告巡查窗口

- 上界：当前自然日
- 下界：上界往前 `30` 个自然日
- 适用范围：
  - `cninfo.notice.sync`
  - `notice.t_date.backfill`
  - `notice.filter`

公告窗口按自然日而不是开市日处理，因为公告发布时间不以市场开闭市为边界。

## 巡查与补录规则

### 1. 只补缺口，不整窗重拉

- 自动任务先判断窗口内哪些日期或游标区间存在缺口。
- 只有缺口日期需要触发 provider 拉取。
- 已经 ready 的日期不应被重复整窗重刷。

### 2. 交易日历仍是行情链前置

- 行情链先确保本地交易日历能支撑窗口判定。
- `最新已收盘交易日` 继续由 `trade_calendar_utils::resolve_latest_completed_trade_date_for_datetime()` 解析。
- 行情窗口的 ready 判定继续以“目标单日快照是否真实落库”为准，而不是只看 `sync_status` 成功标记。

### 3. 公告链复用现有后处理

- 公告仍然先写 `api_cninfo_notice_raw`。
- 然后继续复用现有 `notice_pipeline`：
  - 公告投影
  - `trade_date` / T 日回填
  - `notice.filter`
- 这次重构不把 `notice.ai.analysis` 并回公告补录链。

## 产品语义调整

### 1. 取消正式人工补数语义

目标口径是：

- 正式产品不再把 `runTaskNow()`、`task-runner`、`start_history_sync()` 或显式窗口 CLI 作为数据补录的正常恢复路径。
- 页面、设置页和任务中心不再向普通用户暴露“手动补数”心智。
- 系统对外只保留“自动巡查并自动修复”的正式承诺。

这不等于仓库里永远不能保留内部测试工具，而是：

`这些工具不再构成正式业务恢复模型。`

### 2. 自动恢复承诺的边界

- 行情自动恢复窗口只承诺最近 `120` 个开市日。
- 公告自动恢复窗口只承诺最近 `30` 个自然日。
- 更早历史如果仍需补齐，应通过初始化包或独立历史准备流程解决，不再混入日常补录语义。

## 推荐实现拆分

### 方案 A：最小改造，推荐先落

- 复用 `run_startup_sync_self_check()` 作为主内核。
- 把内部窗口计算改成“行情 120 开市日 / 公告 30 自然日”。
- 在运行时增加一个 `6` 小时周期触发入口。
- 保留当前 `STARTUP / RECOVERY` 触发源，并新增定时巡查触发源或复用 `SCHEDULED`。

优点：

- 复用现有任务、账本、通知和可观测性，改动最小。

代价：

- 任务键仍沿用 `startup.sync.self_check`，命名会暂时带有历史痕迹。

### 方案 B：语义彻底重命名

- 新建专门的 runtime-managed 任务键，例如 `runtime.gap_patrol`。
- 启动、恢复和周期巡查全部迁到新任务键。
- 原 `startup.sync.self_check` 作为兼容壳或迁移掉。

优点：

- 语义更干净。

代价：

- 需要同步迁移任务中心、通知、日志、账本和文档，改面更大。

当前推荐先按方案 A 落地，再视稳定性决定是否重命名。

## 验收标准

- fresh install：没有人工干预时，系统能自动建立最近 `120` 个开市日的行情底座和最近 `30` 个自然日的公告底座。
- 长驻进程：应用不重启，配置修复后最多 `6` 小时内应自动进入下一轮巡查并恢复缺口。
- 分链隔离：CNInfo 异常时，行情链仍能继续巡查并补齐；Tushare 异常时，公告链仍能推进。
- 缺口判定：窗口内已 ready 的日期不会被整窗重刷；窗口内缺口日期会被准确补齐。
- 页面语义：普通用户前端不再提供正式“手动补数”操作。

## 实现注意点

- 现有 runbook 和业务盘点文档仍在描述当前实现；在代码完成前，不应把它们直接改写成新行为说明。
- 代码落地后，应同步更新：
  - `docs/operations/startup-sync-self-check.md`
  - `docs/business/data-fetch-tasks.md`
  - 相关功能测试文档
