Rewrite run commands to python -m inbound_verify.* (and console_script aliases); add pip install -e . to env prep; refresh the directory tree. Also commit the design spec and Tier 1 implementation plan under docs/superpowers/. Co-Authored-By: Claude <noreply@anthropic.com>
182 lines
8.1 KiB
Markdown
182 lines
8.1 KiB
Markdown
# 物流到货数据自动下载与应到未到核对工具
|
||
|
||
自动登录 5 家物流承运商的工作台,下载"应到 / 实到"货物数据,并离线比对出
|
||
**应到未到**(该到没到)的异常运单,最终汇总成一份 Excel 报表。
|
||
|
||
适用场景:网点每日核对进站货物是否到齐。
|
||
|
||
---
|
||
|
||
## 一、覆盖站点与流程
|
||
|
||
5 个站点中,4 个是网页、1 个是 Electron 桌面应用:
|
||
|
||
| 站点 | 形态 | 应到 | 实到 |
|
||
|---|---|---|---|
|
||
| 顺心捷达 | 网页 | ✅ 运单信息 | ✅ 卸车扫描记录 |
|
||
| 百世快运 | 网页 | — | —(直接提取"应到未到/当日未扫",单流程) |
|
||
| 中通快运 | 网页 | ✅ 运单信息 | ✅ 到件扫描 |
|
||
| 韵达快运 | 网页 | ✅ 进站主单 | ✅ 扫描记录 |
|
||
| 安能全网门户 | Electron 应用 | ✅ 运单信息 | ✅ 网点到件扫描 |
|
||
|
||
- **应到** = 进站交接单下的运单明细(这批货"应该"到);
|
||
- **实到** = 到件 / 卸车扫描记录(实际扫到了哪些);
|
||
- 共 **9 个下载流程**(顺心/中通/韵达/安能 各 2 个 + 百世 1 个)。
|
||
|
||
每个站点产出独立的 `{站点}-应到货物数据.xlsx` / `{站点}-实到货物数据.xlsx`
|
||
(百世为 `百世-应到未到货物数据.xlsx`),落在 `downloads/`。
|
||
|
||
---
|
||
|
||
## 二、目录结构
|
||
|
||
```
|
||
InboundVerify/
|
||
├── pyproject.toml # 打包 + 依赖 + console_scripts(inbound-verify 等)
|
||
├── inbound_verify/ # 源码包
|
||
│ ├── paths.py # 统一路径锚点(以项目目录为基准,不依赖 cwd)
|
||
│ ├── runtime.py # 两种模式共享核心(启动 / 就绪 / 任务派发 / 心跳)
|
||
│ ├── state_store.py # SQLite 状态持久化(state/state.db)
|
||
│ ├── expected_undelivered.py# 全站点应到未到离线比对,输出 output/应到未到数据.xlsx
|
||
│ ├── store.py # DB CLI 入口(createdb|init|ingest|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] 退出
|
||
```
|
||
|
||
---
|
||
|
||
## 六、架构
|
||
|
||
**分层原则:路由层只调度,站点模块自洽。**
|
||
|
||
- **`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`),与从哪个目录启动无关。
|