241 lines
8.4 KiB
Go
241 lines
8.4 KiB
Go
|
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"
|
|||
|
}
|