// Package role 提供角色管理的业务逻辑服务 // 包含角色创建、查询、更新、删除、角色权限关联等功能 package role import ( "context" "fmt" "strings" "time" accessauditapp "github.com/break/junhong_cmp_fiber/internal/application/accessaudit" "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" ) // Service 角色业务服务 type Service struct { db *gorm.DB redisClient *redis.Client accessAudit accessauditapp.Writer roleStore *postgres.RoleStore permissionStore *postgres.PermissionStore rolePermissionStore *postgres.RolePermissionStore accountRoleStore *postgres.AccountRoleStore shopRoleStore *postgres.ShopRoleStore } // SetAccessAudit 注入角色与权限配置的事务审计接缝。 func (s *Service) SetAccessAudit(db *gorm.DB, redisClient *redis.Client, writer accessauditapp.Writer) { s.db = db s.redisClient = redisClient s.accessAudit = writer } // New 创建角色服务 func New(roleStore *postgres.RoleStore, permissionStore *postgres.PermissionStore, rolePermissionStore *postgres.RolePermissionStore, accountRoleStore *postgres.AccountRoleStore, shopRoleStore *postgres.ShopRoleStore) *Service { return &Service{ roleStore: roleStore, permissionStore: permissionStore, rolePermissionStore: rolePermissionStore, accountRoleStore: accountRoleStore, shopRoleStore: shopRoleStore, } } // Create 创建角色 func (s *Service) Create(ctx context.Context, req *dto.CreateRoleRequest) (*dto.RoleResponse, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } role := &model.Role{ RoleName: req.RoleName, RoleDesc: req.RoleDesc, RoleType: req.RoleType, Status: constants.StatusEnabled, BaseModel: model.BaseModel{ Creator: currentUserID, Updater: currentUserID, }, } // 检查角色名是否已存在 exists, err := s.roleStore.ExistsByName(ctx, req.RoleName, 0) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleCreated, "创建角色失败", constants.AuditResultFailed, role, nil, nil, err) return nil, errors.Wrap(errors.CodeInternalError, err, "检查角色名失败") } if exists { appErr := errors.New(errors.CodeRoleNameExists) s.recordFailure(ctx, constants.AuditActionRoleCreated, "拒绝创建重复角色", constants.AuditResultDenied, role, nil, nil, appErr) return nil, appErr } if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRoleStore(tx).Create(ctx, role); err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRoleCreated, Summary: "创建角色", Result: constants.AuditResultSuccess, OperatorID: currentUserID, Role: role, AfterData: roleAuditData(role), }) }); err != nil { role.ID = 0 s.recordFailure(ctx, constants.AuditActionRoleCreated, "创建角色失败", constants.AuditResultFailed, role, nil, nil, err) return nil, errors.Wrap(errors.CodeInternalError, err, "创建角色失败") } return toResponse(role), nil } // Get 获取角色 func (s *Service) Get(ctx context.Context, id uint) (*dto.RoleResponse, error) { role, err := s.roleStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeRoleNotFound, "角色不存在") } return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } return toResponse(role), nil } // Update 更新角色 func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleRequest) (*dto.RoleResponse, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 获取现有角色 role, err := s.roleStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeRoleNotFound, "角色不存在") } return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } beforeData := roleAuditData(role) // 如果修改了角色名,检查是否与其他角色重复 if req.RoleName != nil && *req.RoleName != role.RoleName { exists, err := s.roleStore.ExistsByName(ctx, *req.RoleName, id) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, nil, beforeData, err) return nil, errors.Wrap(errors.CodeInternalError, err, "检查角色名失败") } if exists { appErr := errors.New(errors.CodeRoleNameExists) s.recordFailure(ctx, constants.AuditActionRoleUpdated, "拒绝更新重复角色名", constants.AuditResultDenied, role, nil, beforeData, appErr) return nil, appErr } role.RoleName = *req.RoleName } // 更新其他字段 if req.RoleDesc != nil { role.RoleDesc = *req.RoleDesc } if req.Status != nil { role.Status = *req.Status } role.Updater = currentUserID if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRoleStore(tx).Update(ctx, role); err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRoleUpdated, Summary: "更新角色", Result: constants.AuditResultSuccess, OperatorID: currentUserID, Role: role, BeforeData: beforeData, AfterData: roleAuditData(role), }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, nil, beforeData, err) return nil, errors.Wrap(errors.CodeInternalError, err, "更新角色失败") } return toResponse(role), nil } // Delete 软删除角色 func (s *Service) Delete(ctx context.Context, id uint) error { role, err := s.roleStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeRoleNotFound, "角色不存在") } return errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err) return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败") } shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err) return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败") } if accountCount > 0 || shopCount > 0 { appErr := errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺,请先移除相关分配后再删除", accountCount, shopCount)) s.recordFailure(ctx, constants.AuditActionRoleDeleted, "拒绝删除使用中的角色", constants.AuditResultDenied, role, nil, roleAuditData(role), appErr) return appErr } operatorID := middleware.GetUserIDFromContext(ctx) beforeData := roleAuditData(role) if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRoleStore(tx).Delete(ctx, id); err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRoleDeleted, Summary: "删除角色", Result: constants.AuditResultSuccess, OperatorID: operatorID, Role: role, BeforeData: beforeData, AfterData: map[string]any{"deleted": true}, }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, beforeData, err) return errors.Wrap(errors.CodeInternalError, err, "删除角色失败") } return nil } // List 查询角色列表 func (s *Service) List(ctx context.Context, req *dto.RoleListRequest) ([]*dto.RoleResponse, int64, error) { opts := &store.QueryOptions{ Page: req.Page, PageSize: req.PageSize, OrderBy: "id DESC", } if opts.Page == 0 { opts.Page = 1 } if opts.PageSize == 0 { opts.PageSize = constants.DefaultPageSize } filters := make(map[string]interface{}) if req.RoleName != "" { filters["role_name"] = req.RoleName } if req.RoleType != nil { filters["role_type"] = *req.RoleType } if req.Status != nil { filters["status"] = *req.Status } roles, total, err := s.roleStore.List(ctx, opts, filters) if err != nil { return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询角色列表失败") } result := make([]*dto.RoleResponse, 0, len(roles)) for _, role := range roles { result = append(result, toResponse(role)) } return result, total, nil } // AssignPermissions 为角色分配权限 func (s *Service) AssignPermissions(ctx context.Context, roleID uint, permIDs []uint) ([]*model.RolePermission, error) { currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } role, err := s.roleStore.GetByID(ctx, roleID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeRoleNotFound, "角色不存在") } return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } permissions, err := s.permissionStore.GetByIDs(ctx, permIDs) if err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsAssigned, "分配角色权限失败", constants.AuditResultFailed, role, nil, nil, err) return nil, errors.Wrap(errors.CodeInternalError, err, "获取权限失败") } if len(permissions) != len(permIDs) { appErr := errors.New(errors.CodePermissionNotFound, "部分权限不存在") s.recordFailure(ctx, constants.AuditActionRolePermissionsAssigned, "拒绝分配不存在的权限", constants.AuditResultDenied, role, nil, nil, appErr) return nil, appErr } roleTypeStr := fmt.Sprintf("%d", role.RoleType) var invalidPermIDs []uint for _, perm := range permissions { if !contains(perm.AvailableForRoleTypes, roleTypeStr) { invalidPermIDs = append(invalidPermIDs, perm.ID) } } if len(invalidPermIDs) > 0 { appErr := errors.New(errors.CodeInvalidParam, fmt.Sprintf("权限 %v 不适用于此角色类型", invalidPermIDs)) s.recordFailure(ctx, constants.AuditActionRolePermissionsAssigned, "拒绝分配不适用的权限", constants.AuditResultDenied, role, permissionAuditChanges(permissions, nil, nil), nil, appErr) return nil, appErr } // 批量获取已有权限集合,避免逐条 Exists 查询 existingPermIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID) if err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsAssigned, "分配角色权限失败", constants.AuditResultFailed, role, nil, nil, err) return nil, errors.Wrap(errors.CodeInternalError, err, "获取已有权限失败") } existingSet := make(map[uint]bool, len(existingPermIDs)) for _, id := range existingPermIDs { existingSet[id] = true } var rps []*model.RolePermission changedPermissions := make([]*model.Permission, 0, len(permissions)) permissionByID := make(map[uint]*model.Permission, len(permissions)) for _, permission := range permissions { permissionByID[permission.ID] = permission } for _, permID := range permIDs { if existingSet[permID] { continue } rp := &model.RolePermission{ RoleID: roleID, PermID: permID, Status: constants.StatusEnabled, } rps = append(rps, rp) changedPermissions = append(changedPermissions, permissionByID[permID]) } beforeData := map[string]any{"permission_ids": existingPermIDs} afterData := map[string]any{"permission_ids": appendPermissionIDs(existingPermIDs, rps)} if len(rps) == 0 { return rps, nil } var accountIDs []uint if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { store := postgres.NewRolePermissionStore(tx, nil) for _, rp := range rps { if err := store.Create(ctx, rp); err != nil { return err } } var err error accountIDs, err = rolePermissionCacheAccountIDs(ctx, tx, role.ID) if err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRolePermissionsAssigned, Summary: "分配角色权限", Result: constants.AuditResultSuccess, OperatorID: currentUserID, Role: role, Permissions: permissionAuditChanges(changedPermissions, map[string]any{"assigned": false}, map[string]any{"assigned": true}), BeforeData: beforeData, AfterData: afterData, }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsAssigned, "分配角色权限失败", constants.AuditResultFailed, role, permissionAuditChanges(changedPermissions, map[string]any{"assigned": false}, nil), beforeData, err) return nil, errors.Wrap(errors.CodeInternalError, err, "创建角色-权限关联失败") } s.clearRolePermissionCaches(ctx, accountIDs) return rps, nil } // GetPermissions 获取角色的所有权限 func (s *Service) GetPermissions(ctx context.Context, roleID uint) ([]*model.Permission, error) { // 检查角色存在 _, err := s.roleStore.GetByID(ctx, roleID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeRoleNotFound, "角色不存在") } return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } // 获取权限 ID 列表 permIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID) if err != nil { return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色权限 ID 失败") } if len(permIDs) == 0 { return []*model.Permission{}, nil } // 获取权限详情 return s.permissionStore.GetByIDs(ctx, permIDs) } // RemovePermission 移除角色的权限 func (s *Service) RemovePermission(ctx context.Context, roleID, permID uint) error { role, err := s.roleStore.GetByID(ctx, roleID) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeRoleNotFound, "角色不存在") } return errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } existingPermIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID) if err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionRemoved, "移除角色权限失败", constants.AuditResultFailed, role, nil, nil, err) return errors.Wrap(errors.CodeInternalError, err, "获取角色权限失败") } if !containsUint(existingPermIDs, permID) { return nil } permission, permissionErr := s.permissionStore.GetByID(ctx, permID) if permissionErr != nil && permissionErr != gorm.ErrRecordNotFound { s.recordFailure(ctx, constants.AuditActionRolePermissionRemoved, "移除角色权限失败", constants.AuditResultFailed, role, nil, map[string]any{"permission_ids": existingPermIDs}, permissionErr) return errors.Wrap(errors.CodeInternalError, permissionErr, "获取待移除权限失败") } changes := []accessauditapp.PermissionChange(nil) if permissionErr == nil { changes = permissionAuditChanges([]*model.Permission{permission}, map[string]any{"assigned": true}, map[string]any{"assigned": false}) } operatorID := middleware.GetUserIDFromContext(ctx) beforeData := map[string]any{"permission_ids": existingPermIDs} afterData := map[string]any{"permission_ids": removePermissionIDs(existingPermIDs, map[uint]struct{}{permID: {}})} var accountIDs []uint if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRolePermissionStore(tx, nil).Delete(ctx, roleID, permID); err != nil { return err } var err error accountIDs, err = rolePermissionCacheAccountIDs(ctx, tx, role.ID) if err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRolePermissionRemoved, Summary: "移除角色权限", Result: constants.AuditResultSuccess, OperatorID: operatorID, Role: role, Permissions: changes, BeforeData: beforeData, AfterData: afterData, }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionRemoved, "移除角色权限失败", constants.AuditResultFailed, role, changes, beforeData, err) return errors.Wrap(errors.CodeInternalError, err, "删除角色-权限关联失败") } s.clearRolePermissionCaches(ctx, accountIDs) return nil } // BatchRemovePermissions 批量移除角色的权限 func (s *Service) BatchRemovePermissions(ctx context.Context, roleID uint, permIDs []uint) error { role, err := s.roleStore.GetByID(ctx, roleID) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeRoleNotFound, "角色不存在") } return errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } existingPermIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID) if err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsBatchRemoved, "批量移除角色权限失败", constants.AuditResultFailed, role, nil, nil, err) return errors.Wrap(errors.CodeInternalError, err, "获取角色权限失败") } removeSet := make(map[uint]struct{}, len(permIDs)) actualIDs := make([]uint, 0, len(permIDs)) for _, permID := range permIDs { removeSet[permID] = struct{}{} if containsUint(existingPermIDs, permID) { actualIDs = append(actualIDs, permID) } } if len(actualIDs) == 0 { return nil } permissions, err := s.permissionStore.GetByIDs(ctx, actualIDs) if err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsBatchRemoved, "批量移除角色权限失败", constants.AuditResultFailed, role, nil, map[string]any{"permission_ids": existingPermIDs}, err) return errors.Wrap(errors.CodeInternalError, err, "获取待移除权限失败") } changes := permissionAuditChanges(permissions, map[string]any{"assigned": true}, map[string]any{"assigned": false}) operatorID := middleware.GetUserIDFromContext(ctx) beforeData := map[string]any{"permission_ids": existingPermIDs} afterData := map[string]any{"permission_ids": removePermissionIDs(existingPermIDs, removeSet)} var accountIDs []uint if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRolePermissionStore(tx, nil).BatchDelete(ctx, roleID, permIDs); err != nil { return err } var err error accountIDs, err = rolePermissionCacheAccountIDs(ctx, tx, role.ID) if err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRolePermissionsBatchRemoved, Summary: "批量移除角色权限", Result: constants.AuditResultSuccess, OperatorID: operatorID, Role: role, Permissions: changes, BeforeData: beforeData, AfterData: afterData, }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRolePermissionsBatchRemoved, "批量移除角色权限失败", constants.AuditResultFailed, role, changes, beforeData, err) return errors.Wrap(errors.CodeInternalError, err, "批量删除角色-权限关联失败") } s.clearRolePermissionCaches(ctx, accountIDs) return nil } // UpdateStatus 更新角色状态 // 禁用角色时,若角色已分配给账号或店铺,则拒绝操作 func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error { currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return errors.New(errors.CodeUnauthorized, "未授权访问") } role, err := s.roleStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeRoleNotFound, "角色不存在") } return errors.Wrap(errors.CodeInternalError, err, "获取角色失败") } // 禁用角色时检查是否已有分配 if status == constants.StatusDisabled { accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err) return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败") } shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id) if err != nil { s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err) return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败") } if accountCount > 0 || shopCount > 0 { appErr := errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺,请先移除相关分配后再禁用", accountCount, shopCount)) s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "拒绝禁用使用中的角色", constants.AuditResultDenied, role, nil, roleAuditData(role), appErr) return appErr } } beforeData := roleAuditData(role) role.Status = status role.Updater = currentUserID if err := s.runAccessTransaction(ctx, func(tx *gorm.DB) error { if err := postgres.NewRoleStore(tx).Update(ctx, role); err != nil { return err } return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionRoleStatusUpdated, Summary: "更新角色状态", Result: constants.AuditResultSuccess, OperatorID: currentUserID, Role: role, BeforeData: beforeData, AfterData: roleAuditData(role), }) }); err != nil { s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, beforeData, err) return errors.Wrap(errors.CodeInternalError, err, "更新角色状态失败") } return nil } // toResponse 将 model.Role 转换为 dto.RoleResponse func toResponse(role *model.Role) *dto.RoleResponse { return &dto.RoleResponse{ ID: role.ID, RoleName: role.RoleName, RoleDesc: role.RoleDesc, RoleType: role.RoleType, Status: role.Status, DefaultCreditEnabled: role.DefaultCreditEnabled, DefaultCreditLimit: role.DefaultCreditLimit, DefaultCreditScope: "new_shops_only", Creator: role.Creator, Updater: role.Updater, CreatedAt: role.CreatedAt.Format(time.RFC3339), UpdatedAt: role.UpdatedAt.Format(time.RFC3339), } } func contains(availableForRoleTypes, roleTypeStr string) bool { types := strings.Split(availableForRoleTypes, ",") for _, t := range types { if strings.TrimSpace(t) == roleTypeStr { return true } } return false } func (s *Service) runAccessTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error { if s.db == nil || s.accessAudit == nil { return errors.New(errors.CodeInvalidStatus, "角色权限审计接缝未配置") } return s.db.WithContext(ctx).Transaction(fn) } func (s *Service) recordFailure( ctx context.Context, actionCode, summary, result string, role *model.Role, permissions []accessauditapp.PermissionChange, beforeData map[string]any, originalErr error, ) { accessauditapp.RecordFailure(ctx, s.db, s.accessAudit, accessauditapp.ChangeAudit{ ActionCode: actionCode, Summary: summary, Result: result, OperatorID: middleware.GetUserIDFromContext(ctx), Role: role, Permissions: permissions, BeforeData: beforeData, }, originalErr) } func roleAuditData(role *model.Role) map[string]any { if role == nil { return nil } return map[string]any{ "role_name": role.RoleName, "role_desc": role.RoleDesc, "role_type": role.RoleType, "status": role.Status, "default_credit_enabled": role.DefaultCreditEnabled, "default_credit_limit": role.DefaultCreditLimit, } } func permissionAuditChanges(permissions []*model.Permission, beforeData, afterData map[string]any) []accessauditapp.PermissionChange { changes := make([]accessauditapp.PermissionChange, 0, len(permissions)) for _, permission := range permissions { if permission == nil { continue } changes = append(changes, accessauditapp.PermissionChange{ Permission: permission, BeforeData: beforeData, AfterData: afterData, }) } return changes } func appendPermissionIDs(existing []uint, additions []*model.RolePermission) []uint { result := append([]uint(nil), existing...) for _, addition := range additions { result = append(result, addition.PermID) } return result } func removePermissionIDs(existing []uint, removeSet map[uint]struct{}) []uint { result := make([]uint, 0, len(existing)) for _, permissionID := range existing { if _, removed := removeSet[permissionID]; !removed { result = append(result, permissionID) } } return result } func containsUint(values []uint, target uint) bool { for _, value := range values { if value == target { return true } } return false } func rolePermissionCacheAccountIDs(ctx context.Context, tx *gorm.DB, roleID uint) ([]uint, error) { var accountIDs []uint if err := tx.WithContext(ctx).Model(&model.AccountRole{}). Where("role_id = ?", roleID).Distinct().Pluck("account_id", &accountIDs).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询角色关联账号失败") } return accountIDs, nil } func (s *Service) clearRolePermissionCaches(ctx context.Context, accountIDs []uint) { if len(accountIDs) == 0 || s.redisClient == nil { return } pipe := s.redisClient.Pipeline() for _, accountID := range accountIDs { pipe.Del(ctx, constants.RedisUserPermissionsKey(accountID)) } _, _ = pipe.Exec(ctx) }