"""每个受保护接口必须映射权限;未登记接口默认拒绝。""" RESOURCE_MODULES = { "base.personnel": "base:personnel", "base.products": "base:products", "base.customers": "base:customers", "business.businesses": "business:businesses", "business.shipments": "business:shipments", "business.receipts": "business:receipts", "business.ratings": "business:ratings", "system.users": "system:users", "system.roles": "system:roles", "system.business_roles": "system:business_roles", "system.logs": "system:logs", "system.settings": "system:settings", "system.monitor": "system:monitor", } REPORT_MODULES = { "personal-detail": "reports:personal", "department-summary": "reports:department", "business-stats": "reports:business", "payment-stats": "reports:payments", } PUBLIC_ENDPOINTS = {("auth", "get_captcha"), ("auth", "login"), ("system.settings", "get_settings_public"), ("system.settings", "get_logo_public")} LOGIN_ENDPOINTS = {("auth", "me"), ("auth", "logout")} def permission_for(module, name, method, *, params=None, body=None): """返回目录操作码;None 表示没有登记,空串仅用于显式登录态接口。""" params, body = params or {}, body or {} if (module, name) in PUBLIC_ENDPOINTS | LOGIN_ENDPOINTS: return "" if module == "dashboard": return "dashboard:view" if module == "system.authorization": resource = "system:users" if name in ("user_authorization", "preview", "save_user") else "system:roles" return resource + (":view" if method == "GET" or name == "preview" else ":update") if module == "metadata": return "" # 此端点按请求的参照资源执行独立的关联功能校验。 if module == "base.business_roles": return "" # 下拉只返回选项;由端点校验人员查看/维护能力。 if module in RESOURCE_MODULES: resource = RESOURCE_MODULES[module] if module == "base.products" and name in ("extraction_preview", "extraction_apply", "restore_source"): return resource + (":create" if name == "extraction_apply" else ":update" if name == "restore_source" else ":view") if method == "GET": return resource + ":view" special = {"set_status": "status", "change_status": "status", "reset_password": "password", "replace_regions": "regions", "replace_plans": "plans", "rate_customer": "rating", "change_ownership": "ownership", "upload_logo": "update"} action = special.get(name) or {"POST": "create", "PUT": "update", "DELETE": "delete"}.get(method) return resource + ":" + action if action else None if module == "system.params": action = "view" if method == "GET" else {"submit_change": "update", "withdraw_batch": "withdraw", "preview": "preview"}.get(name) if name == "export_changes": action = "export" return "system:params:" + action if action else None if module == "system.coefficients": return "system:params:" + ({"save": "update", "export": "export", "import_preview": "preview"}.get(name, "view")) if module == "system.calc_templates": return "system:params:" + ("view" if method == "GET" else "update") if module == "system.monitor": return "system:monitor:" + ("export" if name == "diagnostic" else "view") if module == "system.tplus": return "tplus:" + ("view" if method == "GET" else "retry" if name == "retry" else "collect") if module == "system.tplus_workbench": if name == "work_report": return "tplus:export" if method == "GET": return "tplus:view" if name == "save_fact": return "tplus:facts" if name == "create_work": return "tplus:import" if body.get("kind") in ("MASTER_APPLY", "BUSINESS_APPLY") else "tplus:preview" if module in ("payroll", "calc"): if name == "probation_adjustment": return "adjustments:create" if name in ("probation_export", "salary_export"): return "reports:personal:export" if method == "GET": return "payroll:view" return "adjustments:create" if name == "adjustment" else "payroll:prepare" if name == "save_input" else "payroll:calculate" if module == "settlements": if name in ("create_adjustment", "list_adjustments"): return "adjustments:" + ("view" if method == "GET" else "create") return "settlements:" + ("view" if method == "GET" else "generate" if name == "generate" else "submit") if module == "system.approvals": return "approvals:view" # 节点动作由服务层根据当前节点检查 finance/leader。 if module == "reports": code = params.get("code") if name == "export_report" else { "personal_detail": "personal-detail", "department_summary": "department-summary", "business_stats": "business-stats", "payment_stats": "payment-stats", "payment_detail": "payment-stats", "payment_day_buckets": "payment-stats", }.get(name) resource = REPORT_MODULES.get(code) return resource + (":export" if name == "export_report" else ":view") if resource else None return None