Files
InboundVerify/docs/站点统计逻辑审查报告.md
Misaka_Company f61f52bcbe docs: align docs with post-restructure package layout
Update README tree (compare.py + domain.py), CLAUDE.md module refs, and the three docs/ deep-dives to the current inbound_verify package (compare/domain/sites/cli). Correct the statistics review report's methodology to the current 口径 (应到=交接件数, 实到=直接数单号去重, 未到=应到−实到) per compare.py docstring; remove ghost-script references in the CDP guide.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 10:12:40 +08:00

9.2 KiB
Raw Blame History

后端各站点「应到件数 / 已到件数」统计逻辑审查报告

审查范围:InboundVerify 后端 核心模块:compare.py(比对统计)、domain.py(站点/文件/列配置)、runtime.py(调度/下载路由)、sites/*.py(各站数据采集)、cli/server.py(对外接口) 审查重点:每个站点的「应到件数」和「已到件数」如何统计、数据来源、口径与潜在歧义。


一、总体架构(两层)

统计逻辑分两层,前端看到的「应到/已到/未到」计数最终都来自第二层,且只在跑比对时才生成。

模块 职责 产物
① 数据采集层 sites/{顺心,中通,韵达,安能,百世}.py 用 Playwright安能用 CDP登录各承运商后台导出 Excel 到 downloads/ downloads/<站>-应到货物数据.xlsxdownloads/<站>-实到货物数据.xlsxdownloads/<站>-未到数据.xlsx
② 比对统计层 compare.py(站点/文件/列配置取自 domain.py downloads/ 源文件,两表比对,算出应到/实到/未到件数并出报表 output/应到未到数据.xlsx(汇总+各站明细)

调度入口:runtime.pyTASK_HANDLERS

  • 4 站:expected 下载 + actual 下载 + undelivered(先下应到+实到,再调 write_site_file 算出未到)。
  • 百世:只有 undelivered(直接导「当日未扫」明细,无应到/实到两份基表)。
  • __compare__(菜单[9]):调 compare.main(),用 downloads/ 现有文件生成全站汇总报表。

二、核心统计公式4 站统一口径)

compare.process(name) 是 4 站统计的唯一实现:

应到(按运单号去重,保留首条):
  应到件数 N  = 应到数据中的「交接件数」   # 注意:用「交接件数」,不是「录单件数」
              (录单件数只是该单号总录单量,并非真正到站量)
  应到件数   += N                          # 站点级应到 = Σ N

实到(直接数,不再由「应到−未到」倒推):
  按「运单号」把实到表的「单号/子单号/扫描单号」分组,组内去重计数
  → 每个运单的实到件数;站点级实到 = Σ 各运单实到件数

未到:
  未到件数   = max(0, 应到件数  实到件数)   # 站点级
  未到率     = 未到件数 ÷ 应到件数
  短少运单   = 实到件数 < 应到件数 N 的运单
              (未到明细 downloads/<站>-未到数据.xlsx 只列这些短少运单,
               每行:交接单号 | 运单号 | 总件数(=N) | 已到单号1 | 已到单号2 | …)

关键事实实到件数 是直接数实到表「单号」、按运单号分组去重得到的(不再由「应到−未到」倒推未到件数 = 应到件数 实到件数。前提是实到单号能正确按运单号分组(分组规则见下表各站 arrived_pieces_*)。


三、各站点统计明细

站点 应到件数来源 实到件数来源 单号→运单号 分组规则(arrived_pieces_* 源文件(downloads/
中通 中通-应到货物数据.xlsx 的「交接件数」 实到表单号去重(直接数) 实到运单号是复合串 = 运单号 + 总数(4) + 顺序(4);按 v[:-8] 归并到应到运单号,每条复合串即 1 件 中通-应到货物数据.xlsx / 中通-实到货物数据.xlsx
顺心 顺心-应到货物数据.xlsx 的「交接件数」 实到表单号去重(直接数) 运单号分组,子单号=每件(一件一个子单号) 顺心-应到货物数据.xlsx / 顺心-实到货物数据.xlsx
韵达 韵达-应到货物数据.xlsx 的「交接件数」 实到表单号去重(直接数) 主单号分组,子单号=每件 韵达-应到货物数据.xlsx / 韵达-实到货物数据.xlsx
安能 安能-应到货物数据.xlsx 的「交接件数」 实到表单号去重(直接数) 所属单号分组,扫描单号=每件 安能-应到货物数据.xlsx / 安能-实到货物数据.xlsx
百世 (站点只给未到) 无(显示「—」) 不适用(无实到基表) 百世-应到未到货物数据.xlsx= 当日未扫明细,本身就是未到结果)

4 站合计/图表口径:compare.build_summary 只累加 4 站(应到−实到口径),百世不计入合计(无应到基数)。百世在表中单列,未到件数 = 其明细行数。


四、百世的特殊口径(务必注意)

百世是唯一「无应到/实到基数」的站点:

  • 它的数据来自后台「扫描综合查询 → 到/接件扫描 → 当日 → 未扫」,导出即「当日未扫」明细(sites/baishi.py)。
  • 因此 process_baishi() 只能给 未到件数 = 行数应到件数 = None已到件数 = None、完全/部分未到 = None。
  • 报表里百世的应到/已到列显示「—」,未到率无法计算。
  • 含义:百世统计的是「今天还没扫到的件」,不是「相对应到总量的缺件率」。与 4 站口径不可直接相加比较。

五、数据日期与偏移(潜在口径不一致风险)

runtime._record_business_date 在下载成功后把业务日期写进状态库:

  • 业务日期 = 下载当天 日期偏移
  • 各站 expected_offset / actual_offset 独立配置(前端 /config 可改;百世偏移恒 0锁定当天
  • 韵达默认 expected_offset=1(取前一日应到)。

风险点4 站的「应到」和「实到」是两次独立下载,各自可能带不同偏移。若 expected_offset ≠ actual_offset,则「应到件数」和「已到件数」来自不同业务日期,比对会变成「拿昨天的应到对比今天的实到」,未到率失真。报表「数据日期」列分别标注各站,但汇总合计不标注,肉眼难发现。


六、统计结果如何暴露给前端

接口 返回内容 是否含应到/已到计数
GET /status 各站 login_state + expected/actual/undelivered_ready + business_date + worker_ready 不含计数,只给就绪标志
GET /report FileResponse(output/应到未到数据.xlsx) 计数只在 xlsx 里
GET /data/{filename} 下载 downloads/ 下某源文件 原始数据,非统计值

结论:后端没有把应到/已到件数以 JSON 形式实时返回前端。计数仅物化在 output/应到未到数据.xlsx。任何前端界面显示的应到/已到数字,都是解析这份 xlsx 得到的——即**「截至上次跑比对」的快照**,不是实时值。


七、潜在歧义与风险点(审查结论)

  1. 实到依赖单号→运单号分组正确:实到件数靠把实到表「单号」按运单号分组去重计数(arrived_pieces_*)。一旦某站单号格式与分组规则不匹配(如复合串切分错),该件归不到对应运单 → 实到被低估、未到率虚高。规则硬编码,承运商改版号段即失准。
  2. 应到件数依赖「交接件数」:若应到数据某运单 交接件数 缺失/为 0/非数字,该运单被跳过,既不计入应到也不计入未到 → 静默漏统(应到总量被低估;该运单即便出现在实到中也因不在应到循环而无处抵扣)。
  3. 应到按运单号去重keep first:同一运单多条交接记录只取首条 录单件数。若重复行的件数不同,取首条,可能与实际不符。
  4. 百世不可并入合计4 站合计的「已到总件数」不含百世;跨站看「已到」时别把百世当成有应到基数的站。
  5. 应到/实到业务日期可能错位(见第五节):两表不同步下载或偏移不一致时,比对口径失真。
  6. 计数是比对产物,非实时:前端若要「实时件数」需先触发比对任务;/statusready 仅表示「下载成功」,不代表「已比对出数」。
  7. 子单号是程序现拼的4 站缺件的「子单号/扫描单号」由 code_* 按各站编号规则生成(code_shunxin 等),并非实到原始记录——明细里的缺件单号是推算值,用于人工核对,不是系统回执。

八、关键文件索引

文件 角色
compare.py 统计核心:process()4站比对process_baishi()(百世)、build_summary()(汇总报表)
domain.py 站点/文件名/列映射配置:STATIONS(各站解析配置)、arrived_pieces_*(实到单号→运单号分组)、_site_cfg
runtime.py TASK_HANDLERS(下载/比对路由)、_site_undelivered_handler4站先下应到+实到再算未到)、_record_business_date(业务日期/偏移写入)
sites/{中通,顺心,韵达,安能}.py 各站 expected_download / actual_downloadPlaywright/CDP 导出源表)
sites/baishi.py baishi_download_undelivered_data(直接导「当日未扫」)
state_store.py site_statusready/business_datesite_configoffset/scheduleget_offset
cli/server.py /status/report/data/{filename} 接口