// RBAC 角色-权限点矩阵配置端点(T6.5-e,超管可配置后台,2026-11-06 用户拍板 #2)。 // // 端点(平台级,不带多租户作用域;访问入口由路由角色白名单 RequireRole("super_admin") 限定): // - GET /org/role-permissions/:roleCode 查某角色当前矩阵 + 全量权限点字典(超管配置页渲染勾选树) // - PUT /org/role-permissions/:roleCode 全量保存某角色矩阵(删旧插新;保存后清本实例 RBAC 缓存) // // 安全模型(RBAC 三层第②层的"配置侧"): // - 路由挂 RequireRole("super_admin"):仅平台超管可读写(其他角色 1003;超管恒放行); // - 服务层校验 roleCode ∈ t_role 字典 + 权限码 ∈ t_permission 字典(防保存脏 code 污染矩阵); // - 保存成功后 Resolver.Invalidate() 清本实例缓存(跨实例靠 TTL 30s 自然一致)。 // // 与同包 handler.Handler(机构/校区只读)的分工:本 handler 面向平台级 RBAC 字典 // (t_role / t_permission / t_role_permission),**不走 scope**(无机构/校区过滤—— // 全局配置与租户无关,越权防线完全由路由角色白名单承担)。 package handler import ( "encoding/json" "github.com/gofiber/fiber/v2" apperrors "yuansiwei/unified-backend/internal/infra/errors" "yuansiwei/unified-backend/internal/infra/response" "yuansiwei/unified-backend/internal/org/service" ) // RBACHandler RBAC 矩阵配置 HTTP 处理器(启动装配一次,全进程共享)。 // // 字段: // - svc:RBAC 矩阵配置服务(List/Save 编排,见 org/service.rbac_service.go)。 type RBACHandler struct { svc *service.RolePermissionService } // RBACConfig RBAC handler 装配参数(T6.5-e main 按此构造)。 type RBACConfig struct { // Svc RBAC 矩阵配置服务(必填,nil 时 NewRBAC 报错 fail-fast)。 Svc *service.RolePermissionService } // NewRBAC 构造 RBAC 矩阵配置处理器(fail-fast:Svc 未注入不允许挂载端点)。 // // 出参:(*RBACHandler, error)——Svc 为 nil 时 error(装配层 fail-fast)。 func NewRBAC(cfg RBACConfig) (*RBACHandler, error) { if cfg.Svc == nil { return nil, apperrors.New(apperrors.CodeInternal, "org rbac handler: RolePermissionService 未注入") } return &RBACHandler{svc: cfg.Svc}, nil } // saveRolePermReq PUT /org/role-permissions/:roleCode 请求体。 // // 字段:Permissions []string——目标全量权限点 code 集(可为空 = 清空该角色矩阵; // 每项须存在于 t_permission;空切片/缺失均合法)。 type saveRolePermReq struct { Permissions []string `json:"permissions"` } // RolePermissions 查某角色的矩阵 + 全量权限点字典(GET /org/role-permissions/:roleCode,超管)。 // // 出参(成功):code 200,data 为 service.RolePermissionView // // {roleCode, assigned[], dictionary[]}(assigned=该角色当前权限码;dictionary=全量 active 权限点,前端构树勾选)。 // // 出参(失败):code 2001(roleCode 不在 t_role 字典)/ 1003(非超管,RequireRole 拦截)/ 5002(DB)。 func (h *RBACHandler) RolePermissions(c *fiber.Ctx) error { roleCode := c.Params("roleCode") view, err := h.svc.ListRolePermissions(c.Context(), roleCode) if err != nil { return response.Fail(c, err) } return response.OK(c, view) } // SaveRolePermissions 全量保存某角色的权限点矩阵(PUT /org/role-permissions/:roleCode,超管)。 // // 入参:请求体 {permissions: ["域.资源.动作", ...]}(可空 = 清空;每项须存在于 t_permission)。 // 出参(成功):code 200,data 省略(操作成功无数据体);保存后清本实例 RBAC 缓存。 // 出参(失败):code 2001(roleCode 非法 / 含未知权限码 / 请求体解析失败)/ 1003(非超管)/ 5002(DB 含事务回滚)。 func (h *RBACHandler) SaveRolePermissions(c *fiber.Ctx) error { var req saveRolePermReq // 请求体可空(= 清空矩阵);非空但非 JSON 时校验失败 → 2002 if len(c.Body()) > 0 { if err := json.Unmarshal(c.Body(), &req); err != nil { return response.Fail(c, apperrors.New(apperrors.CodeBadFormat, "请求体 JSON 解析失败")) } } roleCode := c.Params("roleCode") if err := h.svc.SaveRolePermissions(c.Context(), roleCode, req.Permissions); err != nil { return response.Fail(c, err) } return response.OK(c, nil) }