fix(channel): 统一渠道分析时间筛选范围
让渠道分析弹窗的概览卡片和趋势图共用同一时间筛选范围, 并将默认行为调整为历史全量展示。前端改为在未选择日期时 不再回退最近 12 天,后端新增统一时间窗口解析逻辑,使 overview 与 daily 在全量、指定区间和 days 模式下保持一致。
This commit is contained in:
parent
03214dddf2
commit
fb4b266bac
@ -396,6 +396,46 @@ func (s *service) List(ctx context.Context, in ListInput) (items []*ChannelWithS
|
||||
return
|
||||
}
|
||||
|
||||
func (s *service) resolveStatsRange(ctx context.Context, channelID int64, days int, startDateStr, endDateStr string, now time.Time) (*time.Time, *time.Time, error) {
|
||||
if startDateStr != "" && endDateStr != "" {
|
||||
startDate, err := time.ParseInLocation("2006-01-02", startDateStr, time.Local)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endDate, err := time.ParseInLocation("2006-01-02", endDateStr, time.Local)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||
return &startDate, &endDate, nil
|
||||
}
|
||||
|
||||
if days > 0 {
|
||||
startDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -days+1)
|
||||
endDate := now
|
||||
return &startDate, &endDate, nil
|
||||
}
|
||||
|
||||
type minTimeRow struct {
|
||||
MinCreatedAt *time.Time `gorm:"column:min_created_at"`
|
||||
}
|
||||
var row minTimeRow
|
||||
err := s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders").
|
||||
Joins("JOIN users ON users.id = orders.user_id").
|
||||
Select("MIN(orders.created_at) as min_created_at").
|
||||
Where("users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)", channelID).
|
||||
Scan(&row).Error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if row.MinCreatedAt == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
startDate := time.Date(row.MinCreatedAt.Year(), row.MinCreatedAt.Month(), row.MinCreatedAt.Day(), 0, 0, 0, 0, now.Location())
|
||||
endDate := now
|
||||
return &startDate, &endDate, nil
|
||||
}
|
||||
|
||||
func (s *service) GetStats(ctx context.Context, channelID int64, days int, startDateStr, endDateStr string) (*StatsOutput, error) {
|
||||
now := time.Now()
|
||||
|
||||
@ -411,49 +451,53 @@ func (s *service) GetStats(ctx context.Context, channelID int64, days int, start
|
||||
// source_type: 2=小程序抽奖 3=对对碰 4=一番赏/次卡 5=直播间抽奖抖店(不计入);排除商城直购(1)
|
||||
// actual_amount>0 排除次卡免费使用的订单(避免与购买次卡的订单重复计入GMV)
|
||||
orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)"
|
||||
statsStart, statsEnd, err := s.resolveStatsRange(ctx, channelID, days, startDateStr, endDateStr, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ========== 1. Overview(全量,不限时间)==========
|
||||
// ========== 1. Overview(与当前筛选窗口保持一致)==========
|
||||
|
||||
userCount, _ := s.readDB.Users.WithContext(ctx).Where(s.readDB.Users.ChannelID.Eq(channelID)).Count()
|
||||
userCountQuery := s.readDB.Users.WithContext(ctx)
|
||||
if statsStart != nil && statsEnd != nil {
|
||||
userCountQuery = userCountQuery.Where(s.readDB.Users.CreatedAt.Gte(*statsStart)).Where(s.readDB.Users.CreatedAt.Lte(*statsEnd))
|
||||
}
|
||||
userCount, _ := userCountQuery.Where(s.readDB.Users.ChannelID.Eq(channelID)).Count()
|
||||
out.Overview.TotalUsers = userCount
|
||||
|
||||
type countResult struct{ Count int64 }
|
||||
var cr countResult
|
||||
s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders").
|
||||
overviewOrders := s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders").
|
||||
Joins("JOIN users ON users.id = orders.user_id").
|
||||
Select("count(*) as count").
|
||||
Where(orderFilter, channelID).
|
||||
Scan(&cr)
|
||||
Where(orderFilter, channelID)
|
||||
if statsStart != nil && statsEnd != nil {
|
||||
overviewOrders = overviewOrders.Where("orders.created_at >= ? AND orders.created_at <= ?", *statsStart, *statsEnd)
|
||||
}
|
||||
overviewOrders.Scan(&cr)
|
||||
out.Overview.TotalOrders = cr.Count
|
||||
|
||||
totalGMV, _ := s.calcGMVByTotalAmount(ctx, channelID, "2006-01-02", orderFilter, nil, nil)
|
||||
totalGMV, _ := s.calcGMVByTotalAmount(ctx, channelID, "2006-01-02", orderFilter, statsStart, statsEnd)
|
||||
out.Overview.TotalPaidCents = totalGMV.Total
|
||||
out.Overview.TotalGMV = totalGMV.Total / 100
|
||||
out.Overview.CashCents = totalGMV.Cash
|
||||
out.Overview.CouponCents = totalGMV.Coupon
|
||||
out.Overview.PointsCents = totalGMV.Points
|
||||
|
||||
// 1d. 累计成本(全量,按原始订单/抽奖来源归因)
|
||||
totalCost, _ := s.calcCostByDrawSource(ctx, channelID, "2006-01-02", nil, nil)
|
||||
// 1d. 累计成本(当前筛选窗口,按原始订单/抽奖来源归因)
|
||||
totalCost, _ := s.calcCostByDrawSource(ctx, channelID, "2006-01-02", statsStart, statsEnd)
|
||||
out.Overview.TotalCostCents = totalCost
|
||||
out.Overview.TotalCost = totalCost / 100
|
||||
out.Overview.TotalProfitCents = totalGMV.Total - totalCost
|
||||
out.Overview.TotalProfit = out.Overview.TotalProfitCents / 100
|
||||
|
||||
// ========== 2. 趋势图(按天分组,受 days 限制)==========
|
||||
// ========== 2. 趋势图(按天分组,与 overview 保持相同时间窗口)==========
|
||||
|
||||
var startDate, endDate time.Time
|
||||
if startDateStr != "" && endDateStr != "" {
|
||||
startDate, _ = time.Parse("2006-01-02", startDateStr)
|
||||
endDate, _ = time.Parse("2006-01-02", endDateStr)
|
||||
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||
} else {
|
||||
if days <= 0 {
|
||||
days = 12
|
||||
}
|
||||
startDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -days+1)
|
||||
endDate = now
|
||||
if statsStart == nil || statsEnd == nil {
|
||||
return out, nil
|
||||
}
|
||||
startDate := *statsStart
|
||||
endDate := *statsEnd
|
||||
|
||||
dateMap := make(map[string]*StatsDailyItem)
|
||||
var dateList []string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user