Files
ycc-proxy-server/app/main/api/internal/logic/agent/getteamlistlogic.go
2025-12-09 18:55:28 +08:00

342 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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