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
|
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) {
|
func (s *service) GetStats(ctx context.Context, channelID int64, days int, startDateStr, endDateStr string) (*StatsOutput, error) {
|
||||||
now := time.Now()
|
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)
|
// source_type: 2=小程序抽奖 3=对对碰 4=一番赏/次卡 5=直播间抽奖抖店(不计入);排除商城直购(1)
|
||||||
// actual_amount>0 排除次卡免费使用的订单(避免与购买次卡的订单重复计入GMV)
|
// 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)"
|
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
|
out.Overview.TotalUsers = userCount
|
||||||
|
|
||||||
type countResult struct{ Count int64 }
|
type countResult struct{ Count int64 }
|
||||||
var cr countResult
|
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").
|
Joins("JOIN users ON users.id = orders.user_id").
|
||||||
Select("count(*) as count").
|
Select("count(*) as count").
|
||||||
Where(orderFilter, channelID).
|
Where(orderFilter, channelID)
|
||||||
Scan(&cr)
|
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
|
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.TotalPaidCents = totalGMV.Total
|
||||||
out.Overview.TotalGMV = totalGMV.Total / 100
|
out.Overview.TotalGMV = totalGMV.Total / 100
|
||||||
out.Overview.CashCents = totalGMV.Cash
|
out.Overview.CashCents = totalGMV.Cash
|
||||||
out.Overview.CouponCents = totalGMV.Coupon
|
out.Overview.CouponCents = totalGMV.Coupon
|
||||||
out.Overview.PointsCents = totalGMV.Points
|
out.Overview.PointsCents = totalGMV.Points
|
||||||
|
|
||||||
// 1d. 累计成本(全量,按原始订单/抽奖来源归因)
|
// 1d. 累计成本(当前筛选窗口,按原始订单/抽奖来源归因)
|
||||||
totalCost, _ := s.calcCostByDrawSource(ctx, channelID, "2006-01-02", nil, nil)
|
totalCost, _ := s.calcCostByDrawSource(ctx, channelID, "2006-01-02", statsStart, statsEnd)
|
||||||
out.Overview.TotalCostCents = totalCost
|
out.Overview.TotalCostCents = totalCost
|
||||||
out.Overview.TotalCost = totalCost / 100
|
out.Overview.TotalCost = totalCost / 100
|
||||||
out.Overview.TotalProfitCents = totalGMV.Total - totalCost
|
out.Overview.TotalProfitCents = totalGMV.Total - totalCost
|
||||||
out.Overview.TotalProfit = out.Overview.TotalProfitCents / 100
|
out.Overview.TotalProfit = out.Overview.TotalProfitCents / 100
|
||||||
|
|
||||||
// ========== 2. 趋势图(按天分组,受 days 限制)==========
|
// ========== 2. 趋势图(按天分组,与 overview 保持相同时间窗口)==========
|
||||||
|
|
||||||
var startDate, endDate time.Time
|
if statsStart == nil || statsEnd == nil {
|
||||||
if startDateStr != "" && endDateStr != "" {
|
return out, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
startDate := *statsStart
|
||||||
|
endDate := *statsEnd
|
||||||
|
|
||||||
dateMap := make(map[string]*StatsDailyItem)
|
dateMap := make(map[string]*StatsDailyItem)
|
||||||
var dateList []string
|
var dateList []string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user