"""以同一主账号的实际数据请求新旧明细导出,并逐单元格核对,文件保留在仓库外。""" import argparse import decimal import io import json import time import urllib.parse import urllib.request from pathlib import Path import openpyxl def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--runtime", type=Path, required=True) parser.add_argument("--old-url", default="http://127.0.0.1:18080") parser.add_argument("--new-url", default="http://127.0.0.1:19001") parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() if args.runtime.resolve().is_relative_to(Path(__file__).resolve().parents[1]): raise ValueError("导出文件必须保存在仓库外") client = json.loads((args.runtime / "legacy-client.json").read_text(encoding="utf-8"))["client_id"] headers = [{"Authorization": "Bearer " + (args.runtime / "legacy-admin.token").read_text().strip(), "clientid": client}, {"x-token": (args.runtime / "new-admin.token").read_text().strip()}] reports = [] for kind, time_key in (("register", "Register"), ("spend", "Pay")): params = {"sourceDb": "lx", "pageSize": 10, "begin" + time_key + "Time": "2026-11-03", "end" + time_key + "Time": "2026-11-03"} request = urllib.request.Request(args.new_url + "/sw/" + kind + "/list?" + urllib.parse.urlencode(params), headers=headers[1]) with urllib.request.urlopen(request, timeout=60) as response: body = json.load(response) if body.get("code") != 0: raise RuntimeError("读取导出样本失败:" + kind) sample = next(row for row in body["data"]["rows"] if row.get("userId") is not None) params["userId"] = sample["userId"] sheets, durations = [], [] for i, base in enumerate((args.old_url, args.new_url)): payload = urllib.parse.urlencode(params).encode() request = urllib.request.Request(base + ("/system/" if i == 0 else "/sw/") + kind + "/export", data=payload, headers={**headers[i], "Content-Type": "application/x-www-form-urlencoded"}, method="POST") started = time.monotonic() with urllib.request.urlopen(request, timeout=120) as response: raw = response.read() durations.append(round((time.monotonic() - started) * 1000)) if not raw.startswith(b"PK"): raise RuntimeError("导出未返回工作簿:" + kind + "/" + str(i)) (args.runtime / (("legacy-" if i == 0 else "new-") + kind + "-export.xlsx")).write_bytes(raw) workbook = openpyxl.load_workbook(io.BytesIO(raw), read_only=True, data_only=True) sheets.append(list(workbook.worksheets[0].values)) workbook.close() differences = [] for row_number, (old, new) in enumerate(zip(*sheets), 1): if len(old) != len(new): differences.append({"row": row_number, "kind": "columns"}) for col, (a, b) in enumerate(zip(old, new), 1): if a == b or (a in (None, "") and b in (None, "")): continue if isinstance(a, (int, float)) or isinstance(b, (int, float)): try: if decimal.Decimal(str(a)) == decimal.Decimal(str(b)): continue except decimal.InvalidOperation: pass differences.append({"row": row_number, "column": col, "kind": "value"}) rows = [len(sheet) - 1 for sheet in sheets] reports.append({"kind": kind, "rows": rows, "columns": [len(sheet[0]) for sheet in sheets], "elapsed_ms": durations, "differences": differences, "passed": rows[0] > 0 and rows[0] == rows[1] and not differences}) print(json.dumps(reports[-1], ensure_ascii=False), flush=True) args.output.write_text(json.dumps({"checks": reports, "passed": all(row["passed"] for row in reports)}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") if not all(row["passed"] for row in reports): raise SystemExit(1) if __name__ == "__main__": main()