package managercenter import ( "context" "errors" "strings" "time" "chatapp3-golang/internal/utils" "gorm.io/gorm" ) type bdLeaderRow struct { ID int64 `gorm:"column:id;primaryKey"` Origin string `gorm:"column:sys_origin"` UserID int64 `gorm:"column:user_id"` Contact string `gorm:"column:contact"` RegionID string `gorm:"column:region_id"` CreateTime time.Time `gorm:"column:create_time"` UpdateTime time.Time `gorm:"column:update_time"` CreateUser int64 `gorm:"column:create_user"` UpdateUser int64 `gorm:"column:update_user"` } func (bdLeaderRow) TableName() string { return "room_bd_lead_base_info" } type businessDevelopmentBaseInfoRow struct { ID int64 `gorm:"column:id;primaryKey"` Origin string `gorm:"column:sys_origin"` BDLeadUserID *int64 `gorm:"column:bd_lead_user_id"` UserID int64 `gorm:"column:user_id"` Contact string `gorm:"column:contact"` Region string `gorm:"column:region"` MemberQuantity int64 `gorm:"column:member_quantity"` CreateTime time.Time `gorm:"column:create_time"` UpdateTime time.Time `gorm:"column:update_time"` CreateUser int64 `gorm:"column:create_user"` UpdateUser int64 `gorm:"column:update_user"` } func (businessDevelopmentBaseInfoRow) TableName() string { return "room_business_development_base_info" } type userHistoryIdentityRow struct { UserID int64 `gorm:"column:user_id;primaryKey"` IsBD bool `gorm:"column:is_bd"` IsAgent bool `gorm:"column:is_agent"` IsHost bool `gorm:"column:is_host"` IsManager bool `gorm:"column:is_manager"` CreateTime time.Time `gorm:"column:create_time"` UpdateTime time.Time `gorm:"column:update_time"` CreateUser int64 `gorm:"column:create_user"` UpdateUser int64 `gorm:"column:update_user"` } func (userHistoryIdentityRow) TableName() string { return "user_history_identity" } type administratorRow struct { ID int64 `gorm:"column:id;primaryKey"` UserID int64 `gorm:"column:user_id"` Status bool `gorm:"column:status"` } func (administratorRow) TableName() string { return "sys_administrator" } type bdLeaderListRow struct { ID int64 `gorm:"column:id"` UserID int64 `gorm:"column:user_id"` Origin string `gorm:"column:sys_origin"` Contact string `gorm:"column:contact"` RegionID string `gorm:"column:region_id"` CreateTime time.Time `gorm:"column:create_time"` UpdateTime time.Time `gorm:"column:update_time"` Account string `gorm:"column:account"` UserAvatar string `gorm:"column:user_avatar"` UserNickname string `gorm:"column:user_nickname"` } func (s *Service) ListBDLeaders(ctx context.Context, user AuthUser) (*BDLeaderListResponse, error) { manager, err := s.requireManagerInfo(ctx, user) if err != nil { return nil, err } query := s.db.WithContext(ctx). Table("room_bd_lead_base_info AS leader"). Select(strings.Join([]string{ "leader.id", "leader.user_id", "leader.sys_origin", "leader.contact", "leader.region_id", "leader.create_time", "leader.update_time", "user_info.account", "user_info.user_avatar", "user_info.user_nickname", }, ", ")). Joins("LEFT JOIN user_base_info AS user_info ON user_info.id = leader.user_id AND user_info.is_del = ?", false). Where("leader.create_user = ?", user.UserID) if origin := managerSysOrigin(manager, user); origin != "" { query = query.Where("leader.sys_origin = ?", origin) } var rows []bdLeaderListRow if err := query.Order("leader.create_time DESC, leader.id DESC").Find(&rows).Error; err != nil { return nil, serverError("bd_leader_query_failed", err.Error()) } records := make([]BDLeaderView, 0, len(rows)) for _, row := range rows { records = append(records, bdLeaderListRowToView(row)) } return &BDLeaderListResponse{Count: len(records), Records: records}, nil } func (s *Service) AddBDLeader(ctx context.Context, user AuthUser, req AddBDLeaderRequest) (*AddBDLeaderResponse, error) { manager, err := s.requireManagerInfo(ctx, user) if err != nil { return nil, err } if req.UserID.Int64() <= 0 { return nil, badRequest("user_required", "userId is required") } target, err := s.loadUserRowByID(ctx, req.UserID.Int64()) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, notFound("user_not_found", "user not found") } return nil, serverError("user_query_failed", err.Error()) } sysOrigin := managerSysOrigin(manager, user) if sysOrigin == "" { sysOrigin = normalizeSysOrigin(target.OriginSys) } if target.OriginSys != "" && sysOrigin != "" && !strings.EqualFold(target.OriginSys, sysOrigin) { return nil, badRequest("bd_leader_origin_mismatch", "target user sysOrigin does not match manager") } regionID := strings.TrimSpace(req.RegionID) if regionID == "" { regionID = strings.TrimSpace(manager.RegionID) } if regionID == "" { return nil, badRequest("manager_region_required", "manager region is required") } if exists, err := s.userIsAdministrator(ctx, target.ID); err != nil { return nil, err } else if exists { return nil, badRequest("user_is_admin", "administrator cannot be added as BD leader") } if exists, err := s.userIsBDLeader(ctx, target.ID); err != nil { return nil, err } else if exists { return nil, badRequest("user_already_bd_leader", "user is already a BD leader") } leaderID, err := utils.NextID() if err != nil { return nil, serverError("id_generate_failed", err.Error()) } now := time.Now() contact := strings.TrimSpace(req.Contact) leader := bdLeaderRow{ ID: leaderID, Origin: sysOrigin, UserID: target.ID, Contact: contact, RegionID: regionID, CreateTime: now, UpdateTime: now, CreateUser: user.UserID, UpdateUser: user.UserID, } if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Create(&leader).Error; err != nil { return serverError("bd_leader_create_failed", err.Error()) } if err := upsertBDBaseInfo(tx, leader, user.UserID, now); err != nil { return err } if err := upsertUserHistoryBD(tx, target.ID, user.UserID, now); err != nil { return err } return nil }); err != nil { return nil, err } return &AddBDLeaderResponse{ Success: true, Leader: bdLeaderRowToView(leader, target), }, nil } func (s *Service) requireManagerInfo(ctx context.Context, user AuthUser) (teamManagerInfoRow, error) { if user.UserID <= 0 { return teamManagerInfoRow{}, unauthorized("invalid_user", "authenticated user is required") } if s.db == nil { return teamManagerInfoRow{}, serviceUnavailable("database_unavailable", "database is unavailable") } var row teamManagerInfoRow err := s.db.WithContext(ctx). Table("team_manager_base_info"). Where("user_id = ?", user.UserID). First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return teamManagerInfoRow{}, forbidden("not_manager", "user is not manager") } if err != nil { return teamManagerInfoRow{}, serverError("manager_query_failed", err.Error()) } return row, nil } func managerSysOrigin(manager teamManagerInfoRow, user AuthUser) string { if origin := strings.TrimSpace(manager.Origin); origin != "" { return origin } return strings.TrimSpace(user.SysOrigin) } func (s *Service) userIsAdministrator(ctx context.Context, userID int64) (bool, error) { var count int64 if err := s.db.WithContext(ctx). Table("sys_administrator"). Where("user_id = ? AND status = ?", userID, true). Count(&count).Error; err != nil { return false, serverError("administrator_query_failed", err.Error()) } return count > 0, nil } func (s *Service) userIsBDLeader(ctx context.Context, userID int64) (bool, error) { var count int64 if err := s.db.WithContext(ctx). Table("room_bd_lead_base_info"). Where("user_id = ?", userID). Count(&count).Error; err != nil { return false, serverError("bd_leader_query_failed", err.Error()) } return count > 0, nil } func upsertBDBaseInfo(tx *gorm.DB, leader bdLeaderRow, operatorID int64, now time.Time) error { var row businessDevelopmentBaseInfoRow err := tx.Where("user_id = ?", leader.UserID).First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { id, idErr := utils.NextID() if idErr != nil { return serverError("id_generate_failed", idErr.Error()) } leadUserID := leader.UserID return tx.Create(&businessDevelopmentBaseInfoRow{ ID: id, Origin: leader.Origin, BDLeadUserID: &leadUserID, UserID: leader.UserID, Contact: leader.Contact, Region: leader.RegionID, MemberQuantity: 0, CreateTime: now, UpdateTime: now, CreateUser: operatorID, UpdateUser: operatorID, }).Error } if err != nil { return serverError("bd_info_query_failed", err.Error()) } if row.BDLeadUserID != nil && *row.BDLeadUserID > 0 { return badRequest("user_already_bound_bd_leader", "user already has a BD leader") } updates := map[string]any{ "bd_lead_user_id": leader.UserID, "region": leader.RegionID, "update_time": now, "update_user": operatorID, } if leader.Contact != "" { updates["contact"] = leader.Contact } if leader.Origin != "" { updates["sys_origin"] = leader.Origin } if err := tx.Model(&businessDevelopmentBaseInfoRow{}). Where("id = ?", row.ID). Updates(updates).Error; err != nil { return serverError("bd_info_update_failed", err.Error()) } return nil } func upsertUserHistoryBD(tx *gorm.DB, userID int64, operatorID int64, now time.Time) error { var row userHistoryIdentityRow err := tx.Where("user_id = ?", userID).First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return tx.Create(&userHistoryIdentityRow{ UserID: userID, IsBD: true, CreateTime: now, UpdateTime: now, CreateUser: operatorID, UpdateUser: operatorID, }).Error } if err != nil { return serverError("history_identity_query_failed", err.Error()) } if err := tx.Model(&userHistoryIdentityRow{}). Where("user_id = ?", userID). Updates(map[string]any{ "is_bd": true, "update_time": now, "update_user": operatorID, }).Error; err != nil { return serverError("history_identity_update_failed", err.Error()) } return nil } func bdLeaderRowToView(leader bdLeaderRow, user userBaseInfoRow) BDLeaderView { return BDLeaderView{ ID: ID(leader.ID), UserID: ID(leader.UserID), Account: user.Account, UserAvatar: user.UserAvatar, UserNickname: user.UserNickname, Contact: leader.Contact, RegionID: leader.RegionID, OriginSys: leader.Origin, CreateTime: leader.CreateTime, UpdateTime: leader.UpdateTime, } } func bdLeaderListRowToView(row bdLeaderListRow) BDLeaderView { return BDLeaderView{ ID: ID(row.ID), UserID: ID(row.UserID), Account: row.Account, UserAvatar: row.UserAvatar, UserNickname: row.UserNickname, Contact: row.Contact, RegionID: row.RegionID, OriginSys: row.Origin, CreateTime: row.CreateTime, UpdateTime: row.UpdateTime, } }