package agent import ( "context" "sort" "strings" "time" "ycc-server/app/main/model" "ycc-server/common/ctxdata" "ycc-server/common/globalkey" "ycc-server/common/xerr" "ycc-server/pkg/lzkit/crypto" "github.com/Masterminds/squirrel" "github.com/pkg/errors" "ycc-server/app/main/api/internal/svc" "ycc-server/app/main/api/internal/types" "github.com/zeromicro/go-zero/core/logx" ) type GetTeamListLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetTeamListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTeamListLogic { return &GetTeamListLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetTeamListLogic) GetTeamList(req *types.GetTeamListReq) (resp *types.GetTeamListResp, err error) { userID, err := ctxdata.GetUidFromCtx(l.ctx) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err) } // 1. 获取代理信息 agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "") } return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err) } // 2. 递归查询所有下级(直接+间接) allSubordinateIds := make(map[string]bool) directSubordinateIds := make(map[string]bool) // 递归函数:收集所有下级ID var collectSubordinates func(string) error collectSubordinates = func(parentId string) error { // 查询直接下级 builder := l.svcCtx.AgentRelationModel.SelectBuilder(). Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo) relations, err := l.svcCtx.AgentRelationModel.FindAll(l.ctx, builder, "") if err != nil { return err } for _, relation := range relations { // 如果是第一层,标记为直接下级 if parentId == agent.Id { directSubordinateIds[relation.ChildId] = true } // 添加到所有下级集合 allSubordinateIds[relation.ChildId] = true // 递归查询下级的下级 if err := collectSubordinates(relation.ChildId); err != nil { return err } } return nil } // 开始递归收集所有下级 if err := collectSubordinates(agent.Id); err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级关系失败, %v", err) } // 3. 如果没有下级,返回空数据 if len(allSubordinateIds) == 0 { return &types.GetTeamListResp{ Statistics: types.TeamStatistics{}, Total: 0, List: []types.TeamMemberItem{}, }, nil } // 4. 将下级ID转换为切片用于查询 subordinateIds := make([]string, 0, len(allSubordinateIds)) for id := range allSubordinateIds { subordinateIds = append(subordinateIds, id) } // 5. 计算时间范围 now := time.Now() todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) // 6. 统计顶部数据 statistics := l.calculateTeamStatistics(agent.Id, subordinateIds, todayStart, monthStart) // 7. 查询所有下级代理信息(不进行手机号过滤,因为需要模糊搜索) builder := l.svcCtx.AgentModel.SelectBuilder(). Where(squirrel.Eq{"id": subordinateIds}). Where("del_state = ?", globalkey.DelStateNo) allTeamMembers, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "") if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询团队成员失败, %v", err) } // 8. 如果有手机号搜索条件,进行模糊匹配过滤(在内存中) var filteredMembers []*model.Agent searchMobile := strings.TrimSpace(req.Mobile) if searchMobile != "" { // 解密所有手机号并进行模糊匹配 for _, member := range allTeamMembers { if member.Mobile != "" { decryptedMobile, err := crypto.DecryptMobile(member.Mobile, l.svcCtx.Config.Encrypt.SecretKey) if err == nil && strings.Contains(decryptedMobile, searchMobile) { filteredMembers = append(filteredMembers, member) } } } } else { // 没有搜索条件,使用所有成员 filteredMembers = allTeamMembers } // 9. 按创建时间倒序排序 sort.Slice(filteredMembers, func(i, j int) bool { return filteredMembers[i].CreateTime.After(filteredMembers[j].CreateTime) }) // 10. 分页处理 total := int64(len(filteredMembers)) page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } offset := (page - 1) * pageSize // 计算分页范围 start := int(offset) end := start + int(pageSize) if start > len(filteredMembers) { start = len(filteredMembers) } if end > len(filteredMembers) { end = len(filteredMembers) } var teamMembers []*model.Agent if start < end { teamMembers = filteredMembers[start:end] } // 11. 组装响应列表 var list []types.TeamMemberItem for _, member := range teamMembers { memberItem := l.buildTeamMemberItem(agent.Id, member, directSubordinateIds, todayStart) list = append(list, memberItem) } return &types.GetTeamListResp{ Statistics: statistics, Total: total, List: list, }, nil } // calculateTeamStatistics 计算团队统计数据 // 注意:所有统计都基于 subordinateIds(下级ID列表),不包含自己的数据 func (l *GetTeamListLogic) calculateTeamStatistics(agentId string, subordinateIds []string, todayStart, monthStart time.Time) types.TeamStatistics { // 团队成员总数:只统计下级,不包括自己 stats := types.TeamStatistics{ TotalMembers: int64(len(subordinateIds)), } // 统计今日和本月新增成员(只统计下级,不包括自己) todayNewCount := int64(0) monthNewCount := int64(0) for _, id := range subordinateIds { member, err := l.svcCtx.AgentModel.FindOne(l.ctx, id) if err != nil { continue } if member.CreateTime.After(todayStart) { todayNewCount++ } if member.CreateTime.After(monthStart) { monthNewCount++ } } stats.TodayNewMembers = todayNewCount stats.MonthNewMembers = monthNewCount // 统计团队总查询量(仅统计有返佣给当前用户的订单,通过agent_rebate表去重统计order_id) if len(subordinateIds) > 0 { rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder(). Where("agent_id = ? AND del_state = ?", agentId, globalkey.DelStateNo). Where("source_agent_id != ?", agentId). // 明确排除自己 Where(squirrel.Eq{"source_agent_id": subordinateIds}) // 统计去重的订单数量 totalQueries := l.countDistinctOrders(l.ctx, rebateBuilder) stats.TotalQueries = totalQueries // 今日推广量(仅统计有返佣的订单) todayRebateBuilder := rebateBuilder.Where("create_time >= ?", todayStart) todayPromotions := l.countDistinctOrders(l.ctx, todayRebateBuilder) stats.TodayPromotions = todayPromotions // 本月推广量(仅统计有返佣的订单) monthRebateBuilder := rebateBuilder.Where("create_time >= ?", monthStart) monthPromotions := l.countDistinctOrders(l.ctx, monthRebateBuilder) stats.MonthPromotions = monthPromotions } // 统计收益:只统计从下级获得的返佣(依靠团队得到的收益) // 返佣:从agent_rebate表统计(从下级获得的返佣) // agent_id = 当前代理ID(获得返佣的代理) // source_agent_id IN 下级ID列表(来源代理,即产生订单的下级代理) // 明确排除自己:source_agent_id != agentId(确保不统计自己的数据) if len(subordinateIds) > 0 { rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder(). Where("agent_id = ? AND del_state = ?", agentId, globalkey.DelStateNo). Where("source_agent_id != ?", agentId). // 明确排除自己 Where(squirrel.Eq{"source_agent_id": subordinateIds}) totalRebate, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder, "rebate_amount") todayRebate, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder.Where("create_time >= ?", todayStart), "rebate_amount") monthRebate, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder.Where("create_time >= ?", monthStart), "rebate_amount") stats.TotalEarnings = totalRebate stats.TodayEarnings = todayRebate stats.MonthEarnings = monthRebate } return stats } // countDistinctOrders 统计去重的订单数量(通过返佣记录) func (l *GetTeamListLogic) countDistinctOrders(ctx context.Context, builder squirrel.SelectBuilder) int64 { // 查询所有返佣记录,在内存中去重订单ID rebates, err := l.svcCtx.AgentRebateModel.FindAll(ctx, builder, "") if err != nil { return 0 } orderIdSet := make(map[string]bool) for _, rebate := range rebates { orderIdSet[rebate.OrderId] = true } return int64(len(orderIdSet)) } // countDistinctOrdersForMember 统计单个成员的去重订单数量(通过返佣记录) func (l *GetTeamListLogic) countDistinctOrdersForMember(ctx context.Context, builder squirrel.SelectBuilder) int64 { // 查询所有返佣记录,在内存中去重订单ID rebates, err := l.svcCtx.AgentRebateModel.FindAll(ctx, builder, "") if err != nil { return 0 } orderIdSet := make(map[string]bool) for _, rebate := range rebates { orderIdSet[rebate.OrderId] = true } return int64(len(orderIdSet)) } // buildTeamMemberItem 构建团队成员项 func (l *GetTeamListLogic) buildTeamMemberItem(agentId string, member *model.Agent, directSubordinateIds map[string]bool, todayStart time.Time) types.TeamMemberItem { levelName := "" switch member.Level { case 1: levelName = "普通" case 2: levelName = "黄金" case 3: levelName = "钻石" } // 解密手机号 mobile := "" if member.Mobile != "" { decrypted, err := crypto.DecryptMobile(member.Mobile, l.svcCtx.Config.Encrypt.SecretKey) if err == nil { mobile = decrypted } } // 统计查询量(仅统计有返佣给当前用户的订单,通过agent_rebate表去重统计order_id) rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder(). Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agentId, member.Id, globalkey.DelStateNo) totalQueries := l.countDistinctOrdersForMember(l.ctx, rebateBuilder) todayRebateBuilder := rebateBuilder.Where("create_time >= ?", todayStart) todayQueries := l.countDistinctOrdersForMember(l.ctx, todayRebateBuilder) // 统计返佣给我的金额(从agent_rebate表,source_agent_id = member.Id, agent_id = agentId) totalRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder, "rebate_amount") todayRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder.Where("create_time >= ?", todayStart), "rebate_amount") // 统计邀请人数(从agent_relation表,parent_id = member.Id) inviteBuilder := l.svcCtx.AgentRelationModel.SelectBuilder(). Where("parent_id = ? AND relation_type = ? AND del_state = ?", member.Id, 1, globalkey.DelStateNo) totalInvites, _ := l.svcCtx.AgentRelationModel.FindCount(l.ctx, inviteBuilder, "id") todayInvites, _ := l.svcCtx.AgentRelationModel.FindCount(l.ctx, inviteBuilder.Where("create_time >= ?", todayStart), "id") // 判断是否直接下级 isDirect := directSubordinateIds[member.Id] return types.TeamMemberItem{ AgentId: member.Id, Level: member.Level, LevelName: levelName, Mobile: mobile, CreateTime: member.CreateTime.Format("2006-01-02 15:04:05"), TodayQueries: todayQueries, TotalQueries: totalQueries, TotalRebateAmount: totalRebateAmount, TodayRebateAmount: todayRebateAmount, TotalInvites: totalInvites, TodayInvites: todayInvites, IsDirect: isDirect, } }