2026-07-03 14:43:43 +08:00

144 lines
4.8 KiB
Go

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
}