| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- # -*- 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))
|