deliver_desensitize.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """交付脱敏 (发给非业主方时用): 把事实契约的**内部面**标题换成已有的脱敏公开标题, 重算逐条 sha 与契约 sha, 重出四个消费者, 再自校验.
  4. 契约本来就带 display.title_zh_public (BANNED 替换后的), 四个消费者也早已 BANNED 零命中; 只有 display.title_zh 这一处内部面留了原文。
  5. 用法: python scripts/deliver_desensitize.py --stage <暂存目录> (不动正本; 打包时用暂存目录里的这几个文件覆盖)"""
  6. from __future__ import annotations
  7. import argparse, importlib, json, shutil, sys
  8. from pathlib import Path
  9. ROOT = Path(__file__).resolve().parents[1]; sys.path.insert(0, str(ROOT / "scripts"))
  10. def main():
  11. ap = argparse.ArgumentParser(); ap.add_argument("--stage", required=True); a = ap.parse_args()
  12. stage = Path(a.stage).resolve(); (stage / "derived").mkdir(parents=True, exist_ok=True)
  13. m = importlib.import_module("guanlan_facts_contract")
  14. con = json.loads(m.CONTRACT.read_text(encoding="utf-8"))
  15. n = 0
  16. for c in con["claims"]:
  17. d = c["display"]
  18. if d.get("banned_hits"):
  19. d["title_zh"] = d["title_zh_public"]; d["banned_hits"] = 0; n += 1
  20. c["sha256"] = m.sha(m.canon({k: v for k, v in c.items() if k != "sha256"}).encode("utf-8"))
  21. con["banned_hits_total"] = 0; con["face"] = "delivery (标题已脱敏; 场名/台号保留 —— 那是本场自己的数据)"
  22. con["contract_sha256"] = m.sha(m.canon({k: v for k, v in con.items() if k not in ("contract_sha256", "built", "git_head")}).encode("utf-8"))
  23. cp = stage / m.CONTRACT.name; cp.write_text(json.dumps(con, ensure_ascii=False, indent=1), encoding="utf-8")
  24. # 用暂存路径重出消费者并自校验 (证明脱敏后整条链仍自洽)
  25. m.CONTRACT, m.DERIVED = cp, stage / "derived"
  26. m.render(con, m.DERIVED); bad = m.check()
  27. print(f"脱敏 {n} 条标题; 契约 sha {con['contract_sha256'][:16]}; 消费者重出 {len(list((stage / 'derived').glob('*')))} 个; 自校验 {'通过' if not bad else bad[:3]}")
  28. return 0 if not bad else 2
  29. if __name__ == "__main__":
  30. sys.exit(main())