Files
junhong_cmp_fiber/internal/service/permission/service.go
huang 46e4e5f4f1
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m22s
refactor: 将 DTO 文件从 internal/model 移动到 internal/model/dto 目录
- 移动 17 个 DTO 文件到 internal/model/dto/ 目录
- 更新所有 DTO 文件的 package 声明从 model 改为 dto
- 更新所有引用文件的 import 和类型引用
  - Handler 层:admin 和 h5 所有处理器
  - Service 层:所有业务服务
  - Routes 层:所有路由定义
  - Tests 层:单元测试和集成测试
- 清理未使用的 import 语句
- 验证:项目构建成功,测试编译通过,LSP 无错误
2026-01-22 10:15:04 +08:00

347 lines
9.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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/model/dto"
"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 *dto.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 *dto.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 *dto.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) ([]*dto.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) []*dto.PermissionTreeNode {
nodeMap := make(map[uint]*dto.PermissionTreeNode)
for _, p := range permissions {
nodeMap[p.ID] = &dto.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([]*dto.PermissionTreeNode, 0),
}
}
var roots []*dto.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
}