diff --git a/internal/api/admin/dashboard_admin.go b/internal/api/admin/dashboard_admin.go index 7f5f6e5..4ec72d8 100755 --- a/internal/api/admin/dashboard_admin.go +++ b/internal/api/admin/dashboard_admin.go @@ -1855,18 +1855,44 @@ type productPerformanceItem struct { func (h *handler) OperationsProductPerformance() core.HandlerFunc { return func(ctx core.Context) { s, e := parseRange(ctx.Request().URL.Query().Get("rangeType"), "", "") + db := h.repo.GetDbR().WithContext(ctx.RequestContext()) - // 按活动聚合抽奖数据 - type drawRow struct { - ActivityID int64 `gorm:"column:activity_id"` - Count int64 `gorm:"column:count"` - TotalCost int64 `gorm:"column:total_cost"` + type performanceRow struct { + ActivityID int64 `gorm:"column:activity_id"` + SalesCount int64 `gorm:"column:sales_count"` + PaymentCount int64 `gorm:"column:payment_count"` + GamePassCount int64 `gorm:"column:game_pass_count"` + TotalCost int64 `gorm:"column:total_cost"` + PriceDraw int64 `gorm:"column:price_draw"` + RevenueCents int64 `gorm:"column:revenue_cents"` + ContributionBase int64 `gorm:"column:contribution_base"` } - var rows []drawRow + var rows []performanceRow - // 统计抽奖日志,按活动分组,并计算奖品成本 - if err := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).UnderlyingDB(). + if err := db.Table(model.TableNameActivityDrawLogs). + Select(` + activity_issues.activity_id, + SUM(CASE WHEN orders.status = 2 AND ( + orders.source_type = 4 + OR orders.order_no LIKE 'GP%' + OR (orders.actual_amount = 0 AND COALESCE(orders.remark, '') LIKE '%use_game_pass%') + ) THEN 1 ELSE 0 END) as game_pass_count, + SUM(CASE WHEN orders.status = 2 AND NOT ( + orders.source_type = 4 + OR orders.order_no LIKE 'GP%' + OR (orders.actual_amount = 0 AND COALESCE(orders.remark, '') LIKE '%use_game_pass%') + ) THEN 1 ELSE 0 END) as payment_count, + SUM(CASE WHEN orders.status = 2 THEN 1 ELSE 0 END) as sales_count, + COALESCE(MAX(activities.price_draw), 0) as price_draw, + SUM(CASE WHEN orders.status = 2 THEN COALESCE(activities.price_draw, 0) ELSE 0 END) as revenue_cents, + SUM(CASE WHEN orders.status = 2 THEN COALESCE(activities.price_draw, 0) ELSE 0 END) as contribution_base, + CAST(SUM(CASE WHEN orders.status = 2 THEN COALESCE(products.cost_price, 0) * ( + COALESCE(NULLIF(activity_reward_settings.drop_quantity, 0), 1) + + CASE WHEN user_item_cards.used_draw_log_id = activity_draw_logs.id AND system_item_cards.effect_type = 1 AND system_item_cards.reward_multiplier_x1000 >= 2000 THEN 1 ELSE 0 END + ) ELSE 0 END) AS SIGNED) as total_cost + `). Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id"). + Joins("JOIN activities ON activities.id = activity_issues.activity_id"). Joins("LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id"). Joins("LEFT JOIN products ON products.id = activity_reward_settings.product_id"). Joins("LEFT JOIN orders ON orders.id = activity_draw_logs.order_id"). @@ -1874,41 +1900,33 @@ func (h *handler) OperationsProductPerformance() core.HandlerFunc { Joins("LEFT JOIN system_item_cards ON system_item_cards.id = user_item_cards.card_id"). Where("activity_draw_logs.created_at >= ?", s). Where("activity_draw_logs.created_at <= ?", e). - Select( - "activity_issues.activity_id", - "COUNT(activity_draw_logs.id) as count", - "CAST(SUM(IF(activity_draw_logs.is_winner = 1, COALESCE(activity_reward_settings.price_snapshot_cents, products.price, 0) * GREATEST(COALESCE(system_item_cards.reward_multiplier_x1000, 1000), 1000) / 1000, 0)) AS SIGNED) as total_cost", - ). Group("activity_issues.activity_id"). - Order("count DESC"). + Order("sales_count DESC"). Limit(10). Scan(&rows).Error; err != nil { - h.logger.Error(fmt.Sprintf("OperationsProductPerformance draw cost stats error: %v", err)) + h.logger.Error(fmt.Sprintf("OperationsProductPerformance stats error: %v", err)) } - // 获取活动详情(名称和单价) activityIDs := make([]int64, len(rows)) for i, r := range rows { activityIDs[i] = r.ActivityID } type actInfo struct { - Name string - PriceDraw int64 + Name string } actMap := make(map[int64]actInfo) if len(activityIDs) > 0 { acts, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).ReadDB(). Where(h.readDB.Activities.ID.In(activityIDs...)).Find() for _, a := range acts { - actMap[a.ID] = actInfo{Name: a.Name, PriceDraw: a.PriceDraw} + actMap[a.ID] = actInfo{Name: a.Name} } } - // 计算总数用于贡献率 - var totalCount int64 + var totalRevenueCents int64 for _, r := range rows { - totalCount += r.Count + totalRevenueCents += r.ContributionBase } out := make([]productPerformanceItem, len(rows)) @@ -1916,30 +1934,29 @@ func (h *handler) OperationsProductPerformance() core.HandlerFunc { info := actMap[r.ActivityID] var contribution float64 - if totalCount > 0 { - contribution = float64(r.Count) / float64(totalCount) * 100 + if totalRevenueCents > 0 { + contribution = float64(r.ContributionBase) / float64(totalRevenueCents) * 100 } - // 周转率简化计算 days := e.Sub(s).Hours() / 24 if days < 1 { days = 1 } - turnover := float64(r.Count) / days * 7 + turnover := float64(r.SalesCount) / days * 7 + profitCents := r.RevenueCents - r.TotalCost out[i] = productPerformanceItem{ ID: r.ActivityID, SeriesName: info.Name, - SalesCount: r.Count, - Amount: (r.Count * info.PriceDraw) / 100, // 转换为元 - Profit: (r.Count*info.PriceDraw - r.TotalCost) / 100, + SalesCount: r.SalesCount, + Amount: r.RevenueCents / 100, + Profit: profitCents / 100, ProfitRate: 0, ContributionRate: float64(int(contribution*10)) / 10.0, InventoryTurnover: float64(int(turnover*10)) / 10.0, } - if r.Count > 0 && info.PriceDraw > 0 { - revenue := r.Count * info.PriceDraw - pr := float64(revenue-r.TotalCost) / float64(revenue) * 100 + if r.RevenueCents > 0 { + pr := float64(profitCents) / float64(r.RevenueCents) * 100 out[i].ProfitRate = float64(int(pr*10)) / 10.0 } }