topfans/backend/pkg/statistic/client.go
zerosaturation 3d3f2260f3 feat(statistic): T13-T16 Gateway routes + 5-service business integration (7/7 event types)
Gateway:
- 7 routes /api/v1/dashboard/* with JWT auth (middleware.AuthMiddleware)
- statistic_controller.go: 7 methods, response format {code:200, data:resp}
- gateway/main.go: StatisticServiceClient wired
- gateway/config: StatisticServiceURL (DUBBO_STATISTIC_SERVICE_URL, default tri://127.0.0.1:20009)

pkg/statistic SDK:
- fire-and-forget TrackEvent + BatchTrackEvent
- Init(client) + Get() global singleton pattern

Business-side integration (7/7 event types):
- socialService.LikeAsset → asset.like
- galleryService.PlaceAsset → exhibition.start
- galleryService.RemoveFromSlot → exhibition.end (with duration_ms)
- taskService.OnExhibitionCompleted → exhibition.revenue
- assetService.CreateMintOrder → asset.mint
- assetService.logLevelChange → asset.level_up
- userService.UpdateCrystalBalance → crystal.change (wrapper fn added)

Cache warmup:
- main.go: 7 RPCs x 5 sample starIDs at startup (15s delay)
- prevents cold-start DB thundering herd

Existing service modifications:
- galleryService/exhibition_service.go
- taskService/revenue_service.go
- assetService/{mint_service,asset_level_service}.go
- userService/user_service.go
- socialService/asset_like_service.go

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:20:53 +08:00

92 lines
2.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package statistic
import (
"context"
"sync"
"time"
dubboclient "dubbo.apache.org/dubbo-go/v3/client"
"github.com/google/uuid"
pb "github.com/topfans/backend/pkg/proto/event"
statisticPb "github.com/topfans/backend/pkg/proto/statistic"
)
var (
instance *Client
once sync.Once
)
// Client 业务侧统一 SDKfire-and-forget 调用 statisticService
type Client struct {
service statisticPb.StatisticService
}
// Init 用 Dubbo client 初始化 SDK在业务服务 main.go 启动时调用一次)
func Init(dubboClient *dubboclient.Client) error {
var err error
once.Do(func() {
svc, e := statisticPb.NewStatisticService(dubboClient)
if e != nil {
err = e
return
}
instance = &Client{service: svc}
})
return err
}
// Get 返回全局 SDK 实例Init 之后才能用)
func Get() *Client { return instance }
// TrackEvent fire-and-forget 上报单个事件
// - 自动填充 event_id若为空和 occurred_at
// - 不阻塞业务方(独立 goroutine + background context
func (c *Client) TrackEvent(ctx context.Context, e *pb.Event) {
if c == nil || c.service == nil {
return
}
if e.EventId == "" {
e.EventId = uuid.New().String()
}
if e.OccurredAt == 0 {
e.OccurredAt = time.Now().UnixMilli()
}
bgCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
go func() {
defer cancel()
_, _ = c.service.TrackEvent(bgCtx, e)
}()
}
// SetMockForTest 测试钩子:注入 mock 客户端
var mockForTest Capturer
// Capturer 测试用 capture 接口
type Capturer interface {
Capture(e *pb.Event)
}
// SetMockForTest 注入测试 mock
func SetMockForTest(c Capturer) { mockForTest = c }
// ResetMockForTest 重置 mock
func ResetMockForTest() { mockForTest = nil }
// TrackEventSync 同步版本(测试用)
func (c *Client) TrackEventSync(ctx context.Context, e *pb.Event) (*statisticPb.TrackEventResponse, error) {
if e.EventId == "" {
e.EventId = uuid.New().String()
}
if e.OccurredAt == 0 {
e.OccurredAt = time.Now().UnixMilli()
}
if mockForTest != nil {
mockForTest.Capture(e)
return &statisticPb.TrackEventResponse{Accepted: 1, Rejected: 0}, nil
}
if c == nil || c.service == nil {
return &statisticPb.TrackEventResponse{Accepted: 0, Rejected: 1}, nil
}
return c.service.TrackEvent(ctx, e)
}