2026-07-02 10:53:44 +08:00

179 lines
5.4 KiB
Go

package repository
import (
"errors"
"strings"
"hyapp-admin-server/internal/model"
"gorm.io/gorm"
)
func (s *Store) ListTeams(keyword string) ([]model.Team, error) {
query := s.db.Model(&model.Team{}).
Select("admin_teams.*, parent_team.name AS parent_name, COUNT(DISTINCT admin_users.id) AS user_count").
Joins("LEFT JOIN admin_teams parent_team ON parent_team.id = admin_teams.parent_id").
Joins("LEFT JOIN admin_teams child_team ON child_team.parent_id = admin_teams.id").
Joins("LEFT JOIN admin_users ON admin_users.team_id = admin_teams.id OR admin_users.team_id = child_team.id")
if keyword := strings.TrimSpace(keyword); keyword != "" {
like := "%" + keyword + "%"
query = query.Where("admin_teams.name LIKE ? OR CAST(admin_teams.id AS CHAR) LIKE ? OR admin_teams.description LIKE ? OR parent_team.name LIKE ?", like, like, like, like)
}
var teams []model.Team
err := query.
Group("admin_teams.id, admin_teams.parent_id, admin_teams.name, admin_teams.description, admin_teams.sort, admin_teams.created_at_ms, admin_teams.updated_at_ms, parent_team.name").
Order("CASE WHEN admin_teams.parent_id IS NULL THEN admin_teams.sort ELSE COALESCE(parent_team.sort, admin_teams.sort) END ASC").
Order("COALESCE(admin_teams.parent_id, admin_teams.id) ASC").
Order("CASE WHEN admin_teams.parent_id IS NULL THEN 0 ELSE 1 END ASC").
Order("admin_teams.sort ASC, admin_teams.id ASC").
Scan(&teams).Error
return teams, err
}
func (s *Store) GetTeam(id uint) (*model.Team, error) {
var team model.Team
if err := s.db.First(&team, id).Error; err != nil {
return nil, err
}
return &team, nil
}
func (s *Store) FindTeamByName(name string) (*model.Team, error) {
var team model.Team
if err := s.db.Where("name = ?", strings.TrimSpace(name)).First(&team).Error; err != nil {
return nil, err
}
return &team, nil
}
func (s *Store) CreateTeam(team *model.Team) error {
if err := normalizeTeam(team); err != nil {
return err
}
if err := s.validateTeamParent(s.db, 0, team.ParentID); err != nil {
return err
}
return s.db.Create(team).Error
}
func (s *Store) UpdateTeam(id uint, updates model.Team) (*model.Team, error) {
if id == 0 {
return nil, errors.New("team id is required")
}
if err := normalizeTeam(&updates); err != nil {
return nil, err
}
err := s.db.Transaction(func(tx *gorm.DB) error {
var team model.Team
if err := tx.First(&team, id).Error; err != nil {
return err
}
if err := s.validateTeamParent(tx, id, updates.ParentID); err != nil {
return err
}
if err := tx.Model(&team).Updates(map[string]any{
"parent_id": updates.ParentID,
"name": updates.Name,
"description": updates.Description,
"sort": updates.Sort,
}).Error; err != nil {
return err
}
// admin_users.team 是旧版字符串字段,仍被历史数据权限和导出兜底读取;团队重命名时同步维护,避免新旧字段长期分叉。
return tx.Model(&model.User{}).Where("team_id = ?", id).Update("team", updates.Name).Error
})
if err != nil {
return nil, err
}
return s.GetTeam(id)
}
func (s *Store) TeamAndChildIDs(id uint) ([]uint, error) {
if id == 0 {
return nil, errors.New("team id is required")
}
ids := []uint{id}
var childIDs []uint
if err := s.db.Model(&model.Team{}).Where("parent_id = ?", id).Order("sort ASC, id ASC").Pluck("id", &childIDs).Error; err != nil {
return nil, err
}
ids = append(ids, childIDs...)
return ids, nil
}
func (s *Store) NormalizeUserTeam(teamID *uint, legacyName string) (*uint, string, error) {
name := strings.TrimSpace(legacyName)
if teamID != nil {
if *teamID == 0 {
return nil, "", nil
}
team, err := s.GetTeam(*teamID)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, "", errors.New("团队不存在")
}
if err != nil {
return nil, "", err
}
id := team.ID
return &id, team.Name, nil
}
if name == "" {
return nil, "", nil
}
team, err := s.FindTeamByName(name)
if err == nil {
id := team.ID
return &id, team.Name, nil
}
if errors.Is(err, gorm.ErrRecordNotFound) {
// 老客户端可能仍提交自由文本团队;无法匹配到团队表时保留文本,不阻断兼容请求。
return nil, name, nil
}
return nil, "", err
}
func normalizeTeam(team *model.Team) error {
team.Name = strings.TrimSpace(team.Name)
team.Description = strings.TrimSpace(team.Description)
if team.ParentID != nil && *team.ParentID == 0 {
team.ParentID = nil
}
if team.Name == "" {
return errors.New("团队名称不能为空")
}
return nil
}
func (s *Store) validateTeamParent(tx *gorm.DB, currentID uint, parentID *uint) error {
if parentID == nil {
return nil
}
if currentID > 0 && *parentID == currentID {
return errors.New("子部门不能挂到自己下面")
}
var parent model.Team
if err := tx.First(&parent, *parentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父级团队不存在")
}
return err
}
if parent.ParentID != nil {
return errors.New("暂不支持三级团队")
}
if currentID > 0 {
var childCount int64
if err := tx.Model(&model.Team{}).Where("parent_id = ?", currentID).Count(&childCount).Error; err != nil {
return err
}
// 已有子部门的团队如果再被设为子部门,会形成三级结构;这里提前阻断,保证团队树始终只有团队和子部门两层。
if childCount > 0 {
return errors.New("已有子部门的团队不能设为子部门")
}
}
return nil
}