未到数据按站独立 + 百世并入汇总 + 全量跑比对(失败容错)
- state_store:site_status 加 undelivered_ready 字段(旧库 ALTER 迁移);init_db 提早到启动最前(server lifespan + launch_and_prepare 第0步),避免 /api/status 早于迁移报错
- expected_undelivered:重构为 process(name)/process_baishi/write_site_file/build_full_report;build_summary 支持百世(仅未到件、无基数,不计入合计/图表)与失败容错(未成功站保留行无数据)
- runtime:4 站 ("站","undelivered") = 下应到+实到 → 比对写 <站>-未到数据.xlsx;("__compare__","compare") 改 run_all(顺序跑5站、记成功清单 → build_full_report,未登录/失败跳过);DATA_FILENAMES 加 undelivered、心跳探测之;_site_undelivered_handler 用 is not False 与 dispatch 一致
- site_shunxin:shunxin_expected/actual_download 改为 return with_retry 结果(修复返回 None 致调用方误判失败、以及 with_retry 失败被当成功的潜在 bug)
- server:lifespan 启动时 init_db
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
83
runtime.py
83
runtime.py
@@ -55,11 +55,27 @@ HEARTBEAT_INTERVAL = 30
|
||||
|
||||
# 各站最终数据文件名(探测"数据是否已跑出来");百世为单流程
|
||||
DATA_FILENAMES = {
|
||||
"顺心": {"expected": "顺心-应到货物数据.xlsx", "actual": "顺心-实到货物数据.xlsx"},
|
||||
"中通": {"expected": "中通-应到货物数据.xlsx", "actual": "中通-实到货物数据.xlsx"},
|
||||
"韵达": {"expected": "韵达-应到货物数据.xlsx", "actual": "韵达-实到货物数据.xlsx"},
|
||||
"安能": {"expected": "安能-应到货物数据.xlsx", "actual": "安能-实到货物数据.xlsx"},
|
||||
"百世": {"expected": "百世-应到未到货物数据.xlsx", "actual": ""},
|
||||
"顺心": {
|
||||
"expected": "顺心-应到货物数据.xlsx",
|
||||
"actual": "顺心-实到货物数据.xlsx",
|
||||
"undelivered": "顺心-未到数据.xlsx",
|
||||
},
|
||||
"中通": {
|
||||
"expected": "中通-应到货物数据.xlsx",
|
||||
"actual": "中通-实到货物数据.xlsx",
|
||||
"undelivered": "中通-未到数据.xlsx",
|
||||
},
|
||||
"韵达": {
|
||||
"expected": "韵达-应到货物数据.xlsx",
|
||||
"actual": "韵达-实到货物数据.xlsx",
|
||||
"undelivered": "韵达-未到数据.xlsx",
|
||||
},
|
||||
"安能": {
|
||||
"expected": "安能-应到货物数据.xlsx",
|
||||
"actual": "安能-实到货物数据.xlsx",
|
||||
"undelivered": "安能-未到数据.xlsx",
|
||||
},
|
||||
"百世": {"expected": "", "actual": "", "undelivered": "百世-应到未到货物数据.xlsx"},
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +212,9 @@ def launch_and_prepare(debug_mode=False, debug_target=""):
|
||||
必须在"持有 Playwright 的线程"调用(交互模式主线程 / 服务模式 worker 线程)。
|
||||
阻塞至所有站点登录就绪才返回。
|
||||
"""
|
||||
# 0. 状态库建表/迁移(须在 reset_login_states 等读写新字段的调用之前)
|
||||
state_store.init_db()
|
||||
|
||||
# 1. 读 config(anneng.app_path)
|
||||
anneng_app_path = ""
|
||||
try:
|
||||
@@ -309,8 +328,7 @@ def launch_and_prepare(debug_mode=False, debug_target=""):
|
||||
if s in pages_map:
|
||||
print(f" ✅ 【{s}】已就绪。")
|
||||
|
||||
# 8. 状态库 + 心跳初值(就绪轮询刚通过 → 各站视为已登录)
|
||||
state_store.init_db()
|
||||
# 8. 心跳初值(就绪轮询刚通过 → 各站视为已登录;init_db 已在启动时完成)
|
||||
sites_to_watch = list(ready_status.keys())
|
||||
for _site in sites_to_watch:
|
||||
state_store.set_login_state(_site, True)
|
||||
@@ -389,19 +407,66 @@ def _web_handler(site, download_func):
|
||||
return handler
|
||||
|
||||
|
||||
def _site_undelivered_handler(site):
|
||||
"""4 站未到:下应到+实到 → 比对写 downloads/<站>-未到数据.xlsx。
|
||||
任一下载失败 → 清掉旧未到文件、返回 False(前端不展示陈旧未到)。"""
|
||||
|
||||
def handler(ctx):
|
||||
# 各站下载入口约定返回 True/False;顺心历史返回 None(视为成功,与 dispatch 一致)
|
||||
exp_ok = TASK_HANDLERS[(site, "expected")](ctx) is not False
|
||||
act_ok = (
|
||||
(TASK_HANDLERS[(site, "actual")](ctx) is not False) if exp_ok else False
|
||||
)
|
||||
if exp_ok and act_ok:
|
||||
return expected_undelivered.write_site_file(site)
|
||||
stale = os.path.join(
|
||||
DOWNLOAD_DIR, expected_undelivered.SITE_UNDELIVERED_FILE.format(name=site)
|
||||
)
|
||||
if os.path.exists(stale):
|
||||
os.remove(stale)
|
||||
return False
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def _run_all_compare_handler():
|
||||
"""顶部「跑比对」:顺序跑 5 站未到(4 站 expected+actual→比对;百世直供),
|
||||
记录本次成功站 → build_full_report。失败/未登录站保留行、无数据,不影响他站。"""
|
||||
|
||||
def handler(ctx):
|
||||
include = set()
|
||||
for site in ("顺心", "中通", "韵达", "安能", "百世"):
|
||||
if not probe_site_login(site, ctx.pages_map):
|
||||
print(f">> [跑比对] {site} 未登录,跳过其统计")
|
||||
continue
|
||||
h = TASK_HANDLERS.get((site, "undelivered"))
|
||||
if h and h(ctx):
|
||||
include.add(site)
|
||||
else:
|
||||
print(f">> [跑比对] {site} 未到数据获取失败,跳过其统计")
|
||||
expected_undelivered.build_full_report(include)
|
||||
return True
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
TASK_HANDLERS = {
|
||||
("顺心", "expected"): _web_handler("顺心", site_shunxin.shunxin_expected_download),
|
||||
("顺心", "actual"): _web_handler("顺心", site_shunxin.shunxin_actual_download),
|
||||
("顺心", "undelivered"): _site_undelivered_handler("顺心"),
|
||||
("百世", "undelivered"): _web_handler(
|
||||
"百世", site_baishi.baishi_download_undelivered_data
|
||||
),
|
||||
("中通", "expected"): _web_handler("中通", site_zto.zto_expected_download),
|
||||
("中通", "actual"): _web_handler("中通", site_zto.zto_actual_download),
|
||||
("中通", "undelivered"): _site_undelivered_handler("中通"),
|
||||
("韵达", "expected"): _web_handler("韵达", site_yunda.yunda_expected_download),
|
||||
("韵达", "actual"): _web_handler("韵达", site_yunda.yunda_actual_download),
|
||||
("韵达", "undelivered"): _site_undelivered_handler("韵达"),
|
||||
("安能", "expected"): lambda ctx: site_anneng.anneng_expected_download(),
|
||||
("安能", "actual"): lambda ctx: site_anneng.anneng_actual_download(),
|
||||
("__compare__", "compare"): lambda ctx: (expected_undelivered.main() or True),
|
||||
("安能", "undelivered"): _site_undelivered_handler("安能"),
|
||||
("__compare__", "compare"): _run_all_compare_handler(),
|
||||
}
|
||||
|
||||
|
||||
@@ -449,6 +514,6 @@ def run_heartbeat(ctx):
|
||||
now_login = state_store.LOGIN_IN if logged_in else state_store.LOGIN_OUT
|
||||
if prev_login and prev_login not in (now_login, state_store.LOGIN_UNKNOWN):
|
||||
print(f"\n ⚠️【{site_name}】登录态变化: {prev_login} → {now_login}")
|
||||
for kind in ("expected", "actual"):
|
||||
for kind in ("expected", "actual", "undelivered"):
|
||||
ready, gen_at = probe_data_file(site_name, kind)
|
||||
state_store.set_data_state(site_name, kind, ready, gen_at)
|
||||
|
||||
Reference in New Issue
Block a user