#!/usr/bin/env python3 """按 Canonical 章节/正文重算作品聚合指标。 用于修复历史接受通道未回写的 ``muse_content_work.chapter_count`` 与 ``word_count``;正式接受路径已在 ``write_canonical.accept`` 内同步维护。 """ import argparse import json import pathlib import sys DB_SCRIPTS = pathlib.Path(__file__).resolve().parents[2] / "access-database" / "scripts" sys.path.insert(0, str(DB_SCRIPTS)) from db import connect # noqa: E402 TENANT = 1 ACTOR = "metrics-reconcile" def reconcile_work(work_id, *, updater=ACTOR, dry_run=False): with connect() as conn: try: row = conn.execute( """SELECT COUNT(DISTINCT c.id)::bigint, COALESCE(SUM(b.word_count),0)::bigint FROM muse_content_chapter c LEFT JOIN muse_content_block b ON b.chapter_id=c.id AND b.deleted=false WHERE c.work_id=%s AND c.deleted=false""", (work_id,), ).fetchone() updated = conn.execute( "UPDATE muse_content_work SET chapter_count=%s,word_count=%s," "revision=revision+1,updater=%s WHERE id=%s AND deleted=false " "RETURNING chapter_count,word_count,revision", (row[0], row[1], updater, work_id), ).fetchone() if not updated: raise ValueError(f"作品不存在或已删除:work_id={work_id}") result = {"work_id": work_id, "chapter_count": int(updated[0]), "word_count": int(updated[1]), "revision": updated[2]} if dry_run: conn.rollback() result["status"] = "dry_run_ok" result["note"] = "试跑已回滚,未落库" return result conn.commit() result["status"] = "reconciled" return result except Exception: conn.rollback() raise def main(): parser = argparse.ArgumentParser(description="重算作品 Canonical 聚合指标") parser.add_argument("--work-id", type=int, required=True) parser.add_argument("--dry-run", action="store_true") args = parser.parse_args() print(json.dumps(reconcile_work(args.work_id, dry_run=args.dry_run), ensure_ascii=False)) if __name__ == "__main__": main()