| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- // 权限点/角色矩阵仓储(T6.5):t_permission + t_role_permission 的**平台级**数据访问。
- //
- // 与 InstitutionRepo/CampusRepo 的关键差异(架构规格 §3.5):
- // - **不继承 scope.BaseRepo**:t_permission/t_role_permission 是平台级 RBAC 字典
- // (同 t_role 角色字典的处置:平台级表不进 scoping,见 P0-11 收敛规则 7),
- // 直接用裸 *gorm.DB;
- // - 多租户防线不在此层:权限点矩阵本身是"角色 → 可见菜单/按钮"的全局配置,
- // 与机构/校区无关;作用域越权防线在 Service 层(assertInScope,T6)+
- // 路由角色白名单(T6.5-d)。
- //
- // 依赖方向铁律:org repository → org/model + infra/errors(全下层/同域),
- // 不依赖 handler/service。
- package repository
- import (
- "context"
- stderrors "errors"
- "gorm.io/gorm"
- apperrors "yuansiwei/unified-backend/internal/infra/errors"
- "yuansiwei/unified-backend/internal/org/model"
- )
- // mapGormErr gorm 原错 → sentinel(与 identity/repository 同名同语义:
- // ErrRecordNotFound→ErrNotFound,其它→ErrDBError;平台级仓储统一出口)。
- func mapGormErr(err error) *apperrors.AppError {
- switch {
- case err == nil:
- return nil
- case stderrors.Is(err, gorm.ErrRecordNotFound):
- return apperrors.WithCause(apperrors.ErrNotFound, err)
- default:
- return apperrors.WithCause(apperrors.ErrDBError, err)
- }
- }
- // PermissionRepo 权限点字典 + 角色矩阵仓储(平台级)。
- type PermissionRepo struct {
- db *gorm.DB
- }
- // NewPermissionRepo 构造权限点/角色矩阵仓储。
- //
- // 入参:db *gorm.DB:数据库句柄(必填)。
- // 出参:*PermissionRepo:就绪仓储。
- func NewPermissionRepo(db *gorm.DB) *PermissionRepo {
- return &PermissionRepo{db: db}
- }
- // AllActive 取全部 active 权限点(菜单树构建用;按 sort 升序,构树方自行按 parent_code 组装)。
- //
- // 入参:ctx 请求上下文。
- // 出参:
- // - []model.Permission:全部 active 权限点(**非 nil**,空时为 [];disabled 不返回);
- // - error:ErrDBError(DB 异常)。
- //
- // 实现说明:权限点是平台级字典(量级 < 百),一次全取 + 进程内缓存(rbac 包),
- // 不做分页;Order(sort ASC, code ASC) 保证同级排序稳定。
- func (r *PermissionRepo) AllActive(ctx context.Context) ([]model.Permission, error) {
- var list []model.Permission
- if err := r.db.WithContext(ctx).
- Where("status = ?", model.PermissionStatusActive).
- Order("sort ASC, code ASC").
- Find(&list).Error; err != nil {
- return nil, mapGormErr(err)
- }
- if list == nil {
- list = []model.Permission{}
- }
- return list, nil
- }
- // RolePermissionCodes 取某角色的全部权限点 code(去重、升序)。
- //
- // 入参:ctx 请求上下文;roleCode 角色码(t_role 字典值)。
- // 出参:
- // - []string:权限点 code 切片(**非 nil**,无授权时为 []string{}——前端按空集处理:
- // 菜单空 + 按钮全隐;超管的"全量放行"在 rbac 包/中间件层实现,不经本方法);
- // - error:ErrDBError(DB 异常)。
- func (r *PermissionRepo) RolePermissionCodes(ctx context.Context, roleCode string) ([]string, error) {
- var codes []string
- if err := r.db.WithContext(ctx).
- Model(&model.RolePermission{}).
- Where("role_code = ?", roleCode).
- Distinct().
- Order("permission_code ASC").
- Pluck("permission_code", &codes).Error; err != nil {
- return nil, mapGormErr(err)
- }
- if codes == nil {
- codes = []string{}
- }
- return codes, nil
- }
- // SaveRolePermissions 全量保存某角色的权限点矩阵(超管配置端点,T6.5-e)。
- //
- // 入参:
- // - ctx 请求上下文;
- // - roleCode 角色码(必须为 t_role 字典内角色,调用方校验;本方法只做矩阵维护);
- // - permissionCodes []string:目标全量权限点集合(可为空 = 清空该角色矩阵)。
- //
- // 出参:error——ErrDBError(DB 异常,含事务回滚)。
- //
- // 语义:**删旧插新**(事务内先删该角色全部矩阵行,再批量插入目标集合)——
- // "保存即全量替换",避免差集计算;空集合 = 清空(前端"取消全部勾选"场景合法)。
- // 幂等:目标集合与原矩阵一致时等价于原样重插(先删后插,结果相同)。
- func (r *PermissionRepo) SaveRolePermissions(ctx context.Context, roleCode string, permissionCodes []string) error {
- return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
- if err := tx.Where("role_code = ?", roleCode).Delete(&model.RolePermission{}).Error; err != nil {
- return err
- }
- if len(permissionCodes) == 0 {
- return nil // 清空语义(合法:角色无任何可见菜单/按钮)
- }
- rows := make([]model.RolePermission, 0, len(permissionCodes))
- for _, code := range permissionCodes {
- rows = append(rows, model.RolePermission{RoleCode: roleCode, PermissionCode: code})
- }
- return tx.Create(&rows).Error // GORM 批量 Create(切片)
- })
- }
|