package repository import ( "context" "fmt" "time" "cpa-ext/internal/collection" ) func (r *SQLiteUsageRepository) UsageDashboard(ctx context.Context, today time.Time, days int) (collection.UsageDashboard, error) { if days < 1 { days = 7 } start := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location()) dashboard := collection.UsageDashboard{} if err := r.db.QueryRowContext(ctx, ` SELECT COUNT(*), COALESCE(SUM(u.input_tokens),0), COALESCE(SUM(u.output_tokens),0), COALESCE(SUM(u.total_tokens),0), COALESCE(SUM(u.cost_micros),0) FROM request_detail_index d LEFT JOIN usage_records u ON u.id=d.usage_id WHERE d.requested_at>=?`, formatTime(start)).Scan(&dashboard.Today.Requests, &dashboard.Today.InputTokens, &dashboard.Today.OutputTokens, &dashboard.Today.TotalTokens, &dashboard.Today.CostMicros); err != nil { return dashboard, fmt.Errorf("汇总今日用量: %w", err) } rows, err := r.db.QueryContext(ctx, ` SELECT d.managed_key_id, COALESCE(k.name, '未识别'), SUM(CASE WHEN d.requested_at>=? THEN 1 ELSE 0 END), COALESCE(SUM(CASE WHEN d.requested_at>=? THEN u.input_tokens ELSE 0 END),0), COALESCE(SUM(CASE WHEN d.requested_at>=? THEN u.output_tokens ELSE 0 END),0), COALESCE(SUM(CASE WHEN d.requested_at>=? THEN u.total_tokens ELSE 0 END),0), COALESCE(SUM(CASE WHEN d.requested_at>=? THEN u.cost_micros ELSE 0 END),0), MAX(d.requested_at) FROM request_detail_index d LEFT JOIN usage_records u ON u.id=d.usage_id LEFT JOIN managed_keys k ON k.id=d.managed_key_id GROUP BY d.managed_key_id, COALESCE(k.name, '未识别') ORDER BY 6 DESC, 2`, formatTime(start), formatTime(start), formatTime(start), formatTime(start), formatTime(start)) if err != nil { return dashboard, fmt.Errorf("汇总用户用量: %w", err) } for rows.Next() { var item collection.UserUsageSummary var last string if err := rows.Scan(&item.KeyID, &item.KeyAlias, &item.Today.Requests, &item.Today.InputTokens, &item.Today.OutputTokens, &item.Today.TotalTokens, &item.Today.CostMicros, &last); err != nil { _ = rows.Close() return dashboard, fmt.Errorf("读取用户用量汇总: %w", err) } if parsed, parseErr := time.Parse(time.RFC3339Nano, last); parseErr == nil { item.LastUsedAt = &parsed } dashboard.Users = append(dashboard.Users, item) } _ = rows.Close() dailyStart := start.AddDate(0, 0, -(days - 1)) rows, err = r.db.QueryContext(ctx, ` SELECT DATE(d.requested_at, '+8 hours'), COALESCE(SUM(u.total_tokens),0) FROM request_detail_index d LEFT JOIN usage_records u ON u.id=d.usage_id WHERE d.requested_at>=? GROUP BY DATE(d.requested_at, '+8 hours') ORDER BY 1`, formatTime(dailyStart)) if err != nil { return dashboard, fmt.Errorf("汇总每日用量: %w", err) } byDate := make(map[string]int64) for rows.Next() { var date string var tokens int64 if err := rows.Scan(&date, &tokens); err != nil { _ = rows.Close() return dashboard, fmt.Errorf("读取每日用量: %w", err) } byDate[date] = tokens } _ = rows.Close() for index := 0; index < days; index++ { date := dailyStart.AddDate(0, 0, index) dashboard.Days = append(dashboard.Days, collection.DailyUsageSummary{Date: date, TotalTokens: byDate[date.Format("2006-01-02")]}) } return dashboard, nil }