Files
InboundVerify/README.md
Misaka_Company 5e4889e845 docs: sync docs with current package layout and auto-ingest hook
- config.example.yaml: replace stale site_yunda/main_router with new
  module paths (sites.yunda, runtime)
- CLAUDE.md: add ingest-one to DB CLI list; new subsection documenting
  the download->PostgreSQL auto-ingest hook (_persist_to_db, ingest_task,
  ingest_state, /status.ingest, auto_ingest config)
- README.md: store tree comment lists all 5 CLI commands (add ingest-one)
- docs: /status row notes the ingest field; anneng CDP guide snippet
  gets timeout=15
- cli/server.py: docstring run command -> python -m inbound_verify.cli.server

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

191 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 物流到货数据自动下载与应到未到核对工具
自动登录 5 家物流承运商的工作台,下载"应到 / 实到"货物数据,并离线比对出
**应到未到**(该到没到)的异常运单,最终汇总成一份 Excel 报表。
适用场景:网点每日核对进站货物是否到齐。
---
## 一、覆盖站点与流程
5 个站点中4 个是网页、1 个是 Electron 桌面应用:
| 站点 | 形态 | 应到 | 实到 |
|---|---|---|---|
| 顺心捷达 | 网页 | ✅ 运单信息 | ✅ 卸车扫描记录 |
| 百世快运 | 网页 | — | —(直接提取"应到未到/当日未扫",单流程) |
| 中通快运 | 网页 | ✅ 运单信息 | ✅ 到件扫描 |
| 韵达快运 | 网页 | ✅ 进站主单 | ✅ 扫描记录 |
| 安能全网门户 | Electron 应用 | ✅ 运单信息 | ✅ 网点到件扫描 |
- **应到** = 进站交接单下的运单明细(这批货"应该"到);
- **实到** = 到件 / 卸车扫描记录(实际扫到了哪些);
-**9 个下载流程**(顺心/中通/韵达/安能 各 2 个 + 百世 1 个)。
每个站点产出独立的 `{站点}-应到货物数据.xlsx` / `{站点}-实到货物数据.xlsx`
(百世为 `百世-应到未到货物数据.xlsx`),落在 `downloads/`
---
## 二、目录结构
```
InboundVerify/
├── pyproject.toml # 打包 + 依赖 + console_scriptsinbound-verify 等)
├── inbound_verify/ # 源码包
│ ├── paths.py # 统一路径锚点(以项目目录为基准,不依赖 cwd
│ ├── runtime.py # 两种模式共享核心(启动 / 就绪 / 任务派发 / 心跳)
│ ├── state_store.py # SQLite 状态持久化state/state.db
│ ├── domain.py # 站点 / 文件名 / 列映射共享配置单一来源leaf
│ ├── compare.py # 全站点应到未到离线比对,输出 output/应到未到数据.xlsx
│ ├── store.py # DB CLI 入口createdb|init|ingest|ingest-one|all
│ ├── sites/ # 各站点模块(流程 + 重置 + 重试,自洽)
│ │ ├── shunxin.py # 顺心(含双账号)
│ │ ├── baishi.py # 百世
│ │ ├── zto.py # 中通
│ │ ├── yunda.py # 韵达(含自动登录)
│ │ └── anneng.py # 安能Electron + CDP 驱动)
│ └── cli/ # 命令行入口
│ ├── router.py # 交互菜单(调度层:启动 / 就绪轮询 / 登录检测 / 菜单分发)
│ └── server.py # FastAPI 服务模式(常驻 + HTTP 触发)
├── config.example.yaml # 配置模板
├── config.yaml # 真实配置(自行创建,已被 .gitignore 忽略)
├── schema.sql # 数据库表结构store.py createdb / init 使用)
├── requirements.txt # pyproject 依赖的静态镜像
├── downloads/ # 各站点下载的原始数据
├── output/ # 比对报表输出
├── state/ # 运行状态持久化state.db
└── docs/ # 说明文档
```
---
## 三、环境准备
需 Python 3.10+。
```bash
# 1. 创建并激活虚拟环境
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS / Linux
# 2. 安装依赖(含安能 CDP 驱动所需的 websocket-client
pip install -r requirements.txt
# 3. 以可编辑模式安装本包(注册 inbound-verify 等命令)
pip install -e .
# 4. 安装 Playwright 浏览器内核(网页站点用)
playwright install chromium
# 5. 由模板创建本地配置并填入真实凭据
cp config.example.yaml config.yaml
```
> 安能是 Electron 应用,还需在 `config.yaml` 里填 `anneng.app_path`
> 指向本机的「安能全网门户.exe」路径。
---
## 四、配置说明config.yaml
`config.yaml` 存放凭据与各站参数,**已被 .gitignore 忽略,不会提交**。
所有项都有默认值,留空不会报错(但凭据留空会导致对应站点登录/导出失败)。
| 配置项 | 说明 |
|---|---|
| `debug.enabled` / `debug.target_site` | 调试模式:仅挂载启动指定单个站点(顺心/百世/中通/韵达/安能) |
| `shunxin.query_days` | 顺心查询时间范围(向前回溯 N 天至今天) |
| `baishi.password` | 百世导出授权密码(必填,否则导出失败) |
| `zto.query_days` | 中通查询时间范围 |
| `yunda.username` / `yunda.password` | 韵达自动登录凭据(留空则需手动登录) |
| `yunda.query_days` | 韵达查询时间范围 |
| `anneng.query_days` | 安能查询时间范围 |
| `anneng.app_path` | 安能 Electron 可执行文件路径 |
---
## 五、运行
```bash
# 交互菜单(任选其一)
python -m inbound_verify.cli.router
# 或装包后直接用命令:
inbound-verify
```
程序会:
1. 用 Playwright 打开各网页站点(**顺心为双账号**:同一窗口开两个标签页,分别登录两个
不同归属地账号;韵达若配了凭据会尝试自动登录,其余手动登录);
2. 以调试模式启动安能 Electron 应用(动态空闲端口),**需在应用内手动登录**
3. **轮询各站点登录就绪状态**——全部登录完成后自动进入主菜单(顺心需两个标签页都进主页);
4. 弹出主菜单,按编号选择任务。
主菜单:
```
[1][2] 顺心 应到 / 实到
[3] 百世 应到未到(当日未扫)
[4][5] 中通 应到 / 实到
[6][7] 韵达 应到 / 实到
[10][11] 安能 应到 / 实到
[8] 全站点自动化测试(交叉跑通校验)
[9] 应到未到比对(全站点汇总 → output/应到未到数据.xlsx
[0] 退出
```
### DB CLI入库
```bash
# DB CLI建库 / 初始化 / 灌数据 / 全流程 / 单站单类
.venv/Scripts/python.exe -m inbound_verify.store createdb # 或 init | ingest | ingest-one <site> <kind> | all
# 注下载成功后会自动入库postgres.auto_ingest默认开ingest-one 用于手动重灌指定站/类。
```
---
## 六、架构
**分层原则:路由层只调度,站点模块自洽。**
- **`inbound_verify.cli.router`(调度层)**:负责启动浏览器 / 安能、就绪轮询、登录检测、
菜单分发。**不关心**"任务能否完成、失败怎么办"——只调
`inbound_verify.sites.xxx_download(page)` 然后等结果。
- **各 `inbound_verify.sites.*`(站点模块)**:每个模块对外只暴露一个"把任务做了"的入口
`xxx_download(...)`,内部自行处理一切:
- `HOME_URL`:站点首页 URL也供路由层 `SITES_CONFIG` 引用,单一来源);
- `xxx_reset(...)`:异常兜底的重置(网页 = 跳首页 URL安能 = 关业务 tab + 收菜单);
- `with_retry(...)`:内联在本模块的重试逻辑;
- `xxx_download(...)`**公开入口** = `with_retry(站点, 标签, xxx_download_impl, xxx_reset)`
- `xxx_download_impl(...)`:单次执行、无重试(供自动化测试探测原始失败)。
**异常兜底(失败 → 重置 → 重试)**:任一流程失败(返回 False 或抛异常)→ 调对应
站点的 `xxx_reset` 回到初始态 → 重试,**最多 3 次(含首次)**;每次失败都重置
(含最终放弃那次),确保环境不残留脏状态。
> **顺心是双账号特例**:同一窗口开两个标签页(两个归属地账号),`shunxin_download`
> 接收 page 列表,内部读各账号归属地 → 去重校验 → 顺序下载 → `pd.concat` 融合成
> 统一的 `顺心-{应到/实到}货物数据.xlsx`,比对层无感(仍读同名文件)。
> 安能是 Electron由 CDP远程调试端口驱动而非 Playwright page。其重置**不用
> `Page.reload`**——reload 会让已开的业务 webContents 失去引用、变成无法清理的僵尸,
> 故改用"走 tab 条 X 关 tab + 收起菜单"。
---
## 七、输出
- `downloads/{站点}-应到货物数据.xlsx``{站点}-实到货物数据.xlsx`:各站点原始数据;
- `output/应到未到数据.xlsx`:全站点比对报表(汇总 + 各站明细),由菜单 [9] 生成。
---
## 八、备注
- 首次运行需手动登录各站点(程序会停在就绪轮询,直到检测到所有站点进入工作台);
- 安能为单实例 Electron 应用:启动前请先关闭已打开的安能窗口;
- 所有下载/输出路径以项目目录为基准(见 `inbound_verify.paths`),与从哪个目录启动无关。