2026-07-14 17:12:39 +08:00

209 lines
5.7 KiB
Go

package repository
import (
"errors"
"hyapp-admin-server/internal/model"
"gorm.io/gorm"
)
func (s *Store) ListPermissions() ([]model.Permission, error) {
var permissions []model.Permission
err := s.db.Order("kind ASC, code ASC").Find(&permissions).Error
return permissions, err
}
func (s *Store) CreatePermission(permission *model.Permission) error {
normalized, err := normalizePermission(*permission)
if err != nil {
return err
}
*permission = normalized
return s.db.Create(permission).Error
}
func (s *Store) UpdatePermission(id uint, patch model.Permission) (*model.Permission, error) {
normalized, err := normalizePermission(patch)
if err != nil {
return nil, err
}
var permission model.Permission
if err := s.db.First(&permission, id).Error; err != nil {
return nil, err
}
// 权限码会写入 JWT 和前端按钮判断,更新时必须整体校验后一次性落库。
updates := map[string]any{
"name": normalized.Name,
"code": normalized.Code,
"kind": normalized.Kind,
"description": normalized.Description,
}
if err := s.db.Model(&permission).Updates(updates).Error; err != nil {
return nil, err
}
if err := s.db.First(&permission, id).Error; err != nil {
return nil, err
}
return &permission, nil
}
func (s *Store) DeletePermission(id uint) error {
var roleCount int64
if err := s.db.Table("admin_role_permissions").Where("permission_id = ?", id).Count(&roleCount).Error; err != nil {
return err
}
if roleCount > 0 {
return errors.New("permission is bound to roles")
}
// Menus reference permission_code by string, so deletion must check the code before removing the definition.
var menuCount int64
if err := s.db.Model(&model.Menu{}).Where("permission_code = (SELECT code FROM admin_permissions WHERE id = ?)", id).Count(&menuCount).Error; err != nil {
return err
}
if menuCount > 0 {
return errors.New("permission is bound to menus")
}
return s.db.Delete(&model.Permission{}, id).Error
}
func (s *Store) SyncDefaultPermissions() error {
return s.db.Transaction(func(tx *gorm.DB) error {
if err := s.syncDefaultPermissions(tx); err != nil {
return err
}
return s.syncManagedDefaultRolePermissions(tx)
})
}
func (s *Store) syncManagedDefaultRolePermissions(tx *gorm.DB) error {
var allPermissions []model.Permission
if err := tx.Find(&allPermissions).Error; err != nil {
return err
}
for _, definition := range managedDefaultRoles {
var role model.Role
result := tx.Where("code = ?", definition.Code).Limit(1).Find(&role)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
role = definition
if err := tx.Create(&role).Error; err != nil {
return err
}
} else if err := tx.Model(&role).Updates(map[string]any{
"name": definition.Name,
"description": definition.Description,
}).Error; err != nil {
return err
}
permissions := allPermissions
if definition.Code != roleCodePlatformAdmin {
permissions = nil
codes := defaultRolePermissionCodes(definition.Code)
if len(codes) > 0 {
if err := tx.Where("code IN ?", codes).Find(&permissions).Error; err != nil {
return err
}
}
}
// “同步权限”是恢复固定岗位模板的显式管理动作,必须删除历史跨模块授权,不能只追加缺失项。
if err := tx.Model(&role).Association("Permissions").Replace(permissions); err != nil {
return err
}
}
return nil
}
func (s *Store) ListRoles() ([]model.Role, error) {
var roles []model.Role
err := s.db.Preload("Permissions").Order("id ASC").Find(&roles).Error
return roles, err
}
func (s *Store) CreateRole(role *model.Role, permissionIDs []uint) error {
return s.db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(role).Error; err != nil {
return err
}
if len(permissionIDs) == 0 {
return nil
}
var permissions []model.Permission
if err := tx.Where("id IN ?", permissionIDs).Find(&permissions).Error; err != nil {
return err
}
return tx.Model(role).Association("Permissions").Replace(permissions)
})
}
func (s *Store) UpdateRole(roleID uint, patch model.Role, permissionIDs *[]uint) (*model.Role, error) {
var role model.Role
if err := s.db.First(&role, roleID).Error; err != nil {
return nil, err
}
err := s.db.Transaction(func(tx *gorm.DB) error {
updates := map[string]any{
"name": patch.Name,
"code": patch.Code,
"description": patch.Description,
}
if err := tx.Model(&role).Updates(updates).Error; err != nil {
return err
}
if permissionIDs != nil {
var permissions []model.Permission
if len(*permissionIDs) > 0 {
if err := tx.Where("id IN ?", *permissionIDs).Find(&permissions).Error; err != nil {
return err
}
}
if err := tx.Model(&role).Association("Permissions").Replace(permissions); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
if err := s.db.Preload("Permissions").First(&role, roleID).Error; err != nil {
return nil, err
}
return &role, nil
}
func (s *Store) DeleteRole(roleID uint) error {
var users int64
if err := s.db.Table("admin_user_roles").Where("role_id = ?", roleID).Count(&users).Error; err != nil {
return err
}
if users > 0 {
return errors.New("role is bound to users")
}
return s.db.Delete(&model.Role{}, roleID).Error
}
func (s *Store) ReplaceRolePermissions(roleID uint, permissionIDs []uint) error {
var role model.Role
if err := s.db.First(&role, roleID).Error; err != nil {
return err
}
var permissions []model.Permission
if len(permissionIDs) > 0 {
if err := s.db.Where("id IN ?", permissionIDs).Find(&permissions).Error; err != nil {
return err
}
}
return s.db.Model(&role).Association("Permissions").Replace(permissions)
}