Files
InboundVerify/CLAUDE.md
Misaka 75abbdf14c Add websocket-client to requirements; sync docs
site_anneng 的 CDP 驱动用到 websocket-client,此前 requirements.txt 缺漏,
需额外手动安装。补进 requirements.txt,并把 README/CLAUDE 里"暂未列入"的
临时提示改为"已包含"。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 22:48:07 +08:00

90 lines
5.1 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.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概览
自动登录 5 家物流承运商工作台,下载"应到 / 实到"货物数据,离线比对出**应到未到**
异常运单并汇总成 Excel。4 个网页站点 + 1 个 Electron 应用(安能)。
## 常用命令
所有 Python 一律在项目虚拟环境 `.venv` 中运行Windows 下可直接用
`.venv/Scripts/python.exe`,无需激活)。
```bash
# 安装依赖(含安能 CDP 驱动所需的 websocket-client
pip install -r requirements.txt
playwright install chromium
# 运行主程序(交互式菜单,详见 main_router 的 run_multi_site_daemon
.venv/Scripts/python.exe main_router.py
# 单站点联调:在 config.yaml 设 debug.enabled=true + debug.target_site=顺心|百世|中通|韵达|安能
# 网页站:只挂载该站;安能:只启动 Electron 应用。
# 安能独立运行(需先以 --remote-debugging-port=9222 启动「安能全网门户.exe」并手动登录
.venv/Scripts/python.exe site_anneng.py expected # 或 actual
# 格式化(全局规范:改完 Python 必须 Black
.venv/Scripts/python.exe -m black <file.py>
# 语法自检
.venv/Scripts/python.exe -m py_compile <file.py>
```
**没有 pytest 测试套件。** "测试"指 `main_router` 菜单 **[8] 自动化测试**
`run_automation_test`,按 `CROSS_TEST_SEQUENCE` 交叉跑通各站点流程)。
## 架构big picture
### 两套驱动模态 —— 这是理解全局的关键
- **网页 4 站**(顺心/百世/中通/韵达):`main_router` 用 Playwright 开 chromium
每站一个 `page`,流程函数签名为 `xxx_download_impl(page)`
- **安能**Electron 桌面应用,**不走 Playwright**。`main_router`
`--remote-debugging-port=<动态空闲端口>` 启动 exe`launch_anneng`
通过 `site_anneng.set_cdp_port` 告知模块;`site_anneng.py` 用裸 CDPwebsocket
驱动,业务 tab 是独立 webContents。这也是 `playwright-cli` 接管不了安能的原因
Electron 19 / Chrome 102 不支持 Playwright 要的 setDownloadBehavior
### 分层:路由纯调度,站点模块自洽
- `main_router.py` **只调度**:启动浏览器/安能、就绪轮询、登录检测、菜单分发。
菜单项直接调 `site_xxx.xxx_download(page)`**不关心**重试/重置。
- 每个 `site_xxx.py` 对外只暴露"把任务做了"的入口,内部自洽:
- `xxx_download(...)` —— 公开入口,= `with_retry(站点, 标签, xxx_download_impl, xxx_reset)`
- `xxx_download_impl(...)` —— 单次执行、**无重试**(自动化测试刻意调它以探测原始失败)
- `xxx_reset(...)` —— 重置回初始态(网页 = `page.goto(HOME_URL)`;安能 = 关业务 tab + 收菜单)
- `with_retry(...)` —— 重试逻辑**内联在每个站点模块**(不抽公共组件,现阶段刻意不优化结构);
失败→重置→重试,最多 3 次(含首次),每次失败都重置(含最终放弃那次清场)
- `HOME_URL` —— 站点首页 URL`main_router.SITES_CONFIG` 引用它(单一来源)
### 导出任务队列模式(顺心/中通/韵达/安能-应到 共用)
这些站的下载是异步的:提交导出(记 `export_times` 时间戳)→ 跳"导出任务管理"页轮询 →
匹配本批任务 → 下载 → 合并多个临时 xlsx。
- **匹配只按时间容差 ≤40s****不校验任务标题/模块名**(标题可能被站点改动;单账号无并发,
时间窗内的任务必为本批所建)。**不要把标题校验加回来。**
- 轮询循环都有 **300s deadline**:超时 `raise` → 流程返回 False → 触发 `with_retry` 重试。
- 例外:**百世**是同步下载(`expect_download`,无队列);**安能-实到**页面无导出按钮,
直接抓表格 DOM 翻页。
### 比对
`expected_undelivered.py`(菜单 [9])纯离线:读 `downloads/` 下各站应到/实到 xlsx
比对生成 `output/应到未到数据.xlsx`(汇总 + 各站明细)。
### 路径
`paths.py``DOWNLOAD_DIR` / `CONFIG_PATH` / `BASE_DIR` 全部锚定到项目目录,
**不依赖运行时 cwd**——别用相对路径或 `os.getcwd()`
## 重要约定 / 易踩坑
- **登录是手动的**`main_router` 启动后会停在就绪轮询(`READY_SELECTORS` / `anneng_ready`
直到检测到所有站点进入工作台才进菜单。仅韵达支持凭 `config.yaml` 凭据自动登录。
- **安能单实例**:启动前必须先关闭已打开的安能窗口,否则调试端口起不来。
- **安能重置绝不能用 `Page.reload`**reload 会让已开业务 webContents 失去 app 引用、
变成 `Target.closeTarget` / `window.close` 都杀不掉的僵尸。重置只能走 tab 条 X 关 tab + 收菜单。
- **`config.yaml` 已 gitignore**,存放凭据 / `query_days` / `debug` / `anneng.app_path`
切勿提交,也别把真实凭据写进 `config.example.yaml`
- **不要自动提交 / 推送**:本仓库约定改动后等用户明确说"提交"再 commit/push
(覆盖全局 CLAUDE.md 的 auto-push 默认)。
- 改完 Python 文件**必须跑 Black**(全局规范)。