2026-05-22 16:05:12 +08:00

368 lines
11 KiB
Go

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,
}
}