Commit Graph

14 Commits

Author SHA1 Message Date
Misaka
c6ad6a0ca2 fix(runtime): ingest before db_compare so first-run/force reads fresh data
DB 比对发生在入库之前,导致首次/force 时 PG 无当天数据,比对返回 None、不产出 Excel。在 _site_undelivered_handler 下载成功后、比对前,前置 _record_business_date + ingest_task,使比对能读到本次下载的数据。dispatch_task 后置 _persist_to_db 保持不变(对 undelivered 幂等重复一次,安全)。

四站真机验证通过(中通/韵达/安能 + 顺心):前置入库日志均出现在 db_compare 之前,force 首跑即产出 Excel。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 20:25:52 +08:00
Misaka_Company
541836fd1b feat(db_compare): add PostgreSQL-based comparison engine with SF handling
Replace Excel-based undelivered comparison with DB queries for all four
sites. The engine anchors on actual scan_time, reverse-lookups handover
batches, and compares expected vs actual waybill-by-waybill.

Shunxin SF waybills: use COUNT(*) instead of COUNT(DISTINCT piece_no)
since SF piece numbers are random and not derivable from the waybill.

Changes:
- db_compare.py: new module with compare_site_date(), compare_site_batch(),
  write_result_excel(), and POST /compare API endpoint
- runtime.py: switch _site_undelivered_handler from compare.write_site_file
  (Excel) to db_compare (DB); downloads succeed independently of comparison
- server.py: add POST /compare endpoint with date validation
- docs: implementation plan for Shunxin DB comparison

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 16:31:54 +08:00
Misaka
09e05f8dfc feat(runtime): disable microphone by default to suppress permission popup
韵达等站点打开时会请求麦克风权限,触发浏览器系统级授权弹窗。在共享 context 上加
add_init_script,于每个页面/iframe 加载前覆盖 navigator.mediaDevices.getUserMedia
(及 webkit/moz 旧版) 为直接 reject(NotAllowedError: Permission disabled):站点调用时
立即被拒、不再弹出系统授权窗,且麦克风被真正挡住(非授权给它)。物流工作台无需音视频
采集,故对所有网页站点统一生效。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 20:58:46 +08:00
Misaka_Company
fcb75643d0 feat(runtime): propagate date through dispatch chain and business-date snapshot
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 13:10:23 +08:00
Misaka_Company
f710622a3d feat: dedup expected data by handover_no before export submit
- store.get_existing_handover_nos: query PG expected_record.handover_no

- inject dedup skip before submitting export in zto/yunda/anneng/shunxin

- shunxin reads RTS handover_no from waybill-list view (method 1)

- force-redownload switch threaded via task_spec -> dispatch -> impl

- schema: add idx_expected_handover index

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 10:57:07 +08:00
Misaka
82ab6d8be3 feat(runtime): 服务模式任务执行不再激活浏览器窗口
网页站点(顺心/百世/中通/韵达)执行下载流程时不再把浏览器窗口置顶,避免打扰用户当前工作;流程在后台静默跑。启动登录阶段与交互模式不受影响。

- RuntimeContext 加 foreground 标志:True=任务执行时置顶(交互调试),False=后台静默(服务模式)
- launch_and_prepare(foreground=True) 透传;server worker 以 foreground=False 启动
- _web_handler 按 ctx.foreground 决定单 page 置顶;顺心(list) 透传给 shunxin_download
- shunxin 两个 download 加 foreground 参数,逐账号 bring_to_front 加条件
- 启动登录/初始弹窗清理的 bring_to_front 不变(启动时窗口需可见)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-27 23:17:10 +08:00
Misaka_Company
327f77a727 fix(runtime): wrap ingest_enabled gate so _persist_to_db never raises
Move the store.ingest_enabled() gate inside the main try/except. Previously
it sat outside as a standalone block: ingest_enabled() -> _load_pg_config()
raises FileNotFoundError when config.yaml is absent/malformed, which escaped
_persist_to_db, was caught by dispatch_task's outer except, and flipped a
successful download to FAILED -- violating the hook's never-raise invariant.
Now config errors print a [warn], record ok=False, and return silently.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 11:10:45 +08:00
Misaka_Company
62a66467a9 feat(runtime): auto-ingest hook after successful download
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 11:04:04 +08:00
Misaka_Company
79c84a5a0c fix: retry launch page.goto to absorb transient DNS/timeout
Third launch failure this session (after two 顺心 load-timeouts and a 中通 ERR_NAME_NOT_RESOLVED) — each a transient network blip that killed the whole worker because launch_and_prepare opened sites with no retry. Wrap each site open+goto in _open_page: up to 3 attempts (2s apart) on domcontentloaded, so transient DNS/timeout is absorbed; only persistent failure (all 3) aborts. All 5 sites + 安能 now launch clean.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 09:06:02 +08:00
Misaka_Company
62c44b262a fix: launch page.goto uses domcontentloaded to avoid slow-site timeout
launch_and_prepare opened each site with Playwright's default wait_until='load', which waits for every resource (ads/trackers/images). sxne.sxjdfreight.com (顺心) reliably takes >30s to fire load, so its goto timed out and — launch not being retried — killed the whole worker, leaving only one page open. Switch the two launch gotos to wait_until='domcontentloaded' (return as soon as the DOM is ready; the readiness polling still gates login). Behavior for already-fast sites unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 08:59:56 +08:00
Misaka_Company
23042c272b refactor: rename expected_undelivered to compare
git mv expected_undelivered.py -> compare.py; update the 3 importers (store/runtime/router) to import compare. All public names (main, write_site_file, _read_business_dates) unchanged. Also add the Tier 2 plan doc.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 08:40:04 +08:00
Misaka_Company
91ef8970b8 refactor: extract domain.py (shared site/file/colmap config)
Move ALL_REPORT_SITES / SITE_UNDELIVERED_FILE / BAISHI_FILE / BAISHI_COLUMNS / arrived_pieces_* / STATIONS / _site_cfg out of expected_undelivered into a new leaf module inbound_verify/domain.py. store.py and runtime.py now read site/file config from domain directly instead of through the compare engine (store keeps eu only for _read_business_dates). Behavior identical.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 08:38:18 +08:00
Misaka_Company
5ac618c041 docs: update stale module-name references in comments and docstrings
Replace pre-rename references (main_router, db_store, server.py) in code comments and docstrings with their new locations (cli/router, store, cli/server, runtime). Comment-only; no behavior change.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-23 12:26:34 +08:00
Misaka_Company
7065b88269 refactor: move flat modules into inbound_verify package (Tier 1, behavior-identical)
Relocate 12 root .py modules into inbound_verify/ (sites/, cli/ subpackages). Rewrite all internal imports to package-qualified; drop the site_ prefix on the 5 site modules and their 28 call sites. Fix paths.py BASE_DIR to anchor at the project root. Add main() entry wrappers (cli/router, cli/server, store). No behavior change.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-23 12:21:31 +08:00