package service import ( "bytes" "context" "designs/global" "encoding/json" "go.mongodb.org/mongo-driver/v2/bson" "io" "net/http" "net/url" "time" ) type EReportType string type EReportResCode = int const ( ERTypeGameAddiction EReportType = "game_addiction" //关键行为 ReportOk EReportResCode = 0 //上报成功 ReportIOErr EReportResCode = -50 Reported EReportResCode = -100 //已经上报过 ReportNoNeed EReportResCode = -200 //不需要上报,一般为非广告来源用户 ReportParseErr EReportResCode = -300 ReportRequestErr EReportResCode = -300 ) // 需要核对bson的值是否与数据库中的字段一致 type AdDataDetail struct { Pid string `json:"pid"` Aid string `json:"aid"` Cid string `json:"cid"` ReportUrl string `json:"reportUrl"` Reported bool `json:"reported"` Duration int64 `json:"duration"` AdReqCount uint8 `json:"adReqCount"` AdEposedcount uint8 `json:"adEposedcount"` CreateTime int `json:"createTime"` } type UserBehavior struct { Id string `bson:"_id,omitempty"` Gid string `bson:"gid"` Pf string `bson:"pf"` OpenId string `bson:"openId"` AdData *AdDataDetail `bson:"adData"` AdFromCount int `bson:"adFromCount"` TotalDuration int `bson:"totalDuration"` TotalAdReqCount int `bson:"totalAdReqCount"` TotalAdEposedCount int `bson:"totalAdEposedCount"` } // ReportResult 执行巨量引擎上报函数的返回结果的数据结构 type ReportResult struct { Code int Message string } // ReportResponse 执行巨量引擎上报时请求结果的数据结构 type ReportResponse struct { Code int `json:"code"` Message string `json:"message"` } // ReportData 巨量引擎上报时提交的数据结构 type ReportData struct { EventType string `json:"event_type"` Context struct { Ad struct { Callback string `json:"callback"` } `json:"ad"` } `json:"context"` Timestamp int64 `json:"timestamp"` } func createReportData(evtType EReportType, clickId string) *ReportData { dat := &ReportData{} dat.EventType = string(evtType) dat.Context.Ad.Callback = clickId dat.Timestamp = time.Now().UnixMilli() return dat } func OceanReportAction(behavior string) *ReportResult { collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior") // filter := bson.M{"_id": behavior} result := make(map[string]interface{}) collection.FindOne(context.Background(), filter).Decode(&result) //转换成合适的格式 var user UserBehavior resultString, _ := json.Marshal(result) json.Unmarshal(resultString, &user) if user.AdData.AdReqCount < 1 { //没有看过广告,无需上传 return &ReportResult{Code: ReportNoNeed, Message: "没有看过广告,无需上报"} } if user.AdData == nil { //非广告来源的用户,无需上传 return &ReportResult{Code: ReportNoNeed, Message: "非广告用户,无需上报"} } if user.AdData.Reported { //已经上传过了 return &ReportResult{Code: Reported, Message: "已经上报过"} } //后续通过广告的不同优化目标判断不同上报行为的逻辑判断模块,暂时仅上传关键行为数据 //if !specialReportType("Action", user.AdData.Pid, user.AdData.Aid) { // return false, nil //} //事件上报详见: //https://event-manager.oceanengine.com/docs/8650/app_api_docs#%E8%AF%B7%E6%B1%82%E6%96%B9%E6%B3%95 //https://event-manager.oceanengine.com/docs/8650/api_docs //事件取值详见https://event-manager.oceanengine.com/docs/8650/all_events reportDat := createReportData(ERTypeGameAddiction, user.AdData.Cid) //payload, err := json.Marshal(reportDat) //if err != nil { // return &ReportResult{Code: ReportParseErr, Message: err.Error()} //} content, err := CurlPost("https://analytics.oceanengine.com/api/v2/conversion", reportDat, nil) if err != nil { return &ReportResult{Code: ReportRequestErr, Message: err.Error()} } resp := &ReportResponse{} if str2oErr := json.Unmarshal([]byte(content), resp); str2oErr != nil { return &ReportResult{Code: ReportParseErr, Message: str2oErr.Error()} } if resp.Code != 0 { return &ReportResult{Code: resp.Code, Message: resp.Message} } //数据上传后更新用户数据 //更新到MongoDB 中 user.AdData.Reported = true update := bson.M{ "$set": struct { Gid string `bson:"gid"` Pf string `bson:"pf"` OpenId string `bson:"openId"` AdData interface{} `bson:"adData"` AdFromCount int `bson:"adFromCount"` TotalDuration int `bson:"totalDuration"` TotalAdReqCount int `bson:"totalAdReqCount"` TotalAdEposedCount int `bson:"totalAdEposedCount"` }{ Gid: user.Gid, Pf: user.Pf, OpenId: user.OpenId, AdData: user.AdData, AdFromCount: user.AdFromCount, TotalDuration: user.TotalDuration, TotalAdReqCount: user.TotalAdReqCount, TotalAdEposedCount: user.TotalAdEposedCount, }, } _, err = collection.UpdateOne(context.Background(), filter, update) if err != nil { return &ReportResult{Code: resp.Code, Message: err.Error()} } return &ReportResult{Code: ReportOk, Message: resp.Message} } func CurlPost(requestUrl string, requestBody interface{}, headerMap map[string]string) (string, error) { //转换json jsonBytes, err := json.Marshal(requestBody) if err != nil { return "", err } //创建请求 req, err := http.NewRequest("POST", requestUrl, bytes.NewReader(jsonBytes)) if err != nil { return "", err } //设置请求头 req.Header.Set("Content-Type", "application/json;charset=UTF-8") for k, v := range headerMap { //req.Header.Set("Accept-Encoding", "gzip, deflate, br") req.Header.Set(k, v) } //发送请求 clt := http.Client{Timeout: 30 * time.Second} res, err := clt.Do(req) if err != nil { return "", err } //获取结果 body, err := io.ReadAll(res.Body) data := string(body) return data, err } // specialReportType 是否时指定上传的类型 func specialReportType(reportType EReportType, pid string, aid string) bool { //通过pid和aid查找广告,判断其是否 return true } func CurlGet(url string, values url.Values, header http.Header) (string, error) { req, reqErr := http.NewRequest(http.MethodGet, url, nil) if reqErr != nil { return "", reqErr } if header != nil { req.Header = header } if values != nil { req.URL.RawQuery = values.Encode() } //fmt.Println(req.URL.Host, req.URL.RawQuery) resp, resErr := http.DefaultClient.Do(req) if resErr != nil { return "", resErr } defer func() { _ = resp.Body.Close() }() content, readErr := io.ReadAll(resp.Body) if readErr != nil { return "", readErr } //if decodeErr := json.Unmarshal(content, result); decodeErr != nil { // return "", decodeErr //} return string(content), nil } type HttpResult struct { Code int `bson:"code"` Msg string `bson:"msg"` }