// Package permission 提供权限管理的业务逻辑服务 // 包含权限创建、查询、更新、删除、权限检查等功能 package permission import ( "context" "encoding/json" "fmt" "regexp" "time" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/store" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/middleware" "github.com/redis/go-redis/v9" "gorm.io/gorm" ) // permCodeRegex 权限编码格式验证正则(module:action) var permCodeRegex = regexp.MustCompile(`^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$`) // Service 权限业务服务 type Service struct { permissionStore *postgres.PermissionStore accountRoleStore *postgres.AccountRoleStore rolePermStore *postgres.RolePermissionStore redisClient *redis.Client } // New 创建权限服务 func New( permissionStore *postgres.PermissionStore, accountRoleStore *postgres.AccountRoleStore, rolePermStore *postgres.RolePermissionStore, redisClient *redis.Client, ) *Service { return &Service{ permissionStore: permissionStore, accountRoleStore: accountRoleStore, rolePermStore: rolePermStore, redisClient: redisClient, } } // Create 创建权限 func (s *Service) Create(ctx context.Context, req *model.CreatePermissionRequest) (*model.Permission, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 验证权限编码格式 if !permCodeRegex.MatchString(req.PermCode) { return nil, errors.New(errors.CodeInvalidPermCode, "权限编码格式不正确(应为 module:action 格式)") } // 检查权限编码唯一性 existing, err := s.permissionStore.GetByCode(ctx, req.PermCode) if err == nil && existing != nil { return nil, errors.New(errors.CodePermCodeExists, "权限编码已存在") } // 验证 parent_id 存在(如果提供) if req.ParentID != nil { parent, err := s.permissionStore.GetByID(ctx, *req.ParentID) if err != nil || parent == nil { return nil, errors.New(errors.CodeNotFound, "上级权限不存在") } } // 创建权限 permission := &model.Permission{ PermName: req.PermName, PermCode: req.PermCode, PermType: req.PermType, Platform: req.Platform, URL: req.URL, ParentID: req.ParentID, Sort: req.Sort, Status: constants.StatusEnabled, } // 如果未指定 platform,默认为 all if permission.Platform == "" { permission.Platform = constants.PlatformAll } if err := s.permissionStore.Create(ctx, permission); err != nil { return nil, fmt.Errorf("创建权限失败: %w", err) } return permission, nil } // Get 获取权限 func (s *Service) Get(ctx context.Context, id uint) (*model.Permission, error) { permission, err := s.permissionStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodePermissionNotFound, "权限不存在") } return nil, fmt.Errorf("获取权限失败: %w", err) } return permission, nil } // Update 更新权限 func (s *Service) Update(ctx context.Context, id uint, req *model.UpdatePermissionRequest) (*model.Permission, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 获取现有权限 permission, err := s.permissionStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodePermissionNotFound, "权限不存在") } return nil, fmt.Errorf("获取权限失败: %w", err) } // 更新字段 if req.PermName != nil { permission.PermName = *req.PermName } if req.PermCode != nil { // 验证权限编码格式 if !permCodeRegex.MatchString(*req.PermCode) { return nil, errors.New(errors.CodeInvalidPermCode, "权限编码格式不正确(应为 module:action 格式)") } // 检查新权限编码唯一性 existing, err := s.permissionStore.GetByCode(ctx, *req.PermCode) if err == nil && existing != nil && existing.ID != id { return nil, errors.New(errors.CodePermCodeExists, "权限编码已存在") } permission.PermCode = *req.PermCode } if req.Platform != nil { permission.Platform = *req.Platform } if req.URL != nil { permission.URL = *req.URL } if req.ParentID != nil { // 验证 parent_id 存在 parent, err := s.permissionStore.GetByID(ctx, *req.ParentID) if err != nil || parent == nil { return nil, errors.New(errors.CodeNotFound, "上级权限不存在") } permission.ParentID = req.ParentID } if req.Sort != nil { permission.Sort = *req.Sort } if req.Status != nil { permission.Status = *req.Status } permission.Updater = currentUserID if err := s.permissionStore.Update(ctx, permission); err != nil { return nil, fmt.Errorf("更新权限失败: %w", err) } return permission, nil } // Delete 软删除权限 func (s *Service) Delete(ctx context.Context, id uint) error { // 检查权限存在 _, err := s.permissionStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodePermissionNotFound, "权限不存在") } return fmt.Errorf("获取权限失败: %w", err) } if err := s.permissionStore.Delete(ctx, id); err != nil { return fmt.Errorf("删除权限失败: %w", err) } return nil } // List 查询权限列表 func (s *Service) List(ctx context.Context, req *model.PermissionListRequest) ([]*model.Permission, int64, error) { opts := &store.QueryOptions{ Page: req.Page, PageSize: req.PageSize, OrderBy: "sort ASC, id ASC", } if opts.Page == 0 { opts.Page = 1 } if opts.PageSize == 0 { opts.PageSize = constants.DefaultPageSize } filters := make(map[string]interface{}) if req.PermName != "" { filters["perm_name"] = req.PermName } if req.PermCode != "" { filters["perm_code"] = req.PermCode } if req.PermType != nil { filters["perm_type"] = *req.PermType } if req.Platform != "" { filters["platform"] = req.Platform } if req.AvailableForRoleType != nil { filters["available_for_role_type"] = *req.AvailableForRoleType } if req.ParentID != nil { filters["parent_id"] = *req.ParentID } if req.Status != nil { filters["status"] = *req.Status } return s.permissionStore.List(ctx, opts, filters) } // GetTree 获取权限树 func (s *Service) GetTree(ctx context.Context, availableForRoleType *int) ([]*model.PermissionTreeNode, error) { permissions, err := s.permissionStore.GetAll(ctx, availableForRoleType) if err != nil { return nil, fmt.Errorf("获取权限列表失败: %w", err) } return buildPermissionTree(permissions), nil } // buildPermissionTree 构建权限树 func buildPermissionTree(permissions []*model.Permission) []*model.PermissionTreeNode { nodeMap := make(map[uint]*model.PermissionTreeNode) for _, p := range permissions { nodeMap[p.ID] = &model.PermissionTreeNode{ ID: p.ID, PermName: p.PermName, PermCode: p.PermCode, PermType: p.PermType, Platform: p.Platform, AvailableForRoleTypes: p.AvailableForRoleTypes, URL: p.URL, Sort: p.Sort, Children: make([]*model.PermissionTreeNode, 0), } } var roots []*model.PermissionTreeNode for _, p := range permissions { node := nodeMap[p.ID] if p.ParentID == nil || *p.ParentID == 0 { roots = append(roots, node) } else if parent, ok := nodeMap[*p.ParentID]; ok { parent.Children = append(parent.Children, node) } else { roots = append(roots, node) } } return roots } // permissionCacheItem 权限缓存项 type permissionCacheItem struct { PermCode string `json:"perm_code"` Platform string `json:"platform"` } // CheckPermission 检查用户是否拥有指定权限(实现 PermissionChecker 接口) // userID: 用户ID // permCode: 权限编码 // platform: 端口类型 (all/web/h5) func (s *Service) CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error) { userType := middleware.GetUserTypeFromContext(ctx) if userType == constants.UserTypeSuperAdmin { return true, nil } cacheKey := constants.RedisUserPermissionsKey(userID) cachedData, err := s.redisClient.Get(ctx, cacheKey).Result() if err == nil && cachedData != "" { var permissions []permissionCacheItem if err := json.Unmarshal([]byte(cachedData), &permissions); err == nil { return s.matchPermission(permissions, permCode, platform), nil } } roleIDs, err := s.accountRoleStore.GetRoleIDsByAccountID(ctx, userID) if err != nil { return false, fmt.Errorf("查询用户角色失败: %w", err) } if len(roleIDs) == 0 { return false, nil } permIDs, err := s.rolePermStore.GetPermIDsByRoleIDs(ctx, roleIDs) if err != nil { return false, fmt.Errorf("查询角色权限失败: %w", err) } if len(permIDs) == 0 { return false, nil } permissions, err := s.permissionStore.GetByIDs(ctx, permIDs) if err != nil { return false, fmt.Errorf("查询权限详情失败: %w", err) } cacheItems := make([]permissionCacheItem, 0, len(permissions)) for _, perm := range permissions { cacheItems = append(cacheItems, permissionCacheItem{ PermCode: perm.PermCode, Platform: perm.Platform, }) } if cacheData, err := json.Marshal(cacheItems); err == nil { s.redisClient.Set(ctx, cacheKey, cacheData, 30*time.Minute) } return s.matchPermission(cacheItems, permCode, platform), nil } func (s *Service) matchPermission(permissions []permissionCacheItem, permCode string, platform string) bool { for _, perm := range permissions { if perm.PermCode == permCode { if perm.Platform == constants.PlatformAll || perm.Platform == platform { return true } } } return false }