package shushu import ( "encoding/json" "fmt" "time" "sandc/pkg/bhttp" "sandc/pkg/utils" "github.com/go-kratos/kratos/v2/log" ) // 定义数数client的接口 type Client interface { SyncIapData(accountId, distinctId, eventName string, ip string, properties SSProperties) error ReportError(accountId, distinctId, eventName string, ip string, properties SSProperties) error } // 定义数数client的实例 type client struct { serverUrl string appId string env string service *bhttp.BhttpService } // SyncData 定义数数上传结构体 type SyncData struct { Appid string `json:"appid,omitempty"` Debug int `json:"debug,omitempty"` Data Data `json:"data,omitempty"` } type Data struct { AccountId string `json:"#account_id,omitempty"` DistinctId string `json:"#distinct_id,omitempty"` Type string `json:"#type,omitempty"` Ip string `json:"#ip,omitempty"` Uuid string `json:"#uuid,omitempty"` Time string `json:"#time,omitempty"` EventName string `json:"#event_name,omitempty"` Properties interface{} `json:"properties,omitempty"` } // SSIapProperties 定义数数上传Properties结构体 type SSIapProperties struct { // Add your properties here GpsAdid string `json:"gps_adid"` // 用户的gaid AppToken string `json:"app_token"` // 控制面板上的 Adjust 应用识别码 EventToken string `json:"event_token"` // 控制面板上的 Adjust 事件识别码 S2S string `json:"s2s"` // s2s 参数设置为 1 AndroidId string `json:"android_id"` // 原始安卓 ID Adid string `json:"adid"` // 与设备关联的 Adjust 标识符 IpAddress string `json:"ip_address"` // 设备 IP 地址。用于将事件关联至第三方 (例如 Google) 并在回传中包含位置相关信息 (例如city 、 postal_code )。 ip_address参数仅接受 IPv4 地址。当前不支持 IPv6。 CreatedAtUnix string `json:"created_at_unix"` // 事件发生的日期和时间。 UserAgent string `json:"user_agent"` // 设备的User-Agent。必须进行 URL 编码。 Price string `json:"price"` // 客户端上报的价格 Currency string `json:"currency"` // 货币单位 FailReason string `json:"fail_reason"` // 失败原因 PayoutId string `json:"payout_id"` // 提现订单号1,实例:TS202504150316045rg110SPMDjPB MerchantReference string `json:"merchant_reference"` // 提现订单号2,实例:PGs bfd267c7823a80d97519197a30bfdf28 PaymentMethod string `json:"payment_method"` // 收款方式 PaymentType string `json:"payment_type"` // 账户形式 PaymentNumber string `json:"payment_number"` // 账户号码 IapName string `json:"iap_name"` // 商品名称 GamecoinNumber string `json:"gamecoin_number"` // 提现消耗的虚拟货币数 GamecoinType string `json:"gamecoin_type"` // 提现消耗的虚拟货币类型 SsAccountId string `json:"ss_account_id"` // 数数账号ID SsDistinctId string `json:"ss_distinct_id"` // 数数访客ID SsSuperProperties string `json:"ss_super_properties"` // 数数的公共属性和预制属性 ClientName string `json:"client_name"` // 客户端包名 // Value float64 `json:"value"` // Platform string `json:"platform"` // PlatformChannel string `json:"platform_channel"` // PlatformOs string `json:"platform_os"` // ProductId string `json:"product_id"` // OrderId string `json:"order_id"` // Currency string `json:"currency"` // Environment string `json:"environment"` // AdNetwork string `json:"ad_network"` // Campaign string `json:"campaign"` // Adgroup string `json:"adgroup"` // Creative string `json:"creative"` } // SSErrorProperties 定义数数上报的错误信息 type SSErrorProperties struct { Value float64 `json:"value"` Platform string `json:"platform"` ProductId string `json:"product_id"` OrderId string `json:"order_id"` Currency string `json:"currency"` Environment string `json:"environment"` Msg string `json:"msg"` AdNetwork string `json:"ad_network"` Campaign string `json:"campaign"` Adgroup string `json:"adgroup"` Creative string `json:"creative"` PlatformChannel string `json:"platform_channel"` PlatformOs string `json:"platform_os"` } type SSProperties map[string]interface{} // SyncRes 定义数数上传返回结构体 type SyncRes struct { Code int `json:"code"` Message string `json:"msg"` } func NewClient(serverUrl string, appId string, env string) (Client, error) { service, err := bhttp.NewBhttpService(bhttp.WithCheckStatusOk(true), bhttp.WithTimeout(30)) if err != nil { return nil, err } service.Client.SetHeader("Content-Type", "application/json") return &client{ appId: appId, serverUrl: serverUrl, env: env, service: service, }, nil } func (c *client) syncJson(accountId, distinctId, eventName string, ip string, properties interface{}) error { debug := 0 if !c.IsProd() { debug = 1 } if accountId == "" && distinctId == "" { return fmt.Errorf("accountId or distinctId is empty") } // 根据properties生成唯一的uuid propertiesStr := utils.MD5Any(properties) uuid := utils.GenerateUUID(propertiesStr) syncData := &SyncData{ Appid: c.appId, Debug: debug, Data: Data{ AccountId: accountId, DistinctId: distinctId, Type: "track", Ip: ip, Uuid: uuid, Time: time.Now().Format("2006-01-02 15:04:05.000"), EventName: eventName, Properties: properties, }, } postBody, err := json.Marshal(syncData) if err != nil { return fmt.Errorf("json.Marshal SyncIapData error: %v", err) } c.service.Client.SetBody(postBody) url := fmt.Sprintf("%s/sync_json", c.serverUrl) // utils.PrintLog("SyncIapData-url: %s", url) // utils.PrintLog("SyncIapData-postbody: %s", postBody) log.Infof("SyncIapData-url: %s", url) log.Infof("SyncIapData-postbody: %s", postBody) sendData := postBody numMax := 1 for i := 0; i < numMax; i++ { body, err := c.service.Client.DoPost(url) if err != nil { return fmt.Errorf("shushu server err: %s, reader: %s", err, sendData) } var res SyncRes err = json.Unmarshal(body, &res) if err != nil { return fmt.Errorf("shushu Unmarshal err: %s, reader: %s", res.Message, sendData) } if res.Code == 0 { log.Infof("shushu send successed: i[%d] reader: %s", i, sendData) break } else { log.Infof("shushu res error: %s, i[%d] reader: %s", res.Message, i, sendData) if i+1 >= numMax { return fmt.Errorf("shushu res err: %s, reader: %s", res.Message, sendData) } } } return nil } func setSsSuperPropertiesMap(propertiesMap SSProperties) { v, ok := propertiesMap["ss_super_properties"] if !ok || v == nil { log.Infof("setSsSuperPropertiesMap error: ss_super_properties not found in propertiesMap[%v]", propertiesMap) return } ssSuperProperties, ok := v.(string) if !ok { log.Infof("setSsSuperPropertiesMap error: ss_super_properties not string[%v]", propertiesMap) return } // 解析 ss_super_properties var ssSuperPropertiesMap SSProperties if ssSuperProperties != "" { err := json.Unmarshal([]byte(ssSuperProperties), &ssSuperPropertiesMap) if err != nil { log.Infof("setSsSuperPropertiesMap error: unmarshal err[%v], ssSuperProperties[%s]", err, ssSuperProperties) return } // 插入解析出来的新字段 for key, value := range ssSuperPropertiesMap { if _, ok := propertiesMap[key]; !ok && key != "#zone_offset" { propertiesMap[key] = value } } } else { log.Infof("setSsSuperPropertiesMap error: ss_super_properties is empty[%v]", propertiesMap) } } func (c *client) ReportError(accountId, distinctId, eventName string, ip string, properties SSProperties) error { setSsSuperPropertiesMap(properties) return c.syncJson(accountId, distinctId, eventName, ip, properties) } func (c *client) SyncIapData(accountId, distinctId, eventName string, ip string, properties SSProperties) error { setSsSuperPropertiesMap(properties) return c.syncJson(accountId, distinctId, eventName, ip, properties) } func (c *client) IsProd() bool { return c.env == "prod" }