| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- #!/usr/bin/env python3
- """仅刷新 5 个业务角色的菜单授权(不删角色、不动用户绑定)。
- 用于修复「角色只勾 F 按钮、缺少 C 页面 :list」导致前端侧栏为空的问题。
- 用法:
- cd backend && ./venv/bin/python scripts/refresh_business_role_menus.py --env-file .env.production
- ./venv/bin/python scripts/refresh_business_role_menus.py --dry-run --env-file .env.production
- """
- from __future__ import annotations
- import argparse
- import os
- import sys
- from datetime import datetime
- from pathlib import Path
- from sqlalchemy import create_engine, text
- BACKEND_ROOT = Path(__file__).resolve().parents[1]
- if str(BACKEND_ROOT) not in sys.path:
- sys.path.insert(0, str(BACKEND_ROOT))
- from scripts.setup_business_roles import ( # noqa: E402
- MODULE_MENU_ROOTS,
- NEW_ROLES,
- TENANT_NO,
- collect_subtree_menu_ids,
- grant_module_menus,
- perm_menu_ids,
- )
- def load_settings_env(env_file: str) -> None:
- if env_file:
- os.environ["ENV_FILE"] = env_file
- from dotenv import load_dotenv
- path = Path(env_file) if env_file else BACKEND_ROOT / ".env.production"
- if not path.is_absolute():
- path = BACKEND_ROOT / path
- if path.is_file():
- load_dotenv(path, override=True)
- def load_runtime_settings():
- from app.core.config import Settings
- return Settings()
- def verify_user_menu_codes(engine, user_no: str) -> None:
- from sqlalchemy.orm import Session
- from app.services.web_auth_roles import iter_user_menu_perm_codes
- with Session(engine) as db:
- row = db.execute(
- text(
- """
- SELECT id FROM sys_user
- WHERE tenant_no = :tenant AND user_no = :user_no AND del_flag = 0
- LIMIT 1
- """
- ),
- {"tenant": TENANT_NO, "user_no": user_no},
- ).first()
- if not row:
- print(f" [verify] 用户不存在: {user_no}")
- return
- uid = str(row[0])
- codes = sorted(iter_user_menu_perm_codes(db, uid, TENANT_NO))
- list_codes = [c for c in codes if c.endswith(":list")]
- print(f" [verify] {user_no}: 共 {len(codes)} 个 perms,其中 :list {len(list_codes)} 个")
- if list_codes:
- print(f" 示例: {', '.join(list_codes[:5])}...")
- else:
- print(" 警告: 无任何 :list 权限,前端侧栏仍会为空")
- def main() -> int:
- parser = argparse.ArgumentParser(description="刷新业务角色菜单授权")
- parser.add_argument("--env-file", default=".env.production")
- parser.add_argument("--dry-run", action="store_true")
- parser.add_argument("--verify-user", default="方金强", help="刷新后校验该用户的 menuCodes")
- args = parser.parse_args()
- load_settings_env(args.env_file)
- settings = load_runtime_settings()
- engine = create_engine(settings.DATABASE_URL, pool_pre_ping=True)
- print("=== refresh_business_role_menus ===")
- print(f"模式: {'DRY-RUN' if args.dry_run else 'EXECUTE'}")
- print(f"租户: {TENANT_NO}")
- print()
- with engine.begin() as conn:
- subtree = collect_subtree_menu_ids(conn, MODULE_MENU_ROOTS, TENANT_NO)
- grant_ids = perm_menu_ids(conn, subtree, TENANT_NO)
- print(f"模块子树 {len(subtree)} 项,带 perms 菜单 {len(grant_ids)} 项")
- for role_name, role_id in NEW_ROLES.items():
- cnt = grant_module_menus(conn, role_id, grant_ids, TENANT_NO, args.dry_run)
- print(f" {role_name}: {cnt} 项")
- if not args.dry_run and args.verify_user:
- print()
- print("校验 menuCodes:")
- verify_user_menu_codes(engine, args.verify_user)
- print("完成.")
- return 0
- if __name__ == "__main__":
- raise SystemExit(main())
|