From daa6e66d09f8298bba0eba4025f9b8a4dff7f397 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 24 Jun 2026 01:16:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=87=8D=E5=86=99load=5Fpath.py?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E5=85=A8RBAC=E6=9D=83=E9=99=90=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:discount_list/discount_detail_list/discount_customer_bind_list 返回403 原因:缺少CRUD目录路径、auto-generated .dspy、角色专属权限 修复: - PATHS_ANY: 添加5个CRUD目录别名 - PATHS_LOGINED: 添加25个CRUD auto-generated .dspy - PATHS_OPERATOR: reseller.operator可管理全部折扣功能 - PATHS_SALE: reseller.sale可查看促销码和客户归属 --- scripts/load_path.py | 200 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 171 insertions(+), 29 deletions(-) diff --git a/scripts/load_path.py b/scripts/load_path.py index d941118..4e3c4ce 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -4,9 +4,15 @@ discount 模块 RBAC 权限管理脚本 使用方法: cd ~/repos/sage - ./py3/bin/python ~/discount/scripts/load_path.py + ./py3/bin/python ~/repos/discount/scripts/load_path.py 每次代码变更如有新 path 出现,需同步更新此脚本。 + +路径分类: + - any: 静态资源/菜单/CRUD别名目录 + - logined: 需要认证的页面和 API + - reseller.operator: 运营角色 — 折扣方案、营销方案、客户归属管理 + - reseller.sale: 销售角色 — 促销码、客户归属查看 """ import subprocess @@ -32,65 +38,201 @@ if not SAGE_ROOT: PYTHON = os.path.join(SAGE_ROOT, "py3", "bin", "python") SET_PERM_SCRIPT = os.path.join(SAGE_ROOT, "set_role_perm.py") +if not os.path.exists(SET_PERM_SCRIPT): + print(f"ERROR: set_role_perm.py not found at {SET_PERM_SCRIPT}") + sys.exit(1) + MOD = "discount" # ============================================================ # 权限路径定义 — 每次新增页面或API时同步更新 # ============================================================ -# any — 无需登录(菜单、登录页等) +# any — 无需登录(菜单、CRUD别名目录) PATHS_ANY = [ - f"/discount/menu.ui",] + 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 PATHS_LOGINED = [ - f"/discount", - f"/discount/discount_detail_list", - f"/discount/discount_list", - f"/discount/generate_qr.dspy", - f"/discount/promote", - f"/discount/promote.ui", - f"/discount/promote/index.dspy", - f"/discount/discount_marketing_list", - f"/discount/discount_marketing_list/index.ui", - f"/discount/discount_promo_code_list", - f"/discount/discount_promo_code_list/index.ui", - f"/discount/discount_customer_bind_list", - f"/discount/discount_customer_bind_list/index.ui", - f"/discount/api/marketing_create.dspy", - f"/discount/api/marketing_update.dspy", - f"/discount/api/marketing_delete.dspy", - f"/discount/api/promo_code_create.dspy", - f"/discount/api/promo_code_update.dspy", - f"/discount/api/promo_code_delete.dspy", - f"/discount/api/customer_bind_create.dspy", - f"/discount/api/customer_bind_update.dspy", - f"/discount/api/customer_bind_delete.dspy",] + # 模块入口 + f"/{MOD}", + f"/{MOD}/index.ui", + # 功能页面 + f"/{MOD}/generate_qr.dspy", + 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/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", +] + +# 角色专属权限 — 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", +] # ============================================================ # 执行注册 # ============================================================ -def run_set_perm(role, path): +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: + output = result.stdout.strip() + if output: + print(f" {role}: {path} -> {output}") return result.returncode == 0 + def register_role_paths(role, paths): + """Register all paths for a role.""" count = 0 - for p in paths: - if run_set_perm(role, p): + for path in paths: + if run_set_perm(role, path, verbose=False): count += 1 print(f" {role}: {count}/{len(paths)} paths registered") return count + def main(): print(f"Sage root: {SAGE_ROOT}") + print(f"set_role_perm.py: {SET_PERM_SCRIPT}") + print() + total = 0 + + print("[1/4] Registering 'any' role paths...") total += register_role_paths("any", PATHS_ANY) + + print("[2/4] Registering 'logined' role paths...") total += register_role_paths("logined", PATHS_LOGINED) - print(f"\nDone. Total {total} permission entries registered.") + + print("[3/4] Registering 'reseller.operator' role paths...") + total += register_role_paths("reseller.operator", PATHS_OPERATOR) + + print("[4/4] Registering 'reseller.sale' role paths...") + total += register_role_paths("reseller.sale", PATHS_SALE) + + print() + print(f"Done. Total {total} permission entries registered.") + print() print("NOTE: Restart Sage after permission changes to reload RBAC cache.") + if __name__ == "__main__": main()