package agent import ( "context" "time" "ycc-server/app/main/model" "ycc-server/common/ctxdata" "ycc-server/common/globalkey" "ycc-server/common/xerr" "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 GetConversionRateLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetConversionRateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetConversionRateLogic { return &GetConversionRateLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetConversionRateLogic) GetConversionRate() (resp *types.ConversionRateResp, 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. 统计我的转化率 myConversionRate := l.calculateConversionRate(agent.Id, nil) // 3. 统计下级转化率(按时间段动态查询历史下级关系) subordinateConversionRate := l.calculateSubordinateConversionRate(agent.Id) return &types.ConversionRateResp{ MyConversionRate: myConversionRate, SubordinateConversionRate: subordinateConversionRate, }, nil } // calculateSubordinateConversionRate 计算下级转化率(考虑历史关系) func (l *GetConversionRateLogic) calculateSubordinateConversionRate(parentAgentId int64) types.ConversionRateData { // 使用Asia/Shanghai时区,与数据库保持一致 loc, _ := time.LoadLocation("Asia/Shanghai") now := time.Now().In(loc) // 日统计:今日、昨日、前日 todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc) todayEnd := todayStart.AddDate(0, 0, 1) yesterdayStart := todayStart.AddDate(0, 0, -1) yesterdayEnd := todayStart dayBeforeStart := todayStart.AddDate(0, 0, -2) dayBeforeEnd := yesterdayStart daily := []types.PeriodConversionData{ l.calculateSubordinatePeriodConversion(parentAgentId, "今日", todayStart, todayEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "昨日", yesterdayStart, yesterdayEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "前日", dayBeforeStart, dayBeforeEnd), } // 周统计:本周、上周、上上周 weekdayOffset := int(now.Weekday()) if weekdayOffset == 0 { weekdayOffset = 7 // 周日算作第7天 } thisWeekStart := now.AddDate(0, 0, -(weekdayOffset - 1)) thisWeekStart = time.Date(thisWeekStart.Year(), thisWeekStart.Month(), thisWeekStart.Day(), 0, 0, 0, 0, loc) thisWeekEnd := now lastWeekStart := thisWeekStart.AddDate(0, 0, -7) lastWeekEnd := thisWeekStart lastTwoWeekStart := thisWeekStart.AddDate(0, 0, -14) lastTwoWeekEnd := lastWeekStart weekly := []types.PeriodConversionData{ l.calculateSubordinatePeriodConversion(parentAgentId, "本周", thisWeekStart, thisWeekEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "上周", lastWeekStart, lastWeekEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "上上周", lastTwoWeekStart, lastTwoWeekEnd), } // 月统计:本月、上月、前两月 thisMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, loc) thisMonthEnd := now lastMonthStart := thisMonthStart.AddDate(0, -1, 0) lastMonthEnd := thisMonthStart lastTwoMonthStart := thisMonthStart.AddDate(0, -2, 0) lastTwoMonthEnd := lastMonthStart monthly := []types.PeriodConversionData{ l.calculateSubordinatePeriodConversion(parentAgentId, "本月", thisMonthStart, thisMonthEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "上月", lastMonthStart, lastMonthEnd), l.calculateSubordinatePeriodConversion(parentAgentId, "前两月", lastTwoMonthStart, lastTwoMonthEnd), } return types.ConversionRateData{ Daily: daily, Weekly: weekly, Monthly: monthly, } } // calculateConversionRate 计算转化率 // agentId > 0 时统计该代理的转化率,否则统计 subordinateIds 列表的转化率 func (l *GetConversionRateLogic) calculateConversionRate(agentId int64, subordinateIds []int64) types.ConversionRateData { // 使用Asia/Shanghai时区,与数据库保持一致 loc, _ := time.LoadLocation("Asia/Shanghai") now := time.Now().In(loc) // 日统计:今日、昨日、前日 todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc) todayEnd := todayStart.AddDate(0, 0, 1) yesterdayStart := todayStart.AddDate(0, 0, -1) yesterdayEnd := todayStart dayBeforeStart := todayStart.AddDate(0, 0, -2) dayBeforeEnd := yesterdayStart daily := []types.PeriodConversionData{ l.calculatePeriodConversion(agentId, subordinateIds, "今日", todayStart, todayEnd), l.calculatePeriodConversion(agentId, subordinateIds, "昨日", yesterdayStart, yesterdayEnd), l.calculatePeriodConversion(agentId, subordinateIds, "前日", dayBeforeStart, dayBeforeEnd), } // 周统计:本周、上周、上上周 // 本周:本周一00:00:00 到现在 weekdayOffset := int(now.Weekday()) if weekdayOffset == 0 { weekdayOffset = 7 // 周日算作第7天 } // 计算本周一:当前日期减去(weekdayOffset-1)天 thisWeekStart := now.AddDate(0, 0, -(weekdayOffset - 1)) thisWeekStart = time.Date(thisWeekStart.Year(), thisWeekStart.Month(), thisWeekStart.Day(), 0, 0, 0, 0, loc) thisWeekEnd := now lastWeekStart := thisWeekStart.AddDate(0, 0, -7) lastWeekEnd := thisWeekStart lastTwoWeekStart := thisWeekStart.AddDate(0, 0, -14) lastTwoWeekEnd := lastWeekStart weekly := []types.PeriodConversionData{ l.calculatePeriodConversion(agentId, subordinateIds, "本周", thisWeekStart, thisWeekEnd), l.calculatePeriodConversion(agentId, subordinateIds, "上周", lastWeekStart, lastWeekEnd), l.calculatePeriodConversion(agentId, subordinateIds, "上上周", lastTwoWeekStart, lastTwoWeekEnd), } // 月统计:本月、上月、前两月 thisMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, loc) thisMonthEnd := now lastMonthStart := thisMonthStart.AddDate(0, -1, 0) lastMonthEnd := thisMonthStart lastTwoMonthStart := thisMonthStart.AddDate(0, -2, 0) lastTwoMonthEnd := lastMonthStart monthly := []types.PeriodConversionData{ l.calculatePeriodConversion(agentId, subordinateIds, "本月", thisMonthStart, thisMonthEnd), l.calculatePeriodConversion(agentId, subordinateIds, "上月", lastMonthStart, lastMonthEnd), l.calculatePeriodConversion(agentId, subordinateIds, "前两月", lastTwoMonthStart, lastTwoMonthEnd), } return types.ConversionRateData{ Daily: daily, Weekly: weekly, Monthly: monthly, } } // calculatePeriodConversion 计算指定时间段的转化率数据 func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subordinateIds []int64, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData { // 构建 agent_order 查询条件 agentOrderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder(). Where("del_state = ?", globalkey.DelStateNo). Where("create_time >= ? AND create_time < ?", startTime, endTime) if agentId > 0 { // 统计我的转化率 agentOrderBuilder = agentOrderBuilder.Where("agent_id = ?", agentId) } else if len(subordinateIds) > 0 { // 统计下级转化率 agentOrderBuilder = agentOrderBuilder.Where(squirrel.Eq{"agent_id": subordinateIds}) } else { // 没有数据 l.Infof("calculatePeriodConversion: 没有代理ID或下级ID,periodLabel=%s", periodLabel) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 添加调试日志 if agentId == 0 && len(subordinateIds) > 0 { l.Infof("calculatePeriodConversion: 统计下级转化率,periodLabel=%s, startTime=%v, endTime=%v, subordinateIds数量=%d", periodLabel, startTime, endTime, len(subordinateIds)) } // 查询所有代理订单 agentOrders, err := l.svcCtx.AgentOrderModel.FindAll(l.ctx, agentOrderBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询代理订单失败: periodLabel=%s, err=%v", periodLabel, err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } if len(agentOrders) == 0 { if agentId == 0 && len(subordinateIds) > 0 { l.Infof("calculatePeriodConversion: 未找到代理订单,periodLabel=%s, startTime=%v, endTime=%v", periodLabel, startTime, endTime) } return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } l.Infof("calculatePeriodConversion: 找到代理订单数量=%d, periodLabel=%s", len(agentOrders), periodLabel) // 收集订单ID orderIds := make([]int64, 0, len(agentOrders)) for _, ao := range agentOrders { orderIds = append(orderIds, ao.OrderId) } // 查询订单信息 orderBuilder := l.svcCtx.OrderModel.SelectBuilder(). Where(squirrel.Eq{"id": orderIds}). Where("del_state = ?", globalkey.DelStateNo) orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, orderBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询订单失败: %v", err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 统计查询订单数、付费订单数、用户数和总金额 var totalAmount float64 paidOrderCount := 0 queryUserSet := make(map[int64]bool) paidUserSet := make(map[int64]bool) for _, order := range orders { // 查询用户数(所有订单的用户,去重) queryUserSet[order.UserId] = true // 付费订单数和总金额(只统计已支付的订单) if order.Status == "paid" { paidOrderCount++ paidUserSet[order.UserId] = true totalAmount += order.Amount } } // 查询订单数 = 所有订单数量 queryOrderCount := len(orders) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: int64(queryOrderCount), // 订单数量(保持向后兼容) PaidUsers: int64(paidOrderCount), // 订单数量(保持向后兼容) TotalAmount: totalAmount, QueryUserCount: int64(len(queryUserSet)), // 用户数量(新增) PaidUserCount: int64(len(paidUserSet)), // 用户数量(新增) } } // calculateSubordinatePeriodConversion 计算指定时间段内下级转化率 // 结合使用agent_rebate表和agent_order表: // 1. 查询量:通过agent_order表统计所有查询(包括未付费的) // 2. 付费量和金额:通过agent_rebate表统计(只有付费的订单才会产生返佣) func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgentId int64, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData { // 1. 查询agent_rebate表:获取所有曾经给当前用户产生返佣的source_agent_id(这些代理在某个时间点是下级) // 不限制时间,获取所有历史返佣记录,用于确定哪些代理曾经是下级 rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder(). Where("agent_id = ? AND del_state = ?", parentAgentId, globalkey.DelStateNo). Where("source_agent_id != ?", parentAgentId) allRebates, err := l.svcCtx.AgentRebateModel.FindAll(l.ctx, rebateBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询返佣记录失败: periodLabel=%s, err=%v", periodLabel, err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 收集所有曾经产生返佣的source_agent_id(这些代理在某个时间点是下级) sourceAgentIdSet := make(map[int64]bool) paidOrderIdSet := make(map[int64]bool) // 已付费的订单ID(有返佣的订单) paidOrderIdToAmount := make(map[int64]float64) // 已付费订单的金额 for _, rebate := range allRebates { sourceAgentIdSet[rebate.SourceAgentId] = true // 如果返佣记录的创建时间在时间段内,说明该订单在时间段内已付费 if rebate.CreateTime.After(startTime) || rebate.CreateTime.Equal(startTime) { if rebate.CreateTime.Before(endTime) { paidOrderIdSet[rebate.OrderId] = true } } } if len(sourceAgentIdSet) == 0 { l.Infof("calculateSubordinatePeriodConversion: 未找到返佣记录,periodLabel=%s, startTime=%v, endTime=%v", periodLabel, startTime, endTime) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } sourceAgentIds := make([]int64, 0, len(sourceAgentIdSet)) for agentId := range sourceAgentIdSet { sourceAgentIds = append(sourceAgentIds, agentId) } l.Infof("calculateSubordinatePeriodConversion: periodLabel=%s, 曾经产生返佣的代理数量=%d", periodLabel, len(sourceAgentIds)) // 2. 查询agent_order表:统计这些代理在时间段内的所有订单(包括未付费的) agentOrderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder(). Where("del_state = ?", globalkey.DelStateNo). Where("create_time >= ? AND create_time < ?", startTime, endTime). Where(squirrel.Eq{"agent_id": sourceAgentIds}) agentOrders, err := l.svcCtx.AgentOrderModel.FindAll(l.ctx, agentOrderBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询代理订单失败: periodLabel=%s, err=%v", periodLabel, err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } l.Infof("calculateSubordinatePeriodConversion: periodLabel=%s, 查询到代理订单数量=%d", periodLabel, len(agentOrders)) if len(agentOrders) == 0 { return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 3. 通过order_id去重,获取所有订单ID(用于查询订单详情) orderIdSet := make(map[int64]bool) orderIdToAgentOrder := make(map[int64]*model.AgentOrder) for _, ao := range agentOrders { orderIdSet[ao.OrderId] = true // 如果同一个订单有多个agent_order记录,保留金额更大的 if existing, exists := orderIdToAgentOrder[ao.OrderId]; exists { if ao.OrderAmount > existing.OrderAmount { orderIdToAgentOrder[ao.OrderId] = ao } } else { orderIdToAgentOrder[ao.OrderId] = ao } } orderIds := make([]int64, 0, len(orderIdSet)) for orderId := range orderIdSet { orderIds = append(orderIds, orderId) } // 4. 查询订单信息 orderBuilder := l.svcCtx.OrderModel.SelectBuilder(). Where(squirrel.Eq{"id": orderIds}). Where("del_state = ?", globalkey.DelStateNo) orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, orderBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询订单失败: %v", err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 5. 查询时间段内的返佣记录,获取已付费订单的金额 rebateBuilder = l.svcCtx.AgentRebateModel.SelectBuilder(). Where("agent_id = ? AND del_state = ?", parentAgentId, globalkey.DelStateNo). Where("source_agent_id != ?", parentAgentId). Where("create_time >= ? AND create_time < ?", startTime, endTime). Where(squirrel.Eq{"order_id": orderIds}) rebates, err := l.svcCtx.AgentRebateModel.FindAll(l.ctx, rebateBuilder, "") if err != nil && !errors.Is(err, model.ErrNotFound) { l.Errorf("查询返佣记录失败: %v", err) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: 0, PaidUsers: 0, TotalAmount: 0, QueryUserCount: 0, PaidUserCount: 0, } } // 记录已付费订单的金额(使用agent_order的order_amount) for _, rebate := range rebates { if ao, exists := orderIdToAgentOrder[rebate.OrderId]; exists { paidOrderIdToAmount[rebate.OrderId] = ao.OrderAmount } } // 6. 统计查询订单数、付费订单数、用户数和总金额 var totalAmount float64 paidOrderCount := 0 queryUserSet := make(map[int64]bool) paidUserSet := make(map[int64]bool) for _, order := range orders { // 查询用户数(所有订单的用户,去重) queryUserSet[order.UserId] = true // 付费订单数和总金额(只统计已付费的订单,即order_id在paidOrderIdToAmount中) if _, isPaid := paidOrderIdToAmount[order.Id]; isPaid { paidOrderCount++ paidUserSet[order.UserId] = true // 使用agent_order的order_amount(用户实际支付金额) totalAmount += paidOrderIdToAmount[order.Id] } } // 查询订单数 = 所有订单数量 queryOrderCount := len(orders) l.Infof("calculateSubordinatePeriodConversion: periodLabel=%s, 查询订单数=%d, 付费订单数=%d, 查询用户数=%d, 付费用户数=%d, 总金额=%.2f", periodLabel, queryOrderCount, paidOrderCount, len(queryUserSet), len(paidUserSet), totalAmount) return types.PeriodConversionData{ PeriodLabel: periodLabel, QueryUsers: int64(queryOrderCount), // 订单数量(保持向后兼容) PaidUsers: int64(paidOrderCount), // 订单数量(保持向后兼容) TotalAmount: totalAmount, QueryUserCount: int64(len(queryUserSet)), // 用户数量(新增) PaidUserCount: int64(len(paidUserSet)), // 用户数量(新增) } }