179 lines
5.4 KiB
Go
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
|
|
}
|