Files
xiaotutongxue/scripts/dev-reveal-roster-idcard.py
T
lovife 4e68d0e5da test(e2e): 新增填报流程端到端脚本与测试计划
既有 37 个脚本是按模块切片的(门禁/草稿/提交/审核各一个),有三处固定盲区:
跨步骤的状态轨迹没人断言、切片之间那条缝(驳回后改了字段再交)、以及脚本
docstring 自述与实际断言不符(e2e-m3-draft.py:11 自述的那条断言全文没有)。

docs/11 给出 A~J 十组覆盖矩阵与判据口径(含用 grep 核对实际断言、不读自述的
规程),scripts/e2e-m3-flow.py 只补这三处、其余交给既有脚本,并钉住 F-125 /
F-128 的现状(XFAIL)。verify-all 的脚本数口径随之变成 38。

配套 scripts/dev-reveal-roster-idcard.py:L3-B 走查要在门禁界面手输身份证,
而明文只在建夹具那一刻存在过(库里是 AES 密文 + 脱敏 + HMAC 索引),
此脚本只读解密打印,不入库;用完连同夹具任务一起 --clean。
2026-10-14 15:40:18 +08:00

72 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""临时脚本(只读):解出本轮走查夹具(e2e-m3-flow.py 建的 taskMain)名册行身份证明文。
用途:L3-B 浏览器走查要在门禁界面里**手动输入姓名 + 身份证号**,
而明文只在建夹具的那一刻存在过(库里是 AES 密文 / 脱敏 / HMAC 索引)。
dev 的 AES 密钥在 application-dev.yml 里(提交进仓库的开发专用密钥),格式见 AesGcmCipher:
Base64( IV(12) || ciphertext || tag(16) )。
**只读,不写库。** 走查完请连同夹具任务一起清掉(`python scripts/e2e-m3-flow.py --clean <taskId>`)。
"""
from __future__ import annotations
import base64
import re
import subprocess
import sys
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
TASK_ID = int(sys.argv[1]) if len(sys.argv) > 1 else 2311
DEV_YML = r"D:\IDEA_Project\xiaotutongxue\backend\xt-server\src\main\resources\application-dev.yml"
TEXT = open(DEV_YML, encoding="utf-8").read()
AES_KEY = base64.b64decode(re.search(r"aes-key:\s*(\S+)", TEXT).group(1))
CIPHER = AESGCM(AES_KEY)
def psql(sql: str) -> list[list[str]]:
p = subprocess.run(
["docker", "exec", "xt-pg", "psql", "-U", "fill", "-d", "filldb",
"-A", "-F", "\t", "-P", "pager=off", "-t", "-c", sql],
capture_output=True,
)
if p.returncode != 0:
raise SystemExit(p.stderr.decode("utf-8", "replace"))
# ⚠️ 不能对整段输出 .strip() —— psql 的 -A 模式在**末尾字段为空**时会留下结尾制表符,
# 整段 strip 会把它吃掉,于是"未绑定"的那一行少一列、解包直接报
# `ValueError: not enough values to unpack`(而这个报错看起来像解密失败)。
out = p.stdout.decode("utf-8", "replace")
return [ln.split("\t") for ln in out.splitlines() if ln.strip()]
def decrypt(cipher_b64: str) -> str:
raw = base64.b64decode(cipher_b64.strip())
return CIPHER.decrypt(raw[:12], raw[12:], None).decode("utf-8")
rows = psql(f"""
SELECT id, real_name, COALESCE(id_card_no,''), COALESCE(id_card_mask,''),
COALESCE(bind_user_id::text,'')
FROM collect_roster
WHERE deleted = 0 AND task_id = {TASK_ID}
ORDER BY id
""")
print(f"任务 {TASK_ID} 的名册(走查用;只打印,不入库)")
print("名册行 | 姓名 | 身份证(明文) | 脱敏 | 绑定用户")
for r in rows:
rid, name, enc = r[0], r[1], r[2]
mask = r[3] if len(r) > 3 else ""
bind = r[4] if len(r) > 4 else ""
try:
plain = decrypt(enc)
except Exception as e: # noqa: BLE001
print(f"{rid} | {name} | [解不开:{type(e).__name__}] | {mask} | {bind or '未绑定'}")
continue
print(f"{rid} | {name} | {plain} | {mask} | {bind or '未绑定'}")