package business import ( "context" "time" "gorm.io/gorm" "gorm.io/gorm/clause" "kra/internal/biz/business" ) type teamRepo struct{ data Provider } func NewTeamRepo(data Provider) business.TeamRepo { return &teamRepo{data: data} } func (r *teamRepo) Members(ctx context.Context, scope business.UserScope) ([]*business.BusinessMember, error) { db := r.data.DB().WithContext(ctx) var rows []struct { Account accountPO `gorm:"embedded"` HireDate, Employment string GroupID uint64 } q := applyUserScope(db.Table("sys_users u").Where("u.deleted_at IS NULL AND u.level BETWEEN 1 AND 3"), scope, "u.") if err := q.Select("u.*,p.username AS parent_user_name,mp.hire_date,mp.employment,gm.group_id").Joins("LEFT JOIN sys_users p ON p.id=u.parent_id AND p.deleted_at IS NULL").Joins("LEFT JOIN sys_business_member_profiles mp ON mp.user_id=u.id").Joins("LEFT JOIN sys_business_group_members gm ON gm.user_id=u.id").Order("u.id").Scan(&rows).Error; err != nil { return nil, business.ErrStorage } ids := make([]uint, 0, len(rows)) for _, row := range rows { ids = append(ids, row.Account.ID) } counts, err := (&accountRepo{data: r.data}).DirectCounts(ctx, ids) if err != nil { return nil, err } out := make([]*business.BusinessMember, 0, len(rows)) for _, row := range rows { a := toBizAccount(row.Account) a.Password = "" a.PromoteCount = counts[a.ID].PromoteCount a.BindCount = counts[a.ID].BindCount employment := row.Employment if employment == "" { employment = "employed" if a.Status != 0 { employment = "departed" } } a.HireDate, a.Employment = row.HireDate, employment out = append(out, &business.BusinessMember{Account: *a, GroupID: row.GroupID}) } return out, nil } func readTeamCatalog(db *gorm.DB) (*business.TeamCatalog, error) { var state businessGroupStatePO if err := db.First(&state, "id=1").Error; err != nil { return nil, business.ErrStorage } var groups []businessGroupPO if err := db.Order("sort,id").Find(&groups).Error; err != nil { return nil, business.ErrStorage } var members []businessGroupMemberPO if err := db.Order("user_id").Find(&members).Error; err != nil { return nil, business.ErrStorage } byGroup := map[uint64][]uint{} for _, m := range members { byGroup[m.GroupID] = append(byGroup[m.GroupID], m.UserID) } out := &business.TeamCatalog{Revision: state.Revision, Groups: []*business.BusinessGroup{}} for _, g := range groups { out.Groups = append(out.Groups, &business.BusinessGroup{ID: g.ID, Name: g.Name, OwnerID: g.OwnerID, Sort: g.Sort, MemberIDs: append([]uint{}, byGroup[g.ID]...)}) } return out, nil } func (r *teamRepo) Catalog(ctx context.Context) (*business.TeamCatalog, error) { var value *business.TeamCatalog err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error { var err error; value, err = readTeamCatalog(tx); return err }) return value, storageError(err) } func (r *teamRepo) SaveCatalog(ctx context.Context, revision uint64, groups []*business.BusinessGroup) (*business.TeamCatalog, error) { db := r.data.DB().WithContext(ctx) err := db.Transaction(func(tx *gorm.DB) error { updated := tx.Model(&businessGroupStatePO{}).Where("id=1 AND revision=?", revision).Update("revision", gorm.Expr("revision+1")) if updated.Error != nil { return updated.Error } if updated.RowsAffected != 1 { return business.ErrTeamConflict } var current []businessGroupPO if err := tx.Find(¤t).Error; err != nil { return err } byID := map[uint64]businessGroupPO{} for _, g := range current { byID[g.ID] = g } ids := []uint64{} members := []businessGroupMemberPO{} for _, g := range groups { po := businessGroupPO{ID: g.ID, Name: g.Name, OwnerID: g.OwnerID, Sort: g.Sort} if g.ID == 0 { if err := tx.Create(&po).Error; err != nil { return err } } else { old, exists := byID[g.ID] if !exists { return business.ErrTeamConflict } if old.Name != g.Name || old.Sort != g.Sort { if err := tx.Model(&businessGroupPO{}).Where("id=?", g.ID).Updates(map[string]any{"name": g.Name, "sort": g.Sort, "updated_at": time.Now()}).Error; err != nil { return err } } } ids = append(ids, po.ID) for _, id := range g.MemberIDs { members = append(members, businessGroupMemberPO{UserID: id, GroupID: po.ID}) } } deleted := tx.Model(&businessGroupPO{}) if len(ids) > 0 { deleted = deleted.Where("id NOT IN ?", ids) } else { deleted = deleted.Where("1=1") } if err := deleted.Delete(&businessGroupPO{}).Error; err != nil { return err } if err := tx.Where("1=1").Delete(&businessGroupMemberPO{}).Error; err != nil { return err } if len(members) > 0 { if err := tx.CreateInBatches(&members, 300).Error; err != nil { return err } } return nil }) if err != nil { return nil, storageError(err) } return r.Catalog(ctx) } func (r *teamRepo) UpdateProfile(ctx context.Context, p business.MemberProfile) error { return storageError(r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error { var account accountPO if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, p.ID).Error; err != nil { return err } domain := toBizAccount(account) domain.Phone = p.Phone if err := checkAccountUnique(tx, domain); err != nil { return err } status, enable := 0, 1 if p.Employment == "departed" { status, enable = 1, 0 } values := map[string]any{"real_name": p.RealName, "phone": p.Phone, "business_status": status, "enable": enable, "updated_at": time.Now()} if p.Password != "" { values["password"] = p.Password values["password_updated_at"] = time.Now() } if err := tx.Model(&accountPO{}).Where("id=?", p.ID).Updates(values).Error; err != nil { return err } return tx.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.AssignmentColumns([]string{"hire_date", "employment", "updated_at"})}).Create(&businessMemberProfilePO{UserID: p.ID, HireDate: p.HireDate, Employment: p.Employment, UpdatedAt: time.Now()}).Error })) }