未到数据按站独立 + 百世并入汇总 + 全量跑比对(失败容错)

- 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:
Misaka
2026-07-17 23:19:21 +08:00
parent 26a94999dc
commit 82c80fc859
5 changed files with 272 additions and 82 deletions

View File

@@ -35,16 +35,27 @@ def init_db():
with sqlite3.connect(STATE_DB_PATH) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS site_status (
site TEXT PRIMARY KEY,
login_state TEXT,
login_checked_at TEXT,
expected_ready INTEGER,
expected_generated_at TEXT,
actual_ready INTEGER,
actual_generated_at TEXT,
updated_at TEXT
site TEXT PRIMARY KEY,
login_state TEXT,
login_checked_at TEXT,
expected_ready INTEGER,
expected_generated_at TEXT,
actual_ready INTEGER,
actual_generated_at TEXT,
undelivered_ready INTEGER,
undelivered_generated_at TEXT,
updated_at TEXT
)
""")
# 旧库迁移:补 undelivered 两列(新库已含;重复添加抛 OperationalError忽略
for _col, _typedef in [
("undelivered_ready", "INTEGER NOT NULL DEFAULT 0"),
("undelivered_generated_at", "TEXT NOT NULL DEFAULT ''"),
]:
try:
conn.execute(f"ALTER TABLE site_status ADD COLUMN {_col} {_typedef}")
except sqlite3.OperationalError:
pass
conn.execute("""
CREATE TABLE IF NOT EXISTS task_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -70,7 +81,8 @@ def _upsert(conn, site, **fields):
"""更新(或插入)单站:保留未传字段,刷新 updated_at。"""
row = conn.execute(
"SELECT login_state, login_checked_at, expected_ready, expected_generated_at, "
"actual_ready, actual_generated_at FROM site_status WHERE site = ?",
"actual_ready, actual_generated_at, undelivered_ready, undelivered_generated_at "
"FROM site_status WHERE site = ?",
(site,),
).fetchone()
cur = {
@@ -80,6 +92,8 @@ def _upsert(conn, site, **fields):
"expected_generated_at": "",
"actual_ready": 0,
"actual_generated_at": "",
"undelivered_ready": 0,
"undelivered_generated_at": "",
}
if row:
(
@@ -89,6 +103,8 @@ def _upsert(conn, site, **fields):
cur["expected_generated_at"],
cur["actual_ready"],
cur["actual_generated_at"],
cur["undelivered_ready"],
cur["undelivered_generated_at"],
) = row
cur.update(fields)
cur["updated_at"] = _now()
@@ -101,20 +117,24 @@ def _upsert(conn, site, **fields):
cur["expected_generated_at"],
cur["actual_ready"],
cur["actual_generated_at"],
cur["undelivered_ready"],
cur["undelivered_generated_at"],
cur["updated_at"],
)
if row:
conn.execute(
"UPDATE site_status SET login_state=?, login_checked_at=?, expected_ready=?, "
"expected_generated_at=?, actual_ready=?, actual_generated_at=?, updated_at=? "
"expected_generated_at=?, actual_ready=?, actual_generated_at=?, "
"undelivered_ready=?, undelivered_generated_at=?, updated_at=? "
"WHERE site=?",
values[1:] + (site,),
)
else:
conn.execute(
"INSERT INTO site_status (site, login_state, login_checked_at, expected_ready, "
"expected_generated_at, actual_ready, actual_generated_at, updated_at) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
"expected_generated_at, actual_ready, actual_generated_at, "
"undelivered_ready, undelivered_generated_at, updated_at) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
values,
)
conn.commit()
@@ -136,9 +156,9 @@ def reset_login_states(sites):
def set_data_state(site, kind, ready, generated_at):
"""更新单站数据态。kind: 'expected'/'actual'ready: boolgenerated_at: str''"""
key_ready = "expected_ready" if kind == "expected" else "actual_ready"
key_at = "expected_generated_at" if kind == "expected" else "actual_generated_at"
"""更新单站数据态。kind: 'expected'/'actual'/'undelivered'ready: boolgenerated_at: str。"""
key_ready = f"{kind}_ready"
key_at = f"{kind}_generated_at"
with sqlite3.connect(STATE_DB_PATH) as conn:
_upsert(
conn, site, **{key_ready: 1 if ready else 0, key_at: generated_at or ""}
@@ -152,7 +172,8 @@ def get_all_status():
with sqlite3.connect(STATE_DB_PATH) as conn:
rows = conn.execute(
"SELECT site, login_state, login_checked_at, expected_ready, "
"expected_generated_at, actual_ready, actual_generated_at, updated_at "
"expected_generated_at, actual_ready, actual_generated_at, "
"undelivered_ready, undelivered_generated_at, updated_at "
"FROM site_status"
).fetchall()
return {
@@ -163,7 +184,9 @@ def get_all_status():
"expected_generated_at": r[4],
"actual_ready": bool(r[5]),
"actual_generated_at": r[6],
"updated_at": r[7],
"undelivered_ready": bool(r[7]),
"undelivered_generated_at": r[8],
"updated_at": r[9],
}
for r in rows
}