From 0a863dd112f0b8822ab3db0c987267451ee70f04 Mon Sep 17 00:00:00 2001 From: zhuxianglong <56494565@qq.com> Date: Wed, 22 Jan 2025 17:00:59 +0800 Subject: [PATCH] no message --- point/README.md | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++ point/point.go | 73 ++++++++++++---- test/point_test.go | 17 +++- utils/ahttp/ahttp.go | 11 ++- 4 files changed, 312 insertions(+), 18 deletions(-) create mode 100644 point/README.md diff --git a/point/README.md b/point/README.md new file mode 100644 index 0000000..429d285 --- /dev/null +++ b/point/README.md @@ -0,0 +1,229 @@ +# point - TapDB事件上报SDK / TapDB Event Reporting SDK + +[中文](#中文) | [English](#english) + +--- + +## 中文 + +### 📖 简介 + +`point` 是专为TapDB设计的线程安全事件上报SDK,提供简洁的API接口和并发安全机制。采用单例模式设计,内置HTTP连接池和自动重试策略,适用于高并发场景下的数据上报。 + +GitHub地址: [gitea.weitiangame.com/sdk/wt-game/point](https://gitea.weitiangame.com/sdk/wt-game/point) + +### 📦 安装 + +```bash +go get gitea.weitiangame.com/sdk/wt-game/point +``` + +### 🚀 快速开始 + +#### 初始化上报器 +```go +package main + +import ( + "gitea.weitiangame.com/sdk/wt-game/point" + "go.uber.org/zap" +) + +func main() { + logger, _ := zap.NewProduction() + + // 创建单例上报实例(自动注入client_id) + reporter := point.New("your_client_id", logger) +} +``` + +#### 基础事件上报 +```go +func main() { + reporter := point.New("game_123", zap.NewExample()) + + eventData := map[string]interface{}{ + "event": "player_login", + "distinct_id": "user_888", + "properties": map[string]interface{}{ + "level": 5, + "region": "cn", + }, + } + + if resp, err := reporter.Event(eventData); err != nil { + logger.Error("上报失败", zap.Error(err)) + } else { + logger.Info("上报成功", + zap.Int("status", resp.StatusCode()), + zap.String("trace_id", resp.Header().Get("X-Trace-Id"))) + } +} +``` + +### 🔧 高级用法 + +#### 启用调试模式 +```go +func main() { + // 初始化时启用调试和CURL输出 + reporter := point.New("client_123", logger). + Debug(). // 显示请求详情 + Curl() // 生成CURL命令 + + reporter.Event(map[string]interface{}{ + "event": "ad_click", + "properties": map[string]interface{}{ + "ad_id": "banner_001", + }, + }) +} +``` + +#### 自定义HTTP客户端 +```go +func main() { + customClient := ahttp.New(&ahttp.Config{ + Timeout: 10 * time.Second, + Retries: 3, + }).Client() + + point.New("game_123", logger). + SetClient(customClient). // 注入自定义客户端 + Event(eventData) +} +``` + +### ✨ 核心特性 + +| 特性 | 描述 | +|---------------------|--------------------------------------------------------------------| +| **单例模式** | 全局唯一实例,避免重复创建资源 | +| **线程安全** | 基于互斥锁保护配置变更,内置线程安全HTTP客户端 | +| **调试支持** | 支持请求详情输出和CURL命令生成 | +| **自动重试** | 依赖底层HTTP客户端的重试策略(默认3次) | +| **数据隔离** | 自动复制传入数据,防止并发修改 | + +### ⚠️ 注意事项 +1. `New()` 方法为单例模式,首次调用后配置不可变更 +2. `Debug()` 和 `Curl()` 建议仅在开发环境使用 +3. 事件数据需包含 `event` 字段,建议包含 `distinct_id` 字段 +4. HTTP响应状态码非2xx时需要检查请求参数 +5. 上报数据量较大时建议批量处理 + +### 🤝 参与贡献 +[贡献指南](CONTRIBUTING.md) | [提交Issue](https://gitea.weitiangame.com/sdk/wt-game/point/issues) + +--- + +## English + +### 📖 Introduction + +`point` is a thread-safe event reporting SDK designed for TapDB, featuring a clean API and concurrency-safe mechanisms. Built with singleton pattern and HTTP connection pooling, ideal for high-concurrency data reporting scenarios. + +GitHub URL: [gitea.weitiangame.com/sdk/wt-game/point](https://gitea.weitiangame.com/sdk/wt-game/point) + +### 📦 Installation + +```bash +go get gitea.weitiangame.com/sdk/wt-game/point +``` + +### 🚀 Quick Start + +#### Initialize Reporter +```go +package main + +import ( + "gitea.weitiangame.com/sdk/wt-game/point" + "go.uber.org/zap" +) + +func main() { + logger, _ := zap.NewProduction() + + // Create singleton instance (auto-injects client_id) + reporter := point.New("your_client_id", logger) +} +``` + +#### Basic Event Reporting +```go +func main() { + reporter := point.New("game_123", zap.NewExample()) + + eventData := map[string]interface{}{ + "event": "player_login", + "distinct_id": "user_888", + "properties": map[string]interface{}{ + "level": 5, + "region": "cn", + }, + } + + if resp, err := reporter.Event(eventData); err != nil { + logger.Error("Report failed", zap.Error(err)) + } else { + logger.Info("Report succeeded", + zap.Int("status", resp.StatusCode()), + zap.String("trace_id", resp.Header().Get("X-Trace-Id"))) + } +} +``` + +### 🔧 Advanced Usage + +#### Enable Debug Mode +```go +func main() { + // Enable debug features during initialization + reporter := point.New("client_123", logger). + Debug(). // Show request details + Curl() // Generate CURL commands + + reporter.Event(map[string]interface{}{ + "event": "ad_click", + "properties": map[string]interface{}{ + "ad_id": "banner_001", + }, + }) +} +``` + +#### Custom HTTP Client +```go +func main() { + customClient := ahttp.New(&ahttp.Config{ + Timeout: 10 * time.Second, + Retries: 3, + }).Client() + + point.New("game_123", logger). + SetClient(customClient). // Inject custom client + Event(eventData) +} +``` + +### ✨ Key Features + +| Feature | Description | +|---------------------|-----------------------------------------------------------------| +| **Singleton** | Global single instance prevents resource duplication | +| **Thread-Safe** | Mutex-protected configuration with built-in safe HTTP client | +| **Debug Support** | Request diagnostics and CURL command generation | +| **Auto Retry** | Built-in retry policy (default 3 attempts) | +| **Data Isolation** | Automatic data copying prevents concurrent modification | + +### ⚠️ Important Notes +1. `New()` uses singleton pattern, configurations are immutable after first call +2. Use `Debug()` and `Curl()` only in development environments +3. Event data must contain `event` field, `distinct_id` is recommended +4. Check request parameters for non-2xx HTTP responses +5. Implement batch processing for large-scale data reporting + +### 🤝 Contributing +[Contribution Guide](CONTRIBUTING.md) | [Open an Issue](https://gitea.weitiangame.com/sdk/wt-game/point/issues) + +[⬆ Back to Top](#中文) \ No newline at end of file diff --git a/point/point.go b/point/point.go index e9b2f20..049dd3b 100644 --- a/point/point.go +++ b/point/point.go @@ -12,35 +12,78 @@ var ( once sync.Once ) +// Point 是用于事件上报的核心结构体 +// Point is the core structure for event reporting type Point struct { clientID string logger *zap.Logger - ahttp *resty.Request + client *resty.Client // 线程安全的HTTP客户端 / Thread-safe HTTP client + debug bool + curl bool + mu sync.Mutex // 保护并发访问配置字段 / Mutex to protect concurrent access to configuration fields } -// New returns the singleton instance of Point. +// New 返回Point的单例实例 +// New returns the singleton instance of Point +// 注意:首次调用后的参数变更将不会生效(单例模式特性) +// Note: Parameter changes after the first call will not take effect (singleton pattern characteristic) func New(clientID string, logger *zap.Logger) *Point { once.Do(func() { + // 初始化线程安全的HTTP客户端 + // Initialize thread-safe HTTP client + baseClient := ahttp.New(nil).SetLog(logger).Client() + instance = &Point{ clientID: clientID, logger: logger, + client: baseClient, } - - if instance.logger == nil { - var err error - instance.logger, err = zap.NewProduction() - if err != nil { - panic(err) - } - } - - instance.ahttp = ahttp.New(nil).SetLog(instance.logger).Client() }) - return instance } -// Event sends an event to the specified URL. +// Debug 启用调试模式(非线程安全,建议初始化阶段调用) +// Debug enables debug mode (not thread-safe, recommended to call during initialization) +func (p *Point) Debug() *Point { + p.mu.Lock() + defer p.mu.Unlock() + p.debug = true + return p +} + +// Curl 启用CURL命令生成(非线程安全,建议初始化阶段调用) +// Curl enables CURL command generation (not thread-safe, recommended to call during initialization) +func (p *Point) Curl() *Point { + p.mu.Lock() + defer p.mu.Unlock() + p.curl = true + return p +} + +// Event 发送事件到指定端点 +// Event sends an event to the specified endpoint +// 参数data会被复制以避免原始数据修改(并发安全设计) +// The data parameter is copied to avoid original data modification (concurrency-safe design) func (p *Point) Event(data map[string]interface{}) (*resty.Response, error) { - return p.ahttp.SetBody(data).Post("https://e.tapdb.net/v2/event") + data["client_id"] = p.clientID + // 获取当前配置的调试和CURL设置 + // Get current debug and curl configuration + p.mu.Lock() + debugEnabled := p.debug + curlEnabled := p.curl + p.mu.Unlock() + + // 创建新的请求实例并配置参数 + // Create new request instance and configure parameters + request := p.client.R(). + SetBody(data). + SetDebug(debugEnabled) + + if curlEnabled { + request.EnableTrace() // 启用跟踪以生成CURL命令 / Enable trace to generate CURL command + } + + // 发送请求并返回结果 + // Send request and return results + return request.Post("https://e.tapdb.net/v2/event") } diff --git a/test/point_test.go b/test/point_test.go index 194c557..8753220 100644 --- a/test/point_test.go +++ b/test/point_test.go @@ -3,15 +3,28 @@ package test import ( "fmt" "gitea.weitiangame.com/sdk/wt-game/point" + "go.uber.org/zap" "testing" ) -var pointSdk = point.New("your-client-id", nil) +var logger, _ = zap.NewProduction() +var pointSdk = point.New("your-client-id", logger).Debug().Curl() // TestOrderSign 测试订单签名 func TestPoint(t *testing.T) { event, err := pointSdk.Event(map[string]interface{}{ - "test": "test", + "name": "charge", // 事件名,固定为 charge + "user_id": "your-user-id", // 必需。用户 ID。必须和 SDK 的 setUser 接口传递的 userId 一样,并且该用户已经通过 SDK 接口进行过推送 + "type": "track", // 必需。数据类型,请确保传入的值为 track + "properties": map[string]interface{}{ + "ip": "8.8.8.8", // 可选。充值用户的 IP + "order_id": "100000", // 可选。长度大于 0 并小于等于 256。订单 ID。 + "amount": 100, // 必需。大于 0 并小于等于 100000000000。充值金额。单位分,即无论什么币种,都需要乘以 100 + "virtual_currency_amount": 100, //获赠虚拟币数量,必传,可为 0 + "currency_type": "CNY", // 可选。货币类型。国际通行三字母表示法,为空时默认 CNY。参考:人民币 CNY,美元 USD;欧元 EUR + "product": "item1", // 可选。长度大于 0 并小于等于 256。商品名称 + "payment": "alipay", // 可选。长度大于 0 并小于等于 256。充值渠道 + }, }) fmt.Println(event) diff --git a/utils/ahttp/ahttp.go b/utils/ahttp/ahttp.go index 3414215..842adf0 100644 --- a/utils/ahttp/ahttp.go +++ b/utils/ahttp/ahttp.go @@ -115,6 +115,9 @@ func newTransport(config *Config) *http.Transport { // SetLog 设置日志记录器 / SetLog sets the logger func (h *HttpClient) SetLog(logger *zap.Logger) *HttpClient { + if logger == nil { + return h + } h.logger = logger h.httpClient.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error { logger.Info("Request", @@ -161,7 +164,13 @@ func (h *HttpClient) SetLog(logger *zap.Logger) *HttpClient { } // Client 返回 Resty 客户端的请求实例 / Returns a request instance of the Resty client -func (h *HttpClient) Client() *resty.Request { +func (h *HttpClient) Client() *resty.Client { + h.httpClient.SetHeader("User-Agent", "ahttp") + return h.httpClient +} + +// Client 返回 Resty 客户端的请求实例 / Returns a request instance of the Resty client +func (h *HttpClient) Request() *resty.Request { h.httpClient.SetHeader("User-Agent", "ahttp") return h.httpClient.R() }