refactor: load_path.py自动扫描json/和api/目录生成完整权限路径

- 从json/*.json自动提取tblname和alias,生成所有CRUD路径
- 自动扫描wwwroot/api/*.dspy注册所有API路径
- 去重处理,避免重复注册
- operator获得全部CRUD+API权限,sale获得促销码+客户归属权限
This commit is contained in:
Hermes Agent 2026-06-24 08:57:46 +08:00
parent 95b91c4c25
commit b8a5de7a07

View File

@ -11,13 +11,15 @@ discount 模块 RBAC 权限管理脚本
路径分类:
- any: 静态资源/菜单/CRUD别名目录
- logined: 需要认证的页面和 API
- reseller.operator: 运营角色 折扣方案营销方案客户归属管理
- reseller.sale: 销售角色 促销码客户归属查看
- reseller.operator: 运营角色 全部折扣管理功能
- reseller.sale: 销售角色 促销码客户归属
"""
import subprocess
import os
import sys
import json
import glob
def find_sage_root():
candidates = [
@ -43,23 +45,58 @@ if not os.path.exists(SET_PERM_SCRIPT):
sys.exit(1)
MOD = "discount"
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
JSON_DIR = os.path.join(os.path.dirname(SCRIPT_DIR), "json")
API_DIR = os.path.join(os.path.dirname(SCRIPT_DIR), "wwwroot", "api")
# ============================================================
# 权限路径定义 — 每次新增页面或API时同步更新
# 从 json/ 目录自动推导 CRUD 路径
# ============================================================
def load_crud_definitions():
"""读取所有 json/*.json提取 CRUD 定义信息"""
defs = []
for f in sorted(glob.glob(os.path.join(JSON_DIR, "*.json"))):
try:
d = json.load(open(f))
tblname = d.get("tblname", "")
alias = d.get("alias", tblname)
params = d.get("params", {})
subtables = params.get("subtables", [])
defs.append({
"tblname": tblname,
"alias": alias,
"subtables": subtables,
"file": f,
})
except Exception as e:
print(f" WARNING: skip {f}: {e}")
return defs
def get_api_files():
"""扫描 wwwroot/api/ 下所有 .dspy 文件"""
apis = []
if os.path.isdir(API_DIR):
for f in sorted(os.listdir(API_DIR)):
if f.endswith(".dspy"):
apis.append(f"/{MOD}/api/{f}")
return apis
# ============================================================
# 构建路径列表
# ============================================================
crud_defs = load_crud_definitions()
print(f"Found {len(crud_defs)} CRUD definitions in {JSON_DIR}")
for d in crud_defs:
print(f" - {d['tblname']} (alias: {d['alias']})")
# any — 无需登录菜单、CRUD别名目录
PATHS_ANY = [
f"/{MOD}/menu.ui",
# CRUD alias directories
f"/{MOD}/discount_list",
f"/{MOD}/discount_detail_list",
f"/{MOD}/discount_marketing_list",
f"/{MOD}/discount_promo_code_list",
f"/{MOD}/discount_customer_bind_list",
]
# logined — 需要认证的页面和 API
# 每个 CRUD 定义 → any 别名目录 + logined 页面和脚本
PATHS_LOGINED = [
# 模块入口
f"/{MOD}",
@ -69,142 +106,83 @@ PATHS_LOGINED = [
f"/{MOD}/promote",
f"/{MOD}/promote.ui",
f"/{MOD}/promote/index.dspy",
# CRUD 列表页
f"/{MOD}/discount_list/index.ui",
f"/{MOD}/discount_detail_list/index.ui",
f"/{MOD}/discount_marketing_list/index.ui",
f"/{MOD}/discount_promo_code_list/index.ui",
f"/{MOD}/discount_customer_bind_list/index.ui",
# CRUD auto-generated .dspy — discount
f"/{MOD}/discount_list/get_discount.dspy",
f"/{MOD}/discount_list/add_discount.dspy",
f"/{MOD}/discount_list/update_discount.dspy",
f"/{MOD}/discount_list/delete_discount.dspy",
# CRUD auto-generated .dspy — discount_detail
f"/{MOD}/discount_detail_list/get_discount_detail.dspy",
f"/{MOD}/discount_detail_list/add_discount_detail.dspy",
f"/{MOD}/discount_detail_list/update_discount_detail.dspy",
f"/{MOD}/discount_detail_list/delete_discount_detail.dspy",
# CRUD auto-generated .dspy — discount_marketing
f"/{MOD}/discount_marketing_list/get_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/add_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/update_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/delete_discount_marketing.dspy",
# CRUD auto-generated .dspy — discount_promo_code
f"/{MOD}/discount_promo_code_list/get_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/add_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/update_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/delete_discount_promo_code.dspy",
# CRUD auto-generated .dspy — discount_customer_bind
f"/{MOD}/discount_customer_bind_list/get_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/add_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/update_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/delete_discount_customer_bind.dspy",
# CRUD API (json/ defined)
f"/{MOD}/api/get_customer_list.dspy",
f"/{MOD}/api/discount_create.dspy",
f"/{MOD}/api/discount_update.dspy",
f"/{MOD}/api/discount_delete.dspy",
f"/{MOD}/api/discount_detail_create.dspy",
f"/{MOD}/api/discount_detail_update.dspy",
f"/{MOD}/api/discount_detail_delete.dspy",
f"/{MOD}/api/marketing_create.dspy",
f"/{MOD}/api/marketing_update.dspy",
f"/{MOD}/api/marketing_delete.dspy",
f"/{MOD}/api/promo_code_create.dspy",
f"/{MOD}/api/promo_code_update.dspy",
f"/{MOD}/api/promo_code_delete.dspy",
f"/{MOD}/api/customer_bind_create.dspy",
f"/{MOD}/api/customer_bind_update.dspy",
f"/{MOD}/api/customer_bind_delete.dspy",
]
# 角色专属权限 — operator 可管理折扣方案、营销方案、客户归属
PATHS_OPERATOR = [
# 折扣方案管理
f"/{MOD}/discount_list",
f"/{MOD}/discount_list/index.ui",
f"/{MOD}/discount_list/get_discount.dspy",
f"/{MOD}/discount_list/add_discount.dspy",
f"/{MOD}/discount_list/update_discount.dspy",
f"/{MOD}/discount_list/delete_discount.dspy",
# 折扣产品明细
f"/{MOD}/discount_detail_list",
f"/{MOD}/discount_detail_list/index.ui",
f"/{MOD}/discount_detail_list/get_discount_detail.dspy",
f"/{MOD}/discount_detail_list/add_discount_detail.dspy",
f"/{MOD}/discount_detail_list/update_discount_detail.dspy",
f"/{MOD}/discount_detail_list/delete_discount_detail.dspy",
# 营销方案管理
f"/{MOD}/discount_marketing_list",
f"/{MOD}/discount_marketing_list/index.ui",
f"/{MOD}/discount_marketing_list/get_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/add_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/update_discount_marketing.dspy",
f"/{MOD}/discount_marketing_list/delete_discount_marketing.dspy",
f"/{MOD}/api/marketing_create.dspy",
f"/{MOD}/api/marketing_update.dspy",
f"/{MOD}/api/marketing_delete.dspy",
# 促销码管理
f"/{MOD}/discount_promo_code_list",
f"/{MOD}/discount_promo_code_list/index.ui",
f"/{MOD}/discount_promo_code_list/get_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/add_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/update_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/delete_discount_promo_code.dspy",
f"/{MOD}/api/promo_code_create.dspy",
f"/{MOD}/api/promo_code_update.dspy",
f"/{MOD}/api/promo_code_delete.dspy",
# 客户归属管理
f"/{MOD}/discount_customer_bind_list",
f"/{MOD}/discount_customer_bind_list/index.ui",
f"/{MOD}/discount_customer_bind_list/get_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/add_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/update_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/delete_discount_customer_bind.dspy",
f"/{MOD}/api/customer_bind_create.dspy",
f"/{MOD}/api/customer_bind_update.dspy",
f"/{MOD}/api/customer_bind_delete.dspy",
# 折扣方案和明细 API
f"/{MOD}/api/get_customer_list.dspy",
f"/{MOD}/api/discount_create.dspy",
f"/{MOD}/api/discount_update.dspy",
f"/{MOD}/api/discount_delete.dspy",
f"/{MOD}/api/discount_detail_create.dspy",
f"/{MOD}/api/discount_detail_update.dspy",
f"/{MOD}/api/discount_detail_delete.dspy",
]
for d in crud_defs:
alias = d["alias"]
tblname = d["tblname"]
# any: 别名目录
PATHS_ANY.append(f"/{MOD}/{alias}")
# logined: index.ui
PATHS_LOGINED.append(f"/{MOD}/{alias}/index.ui")
# logined: auto-generated .dspy (get/add/update/delete)
PATHS_LOGINED.append(f"/{MOD}/{alias}/get_{tblname}.dspy")
PATHS_LOGINED.append(f"/{MOD}/{alias}/add_{tblname}.dspy")
PATHS_LOGINED.append(f"/{MOD}/{alias}/update_{tblname}.dspy")
PATHS_LOGINED.append(f"/{MOD}/{alias}/delete_{tblname}.dspy")
# 角色专属权限 — sale 可查看促销码和客户归属
PATHS_SALE = [
# 促销码(生成+查看)
f"/{MOD}/discount_promo_code_list",
f"/{MOD}/discount_promo_code_list/index.ui",
f"/{MOD}/discount_promo_code_list/get_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/add_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/update_discount_promo_code.dspy",
f"/{MOD}/discount_promo_code_list/delete_discount_promo_code.dspy",
f"/{MOD}/api/promo_code_create.dspy",
f"/{MOD}/api/promo_code_update.dspy",
f"/{MOD}/api/promo_code_delete.dspy",
# 客户归属(查看+手动分配)
f"/{MOD}/discount_customer_bind_list",
f"/{MOD}/discount_customer_bind_list/index.ui",
f"/{MOD}/discount_customer_bind_list/get_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/add_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/update_discount_customer_bind.dspy",
f"/{MOD}/discount_customer_bind_list/delete_discount_customer_bind.dspy",
f"/{MOD}/api/customer_bind_create.dspy",
f"/{MOD}/api/customer_bind_update.dspy",
f"/{MOD}/api/customer_bind_delete.dspy",
]
# logined: 所有 API 文件
for api_path in get_api_files():
PATHS_LOGINED.append(api_path)
# ============================================================
# 角色专属权限
# ============================================================
# operator — 全部折扣管理功能
PATHS_OPERATOR = []
for d in crud_defs:
alias = d["alias"]
tblname = d["tblname"]
PATHS_OPERATOR.append(f"/{MOD}/{alias}")
PATHS_OPERATOR.append(f"/{MOD}/{alias}/index.ui")
PATHS_OPERATOR.append(f"/{MOD}/{alias}/get_{tblname}.dspy")
PATHS_OPERATOR.append(f"/{MOD}/{alias}/add_{tblname}.dspy")
PATHS_OPERATOR.append(f"/{MOD}/{alias}/update_{tblname}.dspy")
PATHS_OPERATOR.append(f"/{MOD}/{alias}/delete_{tblname}.dspy")
# operator 也需要所有 API
for api_path in get_api_files():
PATHS_OPERATOR.append(api_path)
# sale — 促销码、客户归属
PATHS_SALE = []
SALE_TABLES = ["discount_promo_code", "discount_customer_bind"]
SALE_ALIASES = {d["tblname"]: d["alias"] for d in crud_defs}
for tblname in SALE_TABLES:
alias = SALE_ALIASES.get(tblname, tblname + "_list")
PATHS_SALE.append(f"/{MOD}/{alias}")
PATHS_SALE.append(f"/{MOD}/{alias}/index.ui")
PATHS_SALE.append(f"/{MOD}/{alias}/get_{tblname}.dspy")
PATHS_SALE.append(f"/{MOD}/{alias}/add_{tblname}.dspy")
PATHS_SALE.append(f"/{MOD}/{alias}/update_{tblname}.dspy")
PATHS_SALE.append(f"/{MOD}/{alias}/delete_{tblname}.dspy")
# sale 相关 API
for api_path in get_api_files():
basename = os.path.basename(api_path)
if any(k in basename for k in ["promo_code", "customer_bind"]):
PATHS_SALE.append(api_path)
# ============================================================
# 去重
# ============================================================
PATHS_ANY = list(dict.fromkeys(PATHS_ANY))
PATHS_LOGINED = list(dict.fromkeys(PATHS_LOGINED))
PATHS_OPERATOR = list(dict.fromkeys(PATHS_OPERATOR))
PATHS_SALE = list(dict.fromkeys(PATHS_SALE))
print(f"\nPath counts:")
print(f" any: {len(PATHS_ANY)}")
print(f" logined: {len(PATHS_LOGINED)}")
print(f" reseller.operator: {len(PATHS_OPERATOR)}")
print(f" reseller.sale: {len(PATHS_SALE)}")
# ============================================================
# 执行注册
# ============================================================
def run_set_perm(role, path, verbose=True):
"""Register a single permission path."""
cmd = [PYTHON, SET_PERM_SCRIPT, role, path]
result = subprocess.run(cmd, capture_output=True, text=True)
if verbose:
@ -215,7 +193,6 @@ def run_set_perm(role, path, verbose=True):
def register_role_paths(role, paths):
"""Register all paths for a role."""
count = 0
for path in paths:
if run_set_perm(role, path, verbose=False):
@ -225,7 +202,7 @@ def register_role_paths(role, paths):
def main():
print(f"Sage root: {SAGE_ROOT}")
print(f"\nSage root: {SAGE_ROOT}")
print(f"set_role_perm.py: {SET_PERM_SCRIPT}")
print()