# -*- coding: utf-8 -*- """验证:权限树形化(parent_code) - seed 回填 + /permissions 返回父子结构""" import json, urllib.request, urllib.error BASE = "http://127.0.0.1:8902" TOKEN = None def req(method, path, payload=None): url = BASE + path data = json.dumps(payload).encode() if payload is not None else None r = urllib.request.Request(url, 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=20) as resp: return json.loads(resp.read().decode()) except urllib.error.HTTPError as e: return {"_http": e.code, "_body": e.read().decode()[:300]} def main(): global TOKEN d = req("POST", "/api/auth/login", {"username": "admin", "password": "123456"}) TOKEN = (d.get("data") or {}).get("token") assert TOKEN, f"登录失败: {d}" print("✓ 登录成功") # 执行 seed 回填 parent_code sd = req("POST", "/api/rbac/seed") print("✓ seed 完成:", sd.get("data") or sd) # 拉取权限,校验 parent_code pl = req("GET", "/api/permissions") listp = (pl.get("data") or {}).get("list") or [] menus = [p for p in listp if p["type"] == "MENU"] buttons = [p for p in listp if p["type"] == "BUTTON"] print(f"权限总数 {len(listp)}:菜单 {len(menus)},按钮 {len(buttons)}") # 校验每个按钮的 parent_code 是否为有效菜单 code 或为空(全局) menu_codes = {m["code"] for m in menus} bad = [p for p in buttons if p.get("parentCode") and p["parentCode"] not in menu_codes] global_btns = [p for p in buttons if not p.get("parentCode")] print(f" 按钮中全局(无父)数: {len(global_btns)} -> {[p['code'] for p in global_btns]}") print(f" 按钮 parent_code 有误(指向不存在菜单): {[p['code'] for p in bad]}") assert not bad, "存在 parent_code 指向无效菜单" print("✓ 所有按钮 parent_code 均有效(或为全局空)") # 展示各菜单下挂的按钮(模拟前端树) for m in menus: kids = [p["name"] for p in buttons if p.get("parentCode") == m["code"]] print(f" [{m['name']}] 挂按钮: {kids if kids else '(无)'}") passed = not bad and len(global_btns) >= 1 print("\n==== 验证 %s ====" % ("通过" if passed else "失败")) return passed if __name__ == "__main__": raise SystemExit(0 if main() else 1)