package services import ( "slices" "github.com/goravel/framework/contracts/database/orm" "github.com/goravel/framework/facades" "github.com/goravel/framework/support/str" "github.com/samber/lo" "github.com/spf13/cast" apperrors "goravel/app/errors" "goravel/app/http/helpers" "goravel/app/models" ) type AdminService interface { // GetByID 根据ID获取管理员 GetByID(id uint, withDepartment bool, withRoles bool) (*models.Admin, error) // GetList 获取管理员列表 GetList(filters AdminFilters, page, pageSize int) ([]models.Admin, int64, error) // GetAllAdminsForExport 获取所有管理员用于导出(不分页) GetAllAdminsForExport(filters AdminFilters) ([]models.Admin, error) // LoadRelations 加载管理员的关联数据(部门、角色) LoadRelations(admin *models.Admin) error // LoadRelationsWithPermissions 加载管理员的关联数据(包括权限和菜单) LoadRelationsWithPermissions(admin *models.Admin) error // LoadRelationsForList 批量加载管理员的关联数据 LoadRelationsForList(admins []models.Admin) error // SyncRoles 同步管理员角色关联 SyncRoles(admin *models.Admin, roleIDs []uint) error // GetProtectedAdminIDs 获取所有受保护的管理员ID GetProtectedAdminIDs() map[uint]bool // GetDepartmentAndChildrenIDs 获取部门及其子部门ID GetDepartmentAndChildrenIDs(departmentID uint) []uint // Update 更新管理员 Update(admin *models.Admin) error } // AdminFilters 管理员查询过滤器 type AdminFilters struct { Username string Status string RoleID string DepartmentID string Is2FABound string StartTime string EndTime string OrderBy string } type AdminServiceImpl struct { } func NewAdminServiceImpl() *AdminServiceImpl { return &AdminServiceImpl{} } // GetByID 根据ID获取管理员 func (s *AdminServiceImpl) GetByID(id uint, withDepartment bool, withRoles bool) (*models.Admin, error) { var admin models.Admin query := facades.Orm().Query().Where("id", id) // 预加载关联 if withDepartment { query = query.With("Department") } if withRoles { query = query.With("Roles") } if err := query.First(&admin); err != nil { return nil, apperrors.ErrAdminNotFound.WithError(err) } return &admin, nil } // buildQuery 构建查询(公共方法,用于列表和导出) func (s *AdminServiceImpl) buildQuery(filters AdminFilters) orm.Query { query := facades.Orm().Query().Model(&models.Admin{}) // 排除受保护的管理员 protectedIDs := s.GetProtectedAdminIDs() if len(protectedIDs) > 0 { var ids []uint for id := range protectedIDs { ids = append(ids, id) } if len(ids) > 0 { query = query.Where("id NOT IN ?", ids) } } // 应用筛选条件 if filters.Username != "" { query = query.Where("username LIKE ?", "%"+filters.Username+"%") } if filters.Status != "" { query = query.Where("status", filters.Status) } if filters.RoleID != "" { roleIDUint := cast.ToUint(filters.RoleID) if roleIDUint > 0 { query = query.Where("id IN (SELECT admin_id FROM admin_role WHERE role_id = ?)", roleIDUint) } } if filters.DepartmentID != "" { departmentIDUint := cast.ToUint(filters.DepartmentID) if departmentIDUint > 0 { departmentIDs := s.GetDepartmentAndChildrenIDs(departmentIDUint) if len(departmentIDs) > 0 { idsAny := make([]any, len(departmentIDs)) for i, id := range departmentIDs { idsAny[i] = id } query = query.WhereIn("department_id", idsAny) } else { // 如果部门不存在,返回空结果 query = query.Where("1 = 0") } } } if filters.Is2FABound != "" { switch filters.Is2FABound { case "1": // 已绑定:google_secret IS NOT NULL AND google_secret != '' query = query.Where("google_secret IS NOT NULL AND google_secret != ?", "") case "0": // 未绑定:google_secret IS NULL OR google_secret = '' query = query.Where("(google_secret IS NULL OR google_secret = ?)", "") } } if filters.StartTime != "" { query = query.Where("created_at >= ?", filters.StartTime) } if filters.EndTime != "" { query = query.Where("created_at <= ?", filters.EndTime) } return query } // GetList 获取管理员列表 func (s *AdminServiceImpl) GetList(filters AdminFilters, page, pageSize int) ([]models.Admin, int64, error) { query := s.buildQuery(filters) // 应用排序 orderBy := filters.OrderBy if orderBy == "" { orderBy = "created_at:desc" } query = helpers.ApplySort(query, orderBy, "created_at:desc") // 分页查询 var admins []models.Admin var total int64 if err := query.With("Department").With("Roles").Paginate(page, pageSize, &admins, &total); err != nil { return nil, 0, err } return admins, total, nil } // GetAllAdminsForExport 获取所有管理员用于导出(不分页) func (s *AdminServiceImpl) GetAllAdminsForExport(filters AdminFilters) ([]models.Admin, error) { query := s.buildQuery(filters) // 应用排序 orderBy := filters.OrderBy if orderBy == "" { orderBy = "created_at:desc" } query = helpers.ApplySort(query, orderBy, "created_at:desc") // 不分页,获取所有数据 var admins []models.Admin if err := query.With("Department").With("Roles").Find(&admins); err != nil { return nil, apperrors.ErrQueryFailed.WithError(err) } return admins, nil } // Update 更新管理员 func (s *AdminServiceImpl) Update(admin *models.Admin) error { if err := facades.Orm().Query().Save(admin); err != nil { return apperrors.ErrUpdateFailed.WithError(err) } return nil } // GetProtectedAdminIDs 获取所有受保护的管理员ID func (s *AdminServiceImpl) GetProtectedAdminIDs() map[uint]bool { allProtectedIDs := make(map[uint]bool) // 添加超级管理员ID(从配置读取,默认1) superAdminID := cast.ToUint(facades.Config().GetInt("admin.super_admin_id", 1)) allProtectedIDs[superAdminID] = true // 添加开发者管理员ID developerIDsStr := facades.Config().GetString("admin.developer_ids", "2") developerIDs := s.parseProtectedIDs(developerIDsStr) for _, did := range developerIDs { allProtectedIDs[did] = true } return allProtectedIDs } // parseProtectedIDs 解析受保护的管理员ID字符串(支持逗号分隔) func (s *AdminServiceImpl) parseProtectedIDs(idsStr string) []uint { var ids []uint if idsStr == "" { return ids } // 使用字符串分割 parts := str.Of(idsStr).Split(",") for _, part := range parts { part = str.Of(part).Trim().String() if !str.Of(part).IsEmpty() { if id := cast.ToUint(part); id > 0 { ids = append(ids, id) } } } return ids } // GetDepartmentAndChildrenIDs 获取部门及其子部门ID func (s *AdminServiceImpl) GetDepartmentAndChildrenIDs(departmentID uint) []uint { var departmentIDs []uint departmentIDs = append(departmentIDs, departmentID) s.getChildrenDepartmentIDs(departmentID, &departmentIDs) return departmentIDs } // getChildrenDepartmentIDs 递归获取子部门ID func (s *AdminServiceImpl) getChildrenDepartmentIDs(parentID uint, departmentIDs *[]uint) { var children []models.Department if err := facades.Orm().Query().Where("parent_id", parentID).Get(&children); err == nil { for _, child := range children { *departmentIDs = append(*departmentIDs, child.ID) s.getChildrenDepartmentIDs(child.ID, departmentIDs) } } } // LoadRelations 加载管理员的关联数据(部门、角色) func (s *AdminServiceImpl) LoadRelations(admin *models.Admin) error { if admin == nil { return nil } // 加载部门 if admin.DepartmentID > 0 { var department models.Department if err := facades.Orm().Query().Where("id", admin.DepartmentID).First(&department); err == nil { admin.Department = department } } // 加载角色关联 type AdminRole struct { AdminID uint `gorm:"column:admin_id"` RoleID uint `gorm:"column:role_id"` } var adminRoles []AdminRole if err := facades.Orm().Query().Table("admin_role").Where("admin_id", admin.ID).Find(&adminRoles); err != nil { return err } var roleIDs []uint for _, ar := range adminRoles { if !contains(roleIDs, ar.RoleID) { roleIDs = append(roleIDs, ar.RoleID) } } admin.Roles = nil if len(roleIDs) > 0 { var roles []models.Role if err := facades.Orm().Query().Where("id IN ?", roleIDs).Find(&roles); err != nil { return err } admin.Roles = roles } return nil } // LoadRelationsWithPermissions 加载管理员的关联数据(包括权限和菜单) func (s *AdminServiceImpl) LoadRelationsWithPermissions(admin *models.Admin) error { // 先加载基本关联 if err := s.LoadRelations(admin); err != nil { return err } // 批量加载所有角色的权限和菜单,避免 N+1 查询 if len(admin.Roles) > 0 { for i := range admin.Roles { admin.Roles[i].Permissions = nil admin.Roles[i].Menus = nil } var roleIDs []uint for _, role := range admin.Roles { roleIDs = append(roleIDs, role.ID) } // 批量加载权限 type RolePermission struct { RoleID uint `gorm:"column:role_id"` PermissionID uint `gorm:"column:permission_id"` } var rolePermissions []RolePermission if err := facades.Orm().Query().Table("role_permission").Where("role_id IN ?", roleIDs).Find(&rolePermissions); err == nil { var permissionIDs []uint rolePermissionMap := make(map[uint][]uint) for _, rp := range rolePermissions { rolePermissionMap[rp.RoleID] = append(rolePermissionMap[rp.RoleID], rp.PermissionID) permissionIDs = append(permissionIDs, rp.PermissionID) } if len(permissionIDs) > 0 { var permissions []models.Permission // 预加载菜单关联,用于检查菜单状态 if err := facades.Orm().Query().With("Menu").Where("id IN ?", permissionIDs).Find(&permissions); err == nil { permissionMap := make(map[uint]models.Permission) for _, perm := range permissions { permissionMap[perm.ID] = perm } for i := range admin.Roles { if permIDs, ok := rolePermissionMap[admin.Roles[i].ID]; ok { for _, permID := range permIDs { if perm, ok := permissionMap[permID]; ok { admin.Roles[i].Permissions = append(admin.Roles[i].Permissions, perm) } } } } } } } // 批量加载菜单 type RoleMenu struct { RoleID uint `gorm:"column:role_id"` MenuID uint `gorm:"column:menu_id"` } var roleMenus []RoleMenu if err := facades.Orm().Query().Table("role_menu").Where("role_id IN ?", roleIDs).Find(&roleMenus); err == nil { var menuIDs []uint roleMenuMap := make(map[uint][]uint) for _, rm := range roleMenus { roleMenuMap[rm.RoleID] = append(roleMenuMap[rm.RoleID], rm.MenuID) menuIDs = append(menuIDs, rm.MenuID) } if len(menuIDs) > 0 { var menus []models.Menu if err := facades.Orm().Query().Where("id IN ?", menuIDs).Find(&menus); err == nil { menuMap := make(map[uint]models.Menu) for _, menu := range menus { menuMap[menu.ID] = menu } for i := range admin.Roles { if mIDs, ok := roleMenuMap[admin.Roles[i].ID]; ok { for _, menuID := range mIDs { if menu, ok := menuMap[menuID]; ok { admin.Roles[i].Menus = append(admin.Roles[i].Menus, menu) } } } } } } } } // 收集所有角色的权限和菜单(去重) permissionMap := make(map[uint]models.Permission) menuMap := make(map[uint]models.Menu) for _, role := range admin.Roles { for _, perm := range role.Permissions { permissionMap[perm.ID] = perm } for _, menu := range role.Menus { menuMap[menu.ID] = menu } } // 将权限和菜单存储到 admin 的扩展字段(如果需要) // 这里可以根据实际需求调整 return nil } // LoadRelationsForList 批量加载管理员的关联数据(优化版,避免 N+1 查询) func (s *AdminServiceImpl) LoadRelationsForList(admins []models.Admin) error { if len(admins) == 0 { return nil } // 收集所有需要查询的 ID var departmentIDs []uint var adminIDs []uint for _, admin := range admins { if admin.DepartmentID > 0 { departmentIDs = append(departmentIDs, admin.DepartmentID) } adminIDs = append(adminIDs, admin.ID) } // 批量查询部门 departmentsMap := make(map[uint]models.Department) if len(departmentIDs) > 0 { var departments []models.Department // 去重 uniqueDeptIDs := make(map[uint]bool) var uniqueIDs []uint for _, id := range departmentIDs { if !uniqueDeptIDs[id] { uniqueIDs = append(uniqueIDs, id) uniqueDeptIDs[id] = true } } if err := facades.Orm().Query().Where("id IN ?", uniqueIDs).Find(&departments); err != nil { return err } for _, dept := range departments { departmentsMap[dept.ID] = dept } } // 批量查询所有管理员的角色关联 // 查询中间表获取 admin_id 和 role_id 的映射 type AdminRole struct { AdminID uint `gorm:"column:admin_id"` RoleID uint `gorm:"column:role_id"` } var adminRoles []AdminRole if err := facades.Orm().Query().Table("admin_role").Where("admin_id IN ?", adminIDs).Find(&adminRoles); err != nil { return err } // 按管理员ID分组角色关联 adminRoleMap := lo.GroupBy(adminRoles, func(ar AdminRole) uint { return ar.AdminID }) // 提取所有角色ID并去重 allRoleIDs := lo.Map(adminRoles, func(ar AdminRole, _ int) uint { return ar.RoleID }) roleIDs := lo.Uniq(allRoleIDs) // 批量查询所有角色 rolesMap := make(map[uint]models.Role) if len(roleIDs) > 0 { var roles []models.Role if err := facades.Orm().Query().Where("id IN ?", roleIDs).Find(&roles); err != nil { return err } rolesMap = lo.SliceToMap(roles, func(role models.Role) (uint, models.Role) { return role.ID, role }) } // 填充关联数据 for i := range admins { // 填充部门 if admins[i].DepartmentID > 0 { if dept, ok := departmentsMap[admins[i].DepartmentID]; ok { admins[i].Department = dept } } // 填充角色(去重) if adminRoleList, ok := adminRoleMap[admins[i].ID]; ok { roleIDs := lo.Uniq(lo.Map(adminRoleList, func(ar AdminRole, _ int) uint { return ar.RoleID })) admins[i].Roles = lo.FilterMap(roleIDs, func(roleID uint, _ int) (models.Role, bool) { role, ok := rolesMap[roleID] return role, ok }) } } return nil } // contains 辅助函数,检查切片中是否包含某个值 func contains(slice []uint, val uint) bool { return slices.Contains(slice, val) } // SyncRoles 同步管理员角色关联 func (s *AdminServiceImpl) SyncRoles(admin *models.Admin, roleIDs []uint) error { // 去重角色ID,避免重复数据 deduplicatedRoleIDs := lo.Uniq(roleIDs) // 先清空该管理员的所有角色关联(包括重复的),确保彻底清理重复数据 if err := facades.Orm().Query().Model(admin).Association("Roles").Clear(); err != nil { return err } // 如果有角色需要关联,则添加去重后的角色关联 if len(deduplicatedRoleIDs) > 0 { var roles []models.Role if err := facades.Orm().Query().Where("id IN ?", deduplicatedRoleIDs).Find(&roles); err != nil { return err } // 对查询到的角色再次去重(双重保险) roleMap := lo.SliceToMap(roles, func(role models.Role) (uint, models.Role) { return role.ID, role }) // 将去重后的角色转换为切片 deduplicatedRoles := lo.Values(roleMap) // Replace 方法会先删除所有现有关联,然后添加新的关联 if err := facades.Orm().Query().Model(admin).Association("Roles").Replace(deduplicatedRoles); err != nil { return err } } return nil }