未到数据按站独立 + 百世并入汇总 + 全量跑比对(失败容错)
- 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:
@@ -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: bool;generated_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: bool;generated_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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user