docs: update README/CLAUDE.md for package layout; add Tier 1 spec and plan
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>
This commit is contained in:
65
CLAUDE.md
65
CLAUDE.md
@@ -8,73 +8,82 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
异常运单并汇总成 Excel。4 个网页站点 + 1 个 Electron 应用(安能)。
|
||||
|
||||
**两种运行模式**(阶段1 起):
|
||||
- **交互模式** `main_router.py`:人工调试 / 操作,交互菜单(登录、触发下载、[12] 状态盘)。
|
||||
- **服务模式** `server.py`:常驻 + FastAPI,客户端经 HTTP 触发任务、查状态、下载数据(API 文档 `/docs`)。
|
||||
- 两者共享 `runtime.py`(启动 / 就绪 / 任务派发 / 心跳)与 `state_store.py`(SQLite 状态持久化)。
|
||||
- **交互模式** `inbound_verify.cli.router`:人工调试 / 操作,交互菜单(登录、触发下载、[12] 状态盘)。
|
||||
- **服务模式** `inbound_verify.cli.server`:常驻 + FastAPI,客户端经 HTTP 触发任务、查状态、下载数据(API 文档 `/docs`)。
|
||||
- 两者共享 `inbound_verify.runtime`(启动 / 就绪 / 任务派发 / 心跳)与 `inbound_verify.state_store`(SQLite 状态持久化)。
|
||||
|
||||
## 常用命令
|
||||
|
||||
所有 Python 一律在项目虚拟环境 `.venv` 中运行(Windows 下可直接用
|
||||
`.venv/Scripts/python.exe`,无需激活)。
|
||||
`.venv/Scripts/python.exe`,无需激活)。首次 / 拉取新代码后需
|
||||
`.venv/Scripts/python.exe -m pip install -e .`(以可编辑模式注册
|
||||
`inbound-verify` 等命令)。
|
||||
|
||||
```bash
|
||||
# 安装依赖(含安能 CDP 驱动所需的 websocket-client)
|
||||
# 安装依赖(含安能 CDP 驱动所需的 websocket-client)+ 以可编辑模式注册命令
|
||||
pip install -r requirements.txt
|
||||
pip install -e .
|
||||
playwright install chromium
|
||||
|
||||
# 运行主程序(交互式菜单,详见 main_router 的 run_multi_site_daemon)
|
||||
.venv/Scripts/python.exe main_router.py
|
||||
# 运行主程序(交互式菜单,详见 inbound_verify.cli.router 的 run_multi_site_daemon)
|
||||
.venv/Scripts/python.exe -m inbound_verify.cli.router
|
||||
# 装包后也可直接用命令:inbound-verify
|
||||
|
||||
# 服务模式(常驻 + FastAPI,客户端经 HTTP 触发;默认 :8000,API 文档见 /docs)
|
||||
.venv/Scripts/python.exe server.py
|
||||
.venv/Scripts/python.exe -m inbound_verify.cli.server
|
||||
# 或:inbound-verify-server
|
||||
|
||||
# DB CLI:建库 / 初始化 / 灌数据 / 全流程
|
||||
.venv/Scripts/python.exe -m inbound_verify.store createdb # 或 init | ingest | all
|
||||
# 或:inbound-verify-db createdb|init|ingest|all
|
||||
|
||||
# 单站点联调:在 config.yaml 设 debug.enabled=true + debug.target_site=顺心|百世|中通|韵达|安能
|
||||
# 网页站:只挂载该站;安能:只启动 Electron 应用。
|
||||
|
||||
# 安能独立运行(需先以 --remote-debugging-port=9222 启动「安能全网门户.exe」并手动登录)
|
||||
.venv/Scripts/python.exe site_anneng.py expected # 或 actual
|
||||
.venv/Scripts/python.exe -m inbound_verify.sites.anneng expected # 或 actual
|
||||
|
||||
# 格式化(全局规范:改完 Python 必须 Black)
|
||||
.venv/Scripts/python.exe -m black <file.py>
|
||||
.venv/Scripts/python.exe -m black inbound_verify
|
||||
|
||||
# 语法自检
|
||||
.venv/Scripts/python.exe -m py_compile <file.py>
|
||||
.venv/Scripts/python.exe -m py_compile inbound_verify
|
||||
```
|
||||
|
||||
**没有 pytest 测试套件。** "测试"指 `main_router` 菜单 **[8] 自动化测试**
|
||||
**没有 pytest 测试套件。** "测试"指 `inbound_verify.cli.router` 菜单 **[8] 自动化测试**
|
||||
(`run_automation_test`,按 `CROSS_TEST_SEQUENCE` 交叉跑通各站点流程)。
|
||||
|
||||
## 架构(big picture)
|
||||
|
||||
### 运行模式与共享核心(阶段0/1 重构)
|
||||
- **`runtime.py`**:两种模式共享的核心——`launch_and_prepare`(启动 Playwright + 各站就绪 + 弹窗 + 心跳初值,阻塞至就绪)、`dispatch_task(ctx, {site,kind})`(派发任务,掉登录直接判 failed)、`run_heartbeat`、`probe_site_login/probe_data_file`、`RuntimeContext.stop()`。常量 `SITES_CONFIG/READY_SELECTORS/APP_SITES/HEARTBEAT_INTERVAL/DATA_FILENAMES` 在此。
|
||||
- **`state_store.py`**:SQLite 状态持久化(`state/state.db`)。`site_status`(登录态 + 数据态 + 时间戳,心跳刷新)、`task_history`(任务记录)。重启不丢。
|
||||
- **`main_router.py`**:交互模式,菜单循环(input 后台线程 + `_await_command` + `dispatch_task` + 心跳)。
|
||||
- **`server.py`**:服务模式。**FastAPI(主线程)+ Playwright worker(独立线程)**——主线程处理 HTTP(绝不碰 Playwright),worker 独占 page 操作,经 `task_queue` + `state_store` 通信。API:`POST/GET /tasks`、`GET /status`、`GET /data/{file}`。
|
||||
- **`inbound_verify.runtime`**:两种模式共享的核心——`launch_and_prepare`(启动 Playwright + 各站就绪 + 弹窗 + 心跳初值,阻塞至就绪)、`dispatch_task(ctx, {site,kind})`(派发任务,掉登录直接判 failed)、`run_heartbeat`、`probe_site_login/probe_data_file`、`RuntimeContext.stop()`。常量 `SITES_CONFIG/READY_SELECTORS/APP_SITES/HEARTBEAT_INTERVAL/DATA_FILENAMES` 在此。
|
||||
- **`inbound_verify.state_store`**:SQLite 状态持久化(`state/state.db`)。`site_status`(登录态 + 数据态 + 时间戳,心跳刷新)、`task_history`(任务记录)。重启不丢。
|
||||
- **`inbound_verify.cli.router`**:交互模式,菜单循环(input 后台线程 + `_await_command` + `dispatch_task` + 心跳)。
|
||||
- **`inbound_verify.cli.server`**:服务模式。**FastAPI(主线程)+ Playwright worker(独立线程)**——主线程处理 HTTP(绝不碰 Playwright),worker 独占 page 操作,经 `task_queue` + `state_store` 通信。API:`POST/GET /tasks`、`GET /status`、`GET /data/{file}`。
|
||||
- **关键线程约束**:Playwright sync 对象绑定创建它的线程;`launch_and_prepare`(含 `sync_playwright().start()`)必须在持有 Playwright 的线程调用(交互=主线程,服务=worker 线程)。FastAPI 路由绝不访问 page。
|
||||
- 站点模块 `site_*.py` 的 `with_retry` 返回 `True/False`(成功 / 放弃),供 `dispatch_task` 判成败。
|
||||
- 站点模块 `inbound_verify.sites.*` 的 `with_retry` 返回 `True/False`(成功 / 放弃),供 `dispatch_task` 判成败。
|
||||
|
||||
### 两套驱动模态 —— 这是理解全局的关键
|
||||
- **网页 4 站**(顺心/百世/中通/韵达):`main_router` 用 Playwright 开 chromium,
|
||||
- **网页 4 站**(顺心/百世/中通/韵达):`inbound_verify.cli.router` 用 Playwright 开 chromium,
|
||||
每站一个 `page`,流程函数签名为 `xxx_download_impl(page)`。**例外:顺心是双账号**
|
||||
——同一窗口开两个标签页(两个归属地账号),`pages_map["顺心"]` 存为 page **列表**,
|
||||
`shunxin_download(pages)` 接收列表(详见下文「顺心双账号」)。
|
||||
- **安能**:Electron 桌面应用,**不走 Playwright**。`main_router` 以
|
||||
- **安能**:Electron 桌面应用,**不走 Playwright**。`inbound_verify.cli.router` 以
|
||||
`--remote-debugging-port=<动态空闲端口>` 启动 exe(`launch_anneng`),
|
||||
通过 `site_anneng.set_cdp_port` 告知模块;`site_anneng.py` 用裸 CDP(websocket)
|
||||
通过 `inbound_verify.sites.anneng.set_cdp_port` 告知模块;`inbound_verify.sites.anneng` 用裸 CDP(websocket)
|
||||
驱动,业务 tab 是独立 webContents。这也是 `playwright-cli` 接管不了安能的原因
|
||||
(Electron 19 / Chrome 102 不支持 Playwright 要的 setDownloadBehavior)。
|
||||
|
||||
### 分层:路由纯调度,站点模块自洽
|
||||
- `main_router.py` **只调度**:启动浏览器/安能、就绪轮询、登录检测、菜单分发。
|
||||
菜单项直接调 `site_xxx.xxx_download(page)`(顺心传 page 列表),**不关心**重试/重置。
|
||||
- 每个 `site_xxx.py` 对外只暴露"把任务做了"的入口,内部自洽:
|
||||
- `inbound_verify.cli.router` **只调度**:启动浏览器/安能、就绪轮询、登录检测、菜单分发。
|
||||
菜单项直接调 `sites.xxx_download(page)`(顺心传 page 列表),**不关心**重试/重置。
|
||||
- 每个 `inbound_verify.sites.*` 模块对外只暴露"把任务做了"的入口,内部自洽:
|
||||
- `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` 引用它(单一来源)
|
||||
- `HOME_URL` —— 站点首页 URL,`runtime.SITES_CONFIG` 引用它(单一来源)
|
||||
|
||||
### 导出任务队列模式(顺心/中通/韵达/安能-应到 共用)
|
||||
这些站的下载是异步的:提交导出(记 `export_times` 时间戳)→ 跳"导出任务管理"页轮询 →
|
||||
@@ -88,7 +97,7 @@ playwright install chromium
|
||||
### 顺心双账号(双归属地)
|
||||
顺心业务上要同时处理**两个归属地网点**(两个账号)。程序在同一窗口开两个标签页,
|
||||
人工分别登录两个账号(顺心站点支持同浏览器双账号并存,无需独立 context/窗口)。
|
||||
- `main_router` 启动时为顺心开 2 个 `context.new_page()`,`pages_map["顺心"]` 为列表;
|
||||
- `runtime` 启动时为顺心开 2 个 `context.new_page()`,`pages_map["顺心"]` 为列表;
|
||||
就绪轮询要求**两个标签页都进主页**才算就绪;初始弹窗对两个标签页各处理一遍。
|
||||
- `shunxin_expected_download(pages)` / `shunxin_actual_download(pages)` 接收 page 列表:
|
||||
先用 `shunxin_belonging(page)` 读各账号归属地(首页「切换网点」控件 `.site___3o7nH`),
|
||||
@@ -100,16 +109,16 @@ playwright install chromium
|
||||
配合每账号独立 `export_times` + ≤40s 容差,B 不会误匹配 A 的任务。
|
||||
|
||||
### 比对
|
||||
`expected_undelivered.py`(菜单 [9])纯离线:读 `downloads/` 下各站应到/实到 xlsx,
|
||||
`inbound_verify.expected_undelivered`(菜单 [9])纯离线:读 `downloads/` 下各站应到/实到 xlsx,
|
||||
比对生成 `output/应到未到数据.xlsx`(汇总 + 各站明细)。
|
||||
|
||||
### 路径
|
||||
`paths.py` 把 `DOWNLOAD_DIR` / `CONFIG_PATH` / `BASE_DIR` 全部锚定到项目目录,
|
||||
`inbound_verify.paths` 把 `DOWNLOAD_DIR` / `CONFIG_PATH` / `BASE_DIR` 全部锚定到项目目录,
|
||||
**不依赖运行时 cwd**——别用相对路径或 `os.getcwd()`。
|
||||
|
||||
## 重要约定 / 易踩坑
|
||||
|
||||
- **登录是手动的**:`main_router` 启动后会停在就绪轮询(`READY_SELECTORS` / `anneng_ready`),
|
||||
- **登录是手动的**:`inbound_verify.cli.router` 启动后会停在就绪轮询(`READY_SELECTORS` / `anneng_ready`),
|
||||
直到检测到所有站点进入工作台才进菜单。仅韵达支持凭 `config.yaml` 凭据自动登录。
|
||||
**顺心需登录两个账号**:同一窗口的两个标签页分别登录两个不同归属地账号,两个标签页
|
||||
都进主页后才算就绪(顺心站点支持同浏览器双账号并存,故用同 context 标签页而非独立窗口)。
|
||||
|
||||
Reference in New Issue
Block a user