# -*- coding: utf-8 -*- """票05 首轮 FAIL 根因探针(一次性,验证后删):operation/login/batch 日志真实行 + batch/file·download 正确姿势 + publish 被拒原因""" import json, urllib.request, urllib.parse, urllib.error, sys, io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') API = "http://localhost:8080" ADMIN = "739564171091247104" T = json.loads(urllib.request.urlopen(f"{API}/api/auth/debug/token?userId={ADMIN}", timeout=15).read().decode())["data"] def post(path, data=None): req = urllib.request.Request(API + path, urllib.parse.urlencode(data or {}).encode(), method="POST") req.add_header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") req.add_header("Authorization", "Bearer " + T) try: with urllib.request.urlopen(req, timeout=30) as r: return json.loads(r.read().decode()) except urllib.error.HTTPError as e: return {"code": -1, "message": f"HTTP {e.code}"} def raw(path, method="GET", data=None, form_pairs=None): body = None if form_pairs: body = urllib.parse.urlencode(form_pairs).encode() elif data: body = urllib.parse.urlencode(data).encode() req = urllib.request.Request(API + path, body, method=method) if body: req.add_header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") req.add_header("Authorization", "Bearer " + T) try: with urllib.request.urlopen(req, timeout=30) as r: b = r.read() return {"status": r.status, "len": len(b), "head": b[:120], "magic": b[:2]} except urllib.error.HTTPError as e: return {"status": e.code, "len": -1, "head": e.read()[:120]} print("== 1. operation/page 最近 30 行(无筛选,看 opType/operatorName/content 真实值)==") d = post("/api/log/operation/page", {"current": 1, "size": 30}) for r in (d.get("data") or {}).get("content", [])[:30]: print(json.dumps(r, ensure_ascii=False)[:230]) print("== 2. login/page 最近 15 行(找「退出」行)==") d = post("/api/log/login/page", {"current": 1, "size": 15}) for r in (d.get("data") or {}).get("content", [])[:15]: print(json.dumps(r, ensure_ascii=False)[:230]) print("== 3. batch/page 最近 5 行(rowId vs fileId)==") d = post("/api/log/batch/page", {"current": 1, "size": 5}) rows = (d.get("data") or {}).get("content", []) for r in rows: print(json.dumps(r, ensure_ascii=False)[:230]) if rows: rid = rows[0]["id"] print(f"== 4. GET batch/file?id={rid}(传日志行主键)==") print(raw(f"/api/log/batch/file?id={rid}")) print(f"== 5. POST batch/download ids=[{rid}](单 id)==") print(raw("/api/log/batch/download", method="POST", form_pairs=[("ids", str(rid))])) if len(rows) >= 2: print("== 5b. POST batch/download ids=[两行] ==") print(raw("/api/log/batch/download", method="POST", form_pairs=[("ids", str(rows[0]["id"])), ("ids", str(rows[1]["id"]))])) print("== 6. publish 试探(e2d-绑定数规则-probe,部门专用 admin 部门)==") d = post("/api/rule/opp-pool-rule/publish", { "ruleName": "e2d-绑定数规则-probe", "applyScope": 2, "deptIds": "744841292348915712", "isDefault": 0, "allowManualPool": 1, "autoRecycleEnabled": 0, "allowFreeClaim": 1, "ruleDesc": "probe 临时"}) print(json.dumps(d, ensure_ascii=False)[:400]) print("== 7. opp-pool-rule/page 最近 20 行(看占用与状态分布)==") d = post("/api/rule/opp-pool-rule/page", {"current": 1, "size": 20}) for r in (d.get("data") or {}).get("content", [])[:20]: print(json.dumps(r, ensure_ascii=False)[:200]) print("== 8. saveOrUpdate 角色后立即查「修改」行(验证 resolveOpType 顺序 bug)==") d = post("/api/roles/saveOrUpdate", {"roleName": "e2d-probe角色-x", "roleCode": "e2d_probe_x", "remark": "probe"}) print("saveOrUpdate:", json.dumps(d, ensure_ascii=False)[:120]) d = post("/api/log/operation/page", {"current": 1, "size": 5}) for r in (d.get("data") or {}).get("content", [])[:5]: print(json.dumps(r, ensure_ascii=False)[:200]) # 清理 probe 角色 d = post("/api/roles/page", {"current": 1, "size": 10, "roleName": "e2d-probe角色-x"}) for r in (d.get("data") or {}).get("content", []): print("cleanup role:", post("/api/roles/delete", {"id": str(r["id"])}).get("code"))