# 应到驱动差缺统计重构 · 实现总结(备忘录) > 完成日期:2026-08-03 > 分支:`refactor/undelivered-by-expected` > 关联文档:`docs/2026-08-02-未到差缺统计逻辑现状梳理.md`、`docs/2026-08-03-应到驱动差缺统计重构-design.md` > 状态:已完成并验证,待提交 --- ## 一、背景 原差缺统计为**实到驱动**:以目标日实到扫描为锚点 → 反推交接批次 → 展开批次全量应到 → 逐单比对。 业务部门日常以**应到数据**为依据,且应到任务可能被**提前提交 1~2 天**(韵达固定提前 1 天),导致: - 完全按应到统计会出现假差缺; - 实到驱动会把历史批次混入当天报表(1 单命中即整批展开)。 重构目标:改为**应到驱动**,并用「出库日」作为批次归属日,自动吸收提前提交。 ## 二、核心口径 - **批次归属日 = 批次内运单「出库时间」日期众数,并列取较早**(字段:`expected_record.batch_out_date`)。 - **统计 D 日差缺 = 取所有 `batch_out_date = D` 的应到批次**,展开全量应到 → 查全量实到 → 逐运单比对。 - 无论批次在 D / D-1 / D-2 哪天下载(`business_date` 为何),只要出库日 = D 即纳入 D 日统计。 ## 三、数据实证(7 月全量) | 站点 | 下载日=出库大头日 | 出库大头日=下载日+1 | |------|-----------------|-------------------| | 顺心 | 83/83(100%) | 0 | | 中通 | 28/30(93%) | 2(`071402`、`071902`) | | 韵达 | 0 | 29/29(100%,固定提前) | | 安能 | 31/31(100%) | 0 | 关键结论: - **出库大头日 ≈ 实到峰值日**(154/158 一致,97.5%)——"出库日"基本等于"这批货实际到的那天"。 - "提前提交"的批次(中通 `071402` 出库 7/15、`071902` 出库 7/20)在出库时间上如实体现真实归属日。 - 四站应到文件的「出库时间」字段非空率 100%,且已完整保留在 PG `expected_record.raw` JSONB 中。 ## 四、代码改动 ### 4.1 数据层 - `schema.sql`:`expected_record` 新增 `out_date DATE`(运单出库日)、`batch_out_date DATE`(批次归属日)+ `idx_expected_out_date` / `idx_expected_batch_out_date` 索引。 - `store.py`: - `_ingest_expected`:解析「出库时间」写 `out_date`;按交接单号聚合出库日众数(并列取较早)写 `batch_out_date`。 - 新增 `_batch_out_date_map()` 辅助函数。 - 新增 `backfill_out_date()` + CLI 子命令 `backfill-out-date`,历史数据一次性回填。 ### 4.2 比对层 - `db_compare.py`: - 新增 `compare_site_outdate(site, target_date)`:应到驱动入口,`WHERE batch_out_date = target_date` 取批次 → 展开 → 比对。 - 保留 `compare_site_date()`(实到驱动)作对照,不删除。 - `_target_date_for()` 改为默认取今天(不再依赖 actual_offset)。 - `build_full_report()` 改用 `compare_site_outdate`。 - `runtime.py`:`_site_undelivered_handler` 切到应到驱动,锚点日期默认今天。 - `cli/server.py`:`POST /compare` 切到应到驱动。 ## 五、实施与验证 ### 5.1 数据迁移 ``` python -m inbound_verify.store init # 建表/补列(幂等) python -m inbound_verify.store backfill-out-date # 历史回填 ``` 回填结果: - `out_date`:顺心 3290 / 中通 5419 / 韵达 1640 / 安能 3459 条,共 13808 条。 - `batch_out_date`:顺心 93 / 中通 35 / 韵达 32 / 安能 35 个批次。 - 抽样核对 PG `out_date` vs 归档 Excel 出库日一致率 96~100%。 ### 5.2 批次归属验证 | 批次 | 下载日 | batch_out_date | 预期 | |------|--------|----------------|------| | 中通 `...071401` | 7/14 | 7/14 | 正常 | | 中通 `...071402` | 7/14 | **7/15** | 提前提交归位 | | 中通 `...071901` | 7/19 | 7/19 | 正常 | | 中通 `...071902` | 7/19 | **7/20** | 提前提交归位 | | 韵达 `...07312001` | 7/31 | 8/1 | 固定 +1 | | 顺心/安能 | — | = 下载日 | 无扰动 | ### 5.3 回溯对照(7/02~7/31) 新应到驱动 vs 旧实到驱动,差异方向符合设计: - 旧驱动混入历史批次(如中通 7/12 旧 236 件 vs 新 11 件;顺心 7/13 旧 59 vs 新 1)。 - 新驱动只统计出库日=当天批次,数字更聚焦。 - 个别日期新驱动未到偏大(如韵达 7/03、安能 7/29),属"当天出库、次日扫描"的真实差缺口径。 ### 5.4 接口联调(真实后端) | 用例 | 结果 | |------|------| | `POST /compare` 韵达 2026-08-02 | 批次 1 个(`...08012001`),差缺 2 件/2 单(`988350756`、`988415586`),历史批次不再混入 | | `POST /compare` 中通 2026-07-15 | 提前提交批次 `...071402` 正确归位到 7/15 | | `__compare__/compare` 全站汇总 2026-08-01 | 顺心 17 件 / 中通 23 件 / 韵达 0 件 / 安能 0 件,合计 40 件,报表正常生成 | ## 六、待确认 / 遗留事项 - `out_date` / `batch_out_date` 依赖站点「出库时间」字段语义(当前按"货物实际发出时间"处理,已与业务确认)。 - 批次内出库日并列众数取较早(已确认)。 - "无应到"时报表直接写(如实呈现,无特殊文案,已确认)。 - 实到驱动入口保留作对照(已确认)。 - `docs/2026-08-02-未到统计重构讨论纪要与下一步.md` 中记录的 18:28 直入入库等链路疑点,本重构未处理,留待后续。 ## 七、附:涉及文件 | 文件 | 说明 | |------|------| | `schema.sql` | 表结构:新增 `out_date` / `batch_out_date` | | `inbound_verify/store.py` | 入库解析 + 历史回填 | | `inbound_verify/db_compare.py` | 应到驱动比对入口(保留实到驱动对照) | | `inbound_verify/runtime.py` | 未到任务切到应到驱动 | | `inbound_verify/cli/server.py` | `/compare` API 切到应到驱动 |