# -*- coding: utf-8 -*- """端到端:拿真令牌打**新端点**,看订单抽屉那两块(退款单 / 回调轨迹)到底收到什么。 与 `_lsy-order-refunds-probe.py` 的分工(别把两者混为一谈): * 那个探针直连库,跑「同一条 SQL」,**证明不了端点** —— 它自己 docstring 里写着这句。 * 这个脚本走 HTTP,过 Spring Security / @PreAuthorize / Jackson / 租户拦截器, 拿到的是**页面真正会收到的那份 JSON**。它才是「抽屉里有没有东西」的证据。 只读:两个 GET,不写任何东西。 凭据纪律:只打令牌**长度**;响应正文落 UTF-8 文件,stdout 只打 ASCII。 控制台 GBK ⇒ 中文一律进文件。 """ import io import json import os import sys import urllib.error import urllib.request ROOT = r"D:\project2\payment-platform" OUT = os.path.join(ROOT, "logs", "_lsy-refunds-e2e.txt") BASE = os.environ.get("LSY_ADMIN_BASE", "http://localhost:8001/api/v1") ORDER = sys.argv[1] if len(sys.argv) > 1 else "BC1790649777" buf = [] def say(s=""): buf.append(str(s)) print(str(s).encode("ascii", "replace").decode()) def req(method, path, token=None, body=None): """返回 (http_status, 解析后的 JSON 或原文)。业务错误也照样返回,由调用方判。""" data = json.dumps(body).encode("utf-8") if body is not None else None r = urllib.request.Request(BASE + path, data=data, method=method) r.add_header("Content-Type", "application/json") if token: r.add_header("Authorization", "Bearer " + token) try: with urllib.request.urlopen(r, timeout=30) as resp: raw = resp.read() return resp.status, json.loads(raw.decode("utf-8")) except urllib.error.HTTPError as e: raw = e.read() try: return e.code, json.loads(raw.decode("utf-8")) except Exception: return e.code, raw.decode("utf-8", "replace") # ---- 1. 登录取令牌 ------------------------------------------------------- say("=" * 74) say(" 1. 取管理端令牌") say("=" * 74) st, cap = req("GET", "/system/auth/captcha/get") say(" captcha/get -> HTTP %s enable=%s" % (st, (cap.get("data") or {}).get("enable"))) token = None st, users = req("GET", "/system/auth/auto-login/users") say(" auto-login/users -> HTTP %s" % st) lst = (users.get("data") if isinstance(users, dict) else None) or [] say(" 免登录可选用户 %d 个" % len(lst)) if lst: uid = lst[0].get("id") uname = lst[0].get("username") say(" 取第一个:username=%s id=%s" % (uname, uid)) st, t = req("POST", "/system/auth/auto-login/token?user_id=%s" % uid) say(" auto-login/token -> HTTP %s" % st) tok = ((t.get("data") or {}) if isinstance(t, dict) else {}).get("token") if tok: st, res = req("POST", "/system/auth/auto-login?token=%s" % tok) say(" auto-login -> HTTP %s" % st) token = ((res.get("data") or {}) if isinstance(res, dict) else {}).get("access_token") if token: say(" 令牌已取得:len=%d (值不打印)" % len(token)) else: say(" !没能取得令牌,下面两个端点会 401 —— 那就是本脚本的结论,不要当成端点坏了") # ---- 2. 新端点 /refunds -------------------------------------------------- say("") say("=" * 74) say(" 2. GET /payment/collection/order/%s/refunds (本次新加的端点)" % ORDER) say("=" * 74) st, refunds = req("GET", "/payment/collection/order/%s/refunds" % ORDER, token) say(" HTTP %s" % st) say(json.dumps(refunds, ensure_ascii=False, indent=2)) rows = ((refunds.get("data") if isinstance(refunds, dict) else None) or []) say(" -> %d 行" % len(rows)) for i, r in enumerate(rows): say(" [%d] keys=%s" % (i, sorted(r.keys()))) # 前端「退款单」那一节的 5 列 + 状态映射要读的键,逐个点名 for k in ("out_refund_no", "refund_fee", "status", "refund_no", "finish_time"): say(" %-14s = %r" % (k, r.get(k, "<缺失>"))) # ---- 3. 回调轨迹 /notify-logs ------------------------------------------- say("") say("=" * 74) say(" 3. GET /payment/collection/order/%s/notify-logs (trace 集合改过)" % ORDER) say("=" * 74) st, logs = req("GET", "/payment/collection/order/%s/notify-logs" % ORDER, token) say(" HTTP %s" % st) say(json.dumps(logs, ensure_ascii=False, indent=2)) lg = ((logs.get("data") if isinstance(logs, dict) else None) or []) say(" -> %d 行(改前只按订单 trace 查,是 4 行)" % len(lg)) n_refund = 0 for x in lg: tt = x.get("trade_type") if tt == "0": n_refund += 1 say(" %s tt=%s verify=%s process=%s trace=%s" % (x.get("created_time"), tt, x.get("verify_status"), x.get("process_status"), x.get("pay_trace_no"))) say(" 其中 trade_type=0(退款回调)的行 = %d" % n_refund) say("") say(" 判定:") say(" 退款单 %d 行(期望 1)、回调轨迹 %d 行含退款 %d 行(期望 6 / 2)" % (len(rows), len(lg), n_refund)) say(" => %s" % ("两项都变了,用户报的两个症状在 HTTP 层确认修好" if len(rows) == 1 and n_refund == 2 else "与期望不符,见上面原始读数")) io.open(OUT, "w", encoding="utf-8").write("\n".join(buf) + "\n") say("") say("正文已写入 %s" % os.path.relpath(OUT, ROOT))