package client import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) type AppTrackingEvent struct { AppCode string `json:"app_code"` EventID string `json:"event_id"` EventName string `json:"event_name"` EventType string `json:"event_type"` Screen string `json:"screen"` TargetType string `json:"target_type"` TargetID string `json:"target_id"` UserID int64 `json:"user_id"` DeviceID string `json:"device_id"` SessionID string `json:"session_id"` Platform string `json:"platform"` AppVersion string `json:"app_version"` Language string `json:"language"` Timezone string `json:"timezone"` CountryID int64 `json:"country_id"` RegionID int64 `json:"region_id"` DurationMS int64 `json:"duration_ms"` Success bool `json:"success"` ErrorCode string `json:"error_code"` Properties json.RawMessage `json:"properties,omitempty"` OccurredAtMS int64 `json:"occurred_at_ms"` } type AppTrackingReportResult struct { Accepted bool `json:"accepted"` Received int `json:"received"` Stored int `json:"stored"` Duplicated int `json:"duplicated"` ServerTimeMS int64 `json:"server_time_ms"` } type SelfGameH5Event struct { AppCode string `json:"app_code"` EventID string `json:"event_id"` EventName string `json:"event_name"` GameID string `json:"game_id"` Page string `json:"page"` SessionID string `json:"session_id"` MatchID string `json:"match_id"` UserID int64 `json:"user_id"` CountryID int64 `json:"country_id"` RegionID int64 `json:"region_id"` Language string `json:"language"` ClientVersion string `json:"client_version"` H5Version string `json:"h5_version"` EntrySource string `json:"entry_source"` DurationMS int64 `json:"duration_ms"` Success bool `json:"success"` ErrorCode string `json:"error_code"` OccurredAtMS int64 `json:"occurred_at_ms"` } type StatisticsClient interface { ReportSelfGameH5Event(ctx context.Context, event SelfGameH5Event) error ReportAppTrackingEvents(ctx context.Context, events []AppTrackingEvent) (AppTrackingReportResult, error) } type HTTPStatisticsClient struct { baseURL string httpClient *http.Client } func NewHTTPStatisticsClient(baseURL string, timeout time.Duration) *HTTPStatisticsClient { baseURL = strings.TrimRight(strings.TrimSpace(baseURL), "/") if baseURL == "" { return nil } if timeout <= 0 { timeout = 2 * time.Second } return &HTTPStatisticsClient{ baseURL: baseURL, httpClient: &http.Client{Timeout: timeout}, } } func (c *HTTPStatisticsClient) ReportSelfGameH5Event(ctx context.Context, event SelfGameH5Event) error { if c == nil || c.httpClient == nil || c.baseURL == "" { return fmt.Errorf("statistics client is not configured") } body, err := json.Marshal(event) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/internal/v1/statistics/self-game/events", bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 300 { return nil } payload, _ := io.ReadAll(io.LimitReader(resp.Body, 512)) return fmt.Errorf("statistics report failed: status=%d body=%s", resp.StatusCode, strings.TrimSpace(string(payload))) } func (c *HTTPStatisticsClient) ReportAppTrackingEvents(ctx context.Context, events []AppTrackingEvent) (AppTrackingReportResult, error) { if c == nil || c.httpClient == nil || c.baseURL == "" { return AppTrackingReportResult{}, fmt.Errorf("statistics client is not configured") } body, err := json.Marshal(map[string]any{"events": events}) if err != nil { return AppTrackingReportResult{}, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/internal/v1/statistics/app/events", bytes.NewReader(body)) if err != nil { return AppTrackingReportResult{}, err } req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return AppTrackingReportResult{}, err } defer resp.Body.Close() payload, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return AppTrackingReportResult{}, fmt.Errorf("statistics app event report failed: status=%d body=%s", resp.StatusCode, strings.TrimSpace(string(payload))) } var result AppTrackingReportResult if err := json.Unmarshal(payload, &result); err != nil { return AppTrackingReportResult{}, err } return result, nil }