Files
InboundVerify/CLAUDE.md
Misaka_Company a07b9b435b 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>
2026-07-23 12:21:45 +08:00

9.7 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

项目概览

自动登录 5 家物流承运商工作台,下载"应到 / 实到"货物数据,离线比对出应到未到 异常运单并汇总成 Excel。4 个网页站点 + 1 个 Electron 应用(安能)。

两种运行模式阶段1 起):

  • 交互模式 inbound_verify.cli.router:人工调试 / 操作,交互菜单(登录、触发下载、[12] 状态盘)。
  • 服务模式 inbound_verify.cli.server:常驻 + FastAPI客户端经 HTTP 触发任务、查状态、下载数据API 文档 /docs)。
  • 两者共享 inbound_verify.runtime(启动 / 就绪 / 任务派发 / 心跳)与 inbound_verify.state_storeSQLite 状态持久化)。

常用命令

所有 Python 一律在项目虚拟环境 .venv 中运行Windows 下可直接用 .venv/Scripts/python.exe,无需激活)。首次 / 拉取新代码后需 .venv/Scripts/python.exe -m pip install -e .(以可编辑模式注册 inbound-verify 等命令)。

# 安装依赖(含安能 CDP 驱动所需的 websocket-client+ 以可编辑模式注册命令
pip install -r requirements.txt
pip install -e .
playwright install chromium

# 运行主程序(交互式菜单,详见 inbound_verify.cli.router 的 run_multi_site_daemon
.venv/Scripts/python.exe -m inbound_verify.cli.router
# 装包后也可直接用命令inbound-verify

# 服务模式(常驻 + FastAPI客户端经 HTTP 触发;默认 :8000API 文档见 /docs
.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 -m inbound_verify.sites.anneng expected   # 或 actual

# 格式化(全局规范:改完 Python 必须 Black
.venv/Scripts/python.exe -m black inbound_verify

# 语法自检
.venv/Scripts/python.exe -m py_compile inbound_verify

没有 pytest 测试套件。 "测试"指 inbound_verify.cli.router 菜单 [8] 自动化测试 run_automation_test,按 CROSS_TEST_SEQUENCE 交叉跑通各站点流程)。

架构big picture

运行模式与共享核心阶段0/1 重构)

  • inbound_verify.runtime:两种模式共享的核心——launch_and_prepare(启动 Playwright + 各站就绪 + 弹窗 + 心跳初值,阻塞至就绪)、dispatch_task(ctx, {site,kind})(派发任务,掉登录直接判 failedrun_heartbeatprobe_site_login/probe_data_fileRuntimeContext.stop()。常量 SITES_CONFIG/READY_SELECTORS/APP_SITES/HEARTBEAT_INTERVAL/DATA_FILENAMES 在此。
  • inbound_verify.state_storeSQLite 状态持久化(state/state.db)。site_status(登录态 + 数据态 + 时间戳,心跳刷新)、task_history(任务记录)。重启不丢。
  • inbound_verify.cli.router交互模式菜单循环input 后台线程 + _await_command + dispatch_task + 心跳)。
  • inbound_verify.cli.server:服务模式。FastAPI主线程+ Playwright worker独立线程——主线程处理 HTTP绝不碰 Playwrightworker 独占 page 操作,经 task_queue + state_store 通信。APIPOST/GET /tasksGET /statusGET /data/{file}
  • 关键线程约束Playwright sync 对象绑定创建它的线程;launch_and_prepare(含 sync_playwright().start())必须在持有 Playwright 的线程调用(交互=主线程,服务=worker 线程。FastAPI 路由绝不访问 page。
  • 站点模块 inbound_verify.sites.*with_retry 返回 True/False(成功 / 放弃),供 dispatch_task 判成败。

两套驱动模态 —— 这是理解全局的关键

  • 网页 4 站(顺心/百世/中通/韵达):inbound_verify.cli.router 用 Playwright 开 chromium 每站一个 page,流程函数签名为 xxx_download_impl(page)例外:顺心是双账号 ——同一窗口开两个标签页(两个归属地账号),pages_map["顺心"] 存为 page 列表 shunxin_download(pages) 接收列表(详见下文「顺心双账号」)。
  • 安能Electron 桌面应用,不走 Playwrightinbound_verify.cli.router--remote-debugging-port=<动态空闲端口> 启动 exelaunch_anneng 通过 inbound_verify.sites.anneng.set_cdp_port 告知模块;inbound_verify.sites.anneng 用裸 CDPwebsocket 驱动,业务 tab 是独立 webContents。这也是 playwright-cli 接管不了安能的原因 Electron 19 / Chrome 102 不支持 Playwright 要的 setDownloadBehavior

分层:路由纯调度,站点模块自洽

  • 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 —— 站点首页 URLruntime.SITES_CONFIG 引用它(单一来源)

导出任务队列模式(顺心/中通/韵达/安能-应到 共用)

这些站的下载是异步的:提交导出(记 export_times 时间戳)→ 跳"导出任务管理"页轮询 → 匹配本批任务 → 下载 → 合并多个临时 xlsx。

  • 匹配只按时间容差 ≤40s不校验任务标题/模块名(标题可能被站点改动;单账号无并发, 时间窗内的任务必为本批所建)。不要把标题校验加回来。
  • 轮询循环都有 300s deadline:超时 raise → 流程返回 False → 触发 with_retry 重试。
  • 例外:百世是同步下载(expect_download,无队列);安能-实到页面无导出按钮, 直接抓表格 DOM 翻页。

顺心双账号(双归属地)

顺心业务上要同时处理两个归属地网点(两个账号)。程序在同一窗口开两个标签页, 人工分别登录两个账号(顺心站点支持同浏览器双账号并存,无需独立 context/窗口)。

  • runtime 启动时为顺心开 2 个 context.new_page()pages_map["顺心"] 为列表; 就绪轮询要求两个标签页都进主页才算就绪;初始弹窗对两个标签页各处理一遍。
  • shunxin_expected_download(pages) / shunxin_actual_download(pages) 接收 page 列表: 先用 shunxin_belonging(page) 读各账号归属地(首页「切换网点」控件 .site___3o7nH 去重校验(两账号同归属地则报错中止,防数据翻倍),再顺序对各账号跑一遍 xxx_download_impl(page, out_tag=归属地)(产物 顺心-{归属}-{应到/实到}货物数据.xlsx 最后 shunxin_merge_final 把两份 pd.concat 成统一的 顺心-{应到/实到}货物数据.xlsx 并删中间文件。比对层 expected_undelivered 零改动(仍读同名文件)。
  • 导出队列不串扰:双账号顺序执行,账号 A 走完完整下载流程(远超 40s后 B 才提交, 配合每账号独立 export_times + ≤40s 容差B 不会误匹配 A 的任务。

比对

inbound_verify.expected_undelivered(菜单 [9])纯离线:读 downloads/ 下各站应到/实到 xlsx 比对生成 output/应到未到数据.xlsx(汇总 + 各站明细)。

路径

inbound_verify.pathsDOWNLOAD_DIR / CONFIG_PATH / BASE_DIR 全部锚定到项目目录, 不依赖运行时 cwd——别用相对路径或 os.getcwd()

重要约定 / 易踩坑

  • 登录是手动的inbound_verify.cli.router 启动后会停在就绪轮询(READY_SELECTORS / anneng_ready 直到检测到所有站点进入工作台才进菜单。仅韵达支持凭 config.yaml 凭据自动登录。 顺心需登录两个账号:同一窗口的两个标签页分别登录两个不同归属地账号,两个标签页 都进主页后才算就绪(顺心站点支持同浏览器双账号并存,故用同 context 标签页而非独立窗口)。
  • 安能单实例:启动前必须先关闭已打开的安能窗口,否则调试端口起不来。
  • 安能重置绝不能用 Page.reloadreload 会让已开业务 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(全局规范)。