package dashboard import ( "context" "encoding/json" "fmt" "net/http" "net/url" "strconv" "strings" "hyapp-admin-server/internal/config" "hyapp-admin-server/internal/repository" ) type DashboardService struct { store *repository.Store cfg config.Config httpClient *http.Client } type StatisticsQuery struct { AppCode string StartMS int64 EndMS int64 SeriesStartMS int64 SeriesEndMS int64 RegionID int64 CountryID int64 } type SelfGameStatisticsQuery struct { AppCode string StartMS int64 EndMS int64 RegionID int64 CountryID int64 GameID string } func NewService(store *repository.Store, cfg config.Config) *DashboardService { return &DashboardService{ store: store, cfg: cfg, httpClient: &http.Client{Timeout: cfg.StatisticsService.RequestTimeout}, } } func (s *DashboardService) Overview() (*repository.DashboardOverview, error) { return s.store.DashboardOverview() } func (s *DashboardService) StatisticsOverview(ctx context.Context, query StatisticsQuery) (map[string]any, error) { values := url.Values{} values.Set("app_code", query.AppCode) if query.StartMS > 0 { values.Set("start_ms", strconv.FormatInt(query.StartMS, 10)) } if query.EndMS > 0 { values.Set("end_ms", strconv.FormatInt(query.EndMS, 10)) } if query.SeriesStartMS > 0 { values.Set("series_start_ms", strconv.FormatInt(query.SeriesStartMS, 10)) } if query.SeriesEndMS > 0 { values.Set("series_end_ms", strconv.FormatInt(query.SeriesEndMS, 10)) } if query.RegionID > 0 { values.Set("region_id", strconv.FormatInt(query.RegionID, 10)) } if query.CountryID > 0 { values.Set("country_id", strconv.FormatInt(query.CountryID, 10)) } return s.statisticsGET(ctx, "/internal/v1/statistics/overview", values) } func (s *DashboardService) SelfGameStatisticsOverview(ctx context.Context, query SelfGameStatisticsQuery) (map[string]any, error) { values := url.Values{} values.Set("app_code", query.AppCode) if query.StartMS > 0 { values.Set("start_ms", strconv.FormatInt(query.StartMS, 10)) } if query.EndMS > 0 { values.Set("end_ms", strconv.FormatInt(query.EndMS, 10)) } if query.RegionID > 0 { values.Set("region_id", strconv.FormatInt(query.RegionID, 10)) } if query.CountryID > 0 { values.Set("country_id", strconv.FormatInt(query.CountryID, 10)) } if gameID := strings.TrimSpace(query.GameID); gameID != "" && gameID != "all" { values.Set("game_id", gameID) } // 数据大屏只通过 statistics-service 读取自研游戏聚合指标,避免 admin 服务绕过聚合层去扫业务明细表。 return s.statisticsGET(ctx, "/internal/v1/statistics/self-games/overview", values) } func (s *DashboardService) statisticsGET(ctx context.Context, path string, values url.Values) (map[string]any, error) { baseURL := s.cfg.StatisticsService.BaseURL + path req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"?"+values.Encode(), nil) if err != nil { return nil, err } resp, err := s.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("statistics service returned status %d", resp.StatusCode) } var out map[string]any if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return nil, err } return out, nil }