- site_anneng.py: set_scan_date 改用 execCommand 全选替换+回车(修复 Ant DatePicker 受控组件未写入导致实到下成当天); wait_scan_form_ready 增加 settle 与 DatePicker 预热,消除首设竞态 - expected_undelivered.py: 应到口径改交接件数、实到改单号去重、未到明细列已到单号(不再编子单号),百世排除 - docs: 补充站点统计逻辑审查报告与韵达/安能计算逻辑梳理
9.0 KiB
后端各站点「应到件数 / 已到件数」统计逻辑审查报告
审查范围:
InboundVerify后端 核心模块:expected_undelivered.py(比对统计)、runtime.py(调度/下载路由)、site_*.py(各站数据采集)、server.py(对外接口) 审查重点:每个站点的「应到件数」和「已到件数」如何统计、数据来源、口径与潜在歧义。
一、总体架构(两层)
统计逻辑分两层,前端看到的「应到/已到/未到」计数最终都来自第二层,且只在跑比对时才生成。
| 层 | 模块 | 职责 | 产物 |
|---|---|---|---|
| ① 数据采集层 | site_顺心/中通/韵达/安能.py、site_百世.py |
用 Playwright(安能用 CDP)登录各承运商后台,导出 Excel 到 downloads/ |
downloads/<站>-应到货物数据.xlsx、downloads/<站>-实到货物数据.xlsx、downloads/<站>-未到数据.xlsx |
| ② 比对统计层 | expected_undelivered.py |
读 downloads/ 源文件,两表比对,算出应到/已到/未到件数并出报表 |
output/应到未到数据.xlsx(汇总+各站明细) |
调度入口:runtime.py 的 TASK_HANDLERS。
- 4 站:
expected下载 +actual下载 +undelivered(先下应到+实到,再调write_site_file算出未到)。 - 百世:只有
undelivered(直接导「当日未扫」明细,无应到/实到两份基表)。 __compare__(菜单[9]):调expected_undelivered.main(),用downloads/现有文件生成全站汇总报表。
二、核心统计公式(4 站统一口径)
expected_undelivered.process(name) 是 4 站统计的唯一实现:
对每一条应到运单(按运单号去重,保留首条):
应到件数 N = 应到数据中的「录单件数」(输出列名「总件数」)
应到序号集 = {1, 2, …, N}
已到序号集 = 从「实到货物数据.xlsx」解析该运单实际到货的子单序号
未到序号集 = {1…N} − 已到序号集
未到件数 += len(未到序号集) # 每缺 1 个子单 → 1 行未到明细
应到件数 += N
站点级:
应到件数 = Σ N(所有应到运单)
未到件数 = Σ len(未到序号集)
已到件数 = 应到件数 − 未到件数 ← 注意:是「减出来」的,不是直接数实到
未到率 = 未到件数 ÷ 应到件数
完全未到运单 = 未到序号集 == {1…N} 的运单数
部分未到运单 = 0 < 未到 < N 的运单数
关键事实:已到件数 是 应到件数 − 未到件数 推算出来的(exp_pieces - len(rows)),并非独立去数实到表。它等价于 Σ|已到序号集|,前提是实到解析正确且已到序号 ⊆ 应到序号。
三、各站点统计明细
| 站点 | 应到件数来源 | 已到件数来源 | 实到序号解析规则(arrived) |
源文件(downloads/) |
|---|---|---|---|---|
| 中通 | 中通-应到货物数据.xlsx 的「录单件数」 |
应到−未到(推算) | 运单号列是复合串 = 运单号 + 总数(4位) + 顺序号(4位);取右 8 位,前 4 为总数、后 4 为顺序号 |
中通-应到货物数据.xlsx / 中通-实到货物数据.xlsx |
| 顺心 | 顺心-应到货物数据.xlsx 的「录单件数」 |
应到−未到(推算) | 运单号(干净) + 子单号 = 运单号 + 顺序号(3位);后缀(3位)即顺序号 |
顺心-应到货物数据.xlsx / 顺心-实到货物数据.xlsx |
| 韵达 | 韵达-应到货物数据.xlsx 的「录单件数」 |
应到−未到(推算) | 主单号=运单号,子单号=主单号 + 顺序号(4位);后缀(4位)即顺序号 |
韵达-应到货物数据.xlsx / 韵达-实到货物数据.xlsx |
| 安能 | 安能-应到货物数据.xlsx 的「录单件数」 |
应到−未到(推算) | 所属单号=运单号,扫描单号=所属单号 + 总数(4位) + 顺序号(4位);has_total=True,取末 4 位为顺序号 |
安能-应到货物数据.xlsx / 安能-实到货物数据.xlsx |
| 百世 | 无(站点只给未到) | 无(显示「—」) | 不适用(无实到基表) | 仅 百世-应到未到货物数据.xlsx(= 当日未扫明细,本身就是未到结果) |
4 站合计/图表口径:
expected_undelivered.build_summary只累加 4 站(应到−实到口径),百世不计入合计(无应到基数)。百世在表中单列,未到件数 = 其明细行数。
四、百世的特殊口径(务必注意)
百世是唯一「无应到/实到基数」的站点:
- 它的数据来自后台「扫描综合查询 → 到/接件扫描 → 当日 → 未扫」,导出即「当日未扫」明细(
site_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 得到的——即**「截至上次跑比对」的快照**,不是实时值。
七、潜在歧义与风险点(审查结论)
- 已到件数是减出来的,不是数出来的:依赖实到子单号解析正确。一旦某站
子单号格式与解析规则(arrived_*)不匹配,该序号进不了「已到序号集」→ 误判为未到 → 已到件数被低估、未到率虚高。解析规则是硬编码的,承运商改版号段即失准。 - 应到件数依赖「录单件数」:若应到数据某运单
录单件数缺失/为 0/非数字,该运单被continue跳过,既不计入应到也不计入未到 → 静默漏统(应到总量被低估,但该运单若出现在实到中也因不在应到循环而永不计入已到,方向一致)。 - 应到按运单号去重(keep first):同一运单多条交接记录只取首条
录单件数。若重复行的件数不同,取首条,可能与实际不符。 - 百世不可并入合计:4 站合计的「已到总件数」不含百世;跨站看「已到」时别把百世当成有应到基数的站。
- 应到/实到业务日期可能错位(见第五节):两表不同步下载或偏移不一致时,比对口径失真。
- 计数是比对产物,非实时:前端若要「实时件数」需先触发比对任务;
/status的ready仅表示「下载成功」,不代表「已比对出数」。 - 子单号是程序现拼的:4 站缺件的「子单号/扫描单号」由
code_*按各站编号规则生成(code_shunxin等),并非实到原始记录——明细里的缺件单号是推算值,用于人工核对,不是系统回执。
八、关键文件索引
| 文件 | 角色 |
|---|---|
expected_undelivered.py |
统计核心:process()(4站比对)、process_baishi()(百世)、build_summary()(汇总报表)、STATIONS(各站解析配置) |
runtime.py |
TASK_HANDLERS(下载/比对路由)、_site_undelivered_handler(4站先下应到+实到再算未到)、_record_business_date(业务日期/偏移写入) |
site_中通/顺心/韵达/安能.py |
各站 expected_download / actual_download(Playwright/CDP 导出源表) |
site_百世.py |
baishi_download_undelivered_data(直接导「当日未扫」) |
state_store.py |
site_status(ready/business_date)、site_config(offset/schedule)、get_offset |
server.py |
/status、/report、/data/{filename} 接口 |