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"
|
||
}
|