| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- // 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)
- }
|