wucan преди 6 месеца
родител
ревизия
dbde2d9914
променени са 15 файла, в които са добавени 844 реда и са изтрити 226 реда
  1. 24 0
      bootstrap/cron.go
  2. 8 1
      bootstrap/mongodb.go
  3. 3 0
      config/app.go
  4. 198 184
      controller/v1/behavior.go
  5. 70 39
      controller/v1/user.go
  6. 220 0
      crons/ecpm.go
  7. 2 1
      global/app.go
  8. 1 0
      go.mod
  9. 2 0
      go.sum
  10. 2 0
      main.go
  11. 58 0
      model/behavior.go
  12. 2 0
      model/user.go
  13. 1 1
      route/api.go
  14. 236 0
      service/oceanEngine.go
  15. 17 0
      utils/map.go

+ 24 - 0
bootstrap/cron.go

@@ -0,0 +1,24 @@
+package bootstrap
+
+import (
+	"designs/crons"
+	"designs/global"
+	"fmt"
+	"github.com/robfig/cron/v3"
+	"time"
+)
+
+func InitializeCron() {
+	global.App.Cron = cron.New(cron.WithSeconds())
+
+	go func() {
+		global.App.Cron.AddFunc("0 0 2 * * *", func() {
+			fmt.Println(time.Now())
+		})
+
+		global.App.Cron.AddFunc("0 0 * * * *", crons.SetEcpm)
+		global.App.Cron.Start()
+		defer global.App.Cron.Stop()
+		select {}
+	}()
+}

+ 8 - 1
bootstrap/mongodb.go

@@ -2,6 +2,7 @@ package bootstrap
 
 import (
 	"context"
+	"designs/config"
 	"designs/global"
 	"go.mongodb.org/mongo-driver/v2/mongo"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
@@ -10,7 +11,13 @@ import (
 import mongoOption "go.mongodb.org/mongo-driver/v2/mongo/options"
 
 func InitializeMongo() *mongo.Client {
-	client, _ := mongo.Connect(mongoOption.Client().ApplyURI("mongodb://admin:admin@localhost:27017"))
+	var url string
+	if config.Get("app.local") == "local" {
+		url = "mongodb://localhost:27017"
+	} else {
+		url = "mongodb://admin:admin@localhost:27017"
+	}
+	client, _ := mongo.Connect(mongoOption.Client().ApplyURI(url))
 
 	err := client.Ping(context.Background(), readpref.Primary())
 	if err != nil {

+ 3 - 0
config/app.go

@@ -37,6 +37,9 @@ func App() *ConfigNode {
 		"admin_user":   env("ADMIN_USER", "chunhao"),    //默认管理员
 		"admin_secret": env("ADMIN_SECRET", "123456"),   //默认管理密码
 		"user_total":   env("USER_TOTAL", "user_total"), //所有用户的集合
+
+		"check_user_active": env("CHECK_USER_ACTIVE", "http://127.0.0.1:8001/user/checkUserActive"), //curl 接口
+		"local":             env("LOCAL", ""),
 	}
 }
 

+ 198 - 184
controller/v1/behavior.go

@@ -2,133 +2,111 @@ package v1
 
 import (
 	"context"
-	"designs/app/common/request"
 	"designs/app/common/response"
+	"designs/config"
 	"designs/global"
+	"designs/model"
 	"designs/service"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"github.com/gin-gonic/gin"
 	"go.mongodb.org/mongo-driver/v2/bson"
 	"strconv"
 	"time"
 )
 
-type AdData struct {
-	Pid string `json:"pid" bson:"pid"`
-	Aid string `json:"aid" bson:"aid"`
-	Cid string `json:"cid" bson:"cid"`
-	//ReportUrl     string `json:"reportUrl" bson:"reportUrl"`
-	Reported      bool  `json:"reported" bson:"reported"`
-	Duration      int64 `json:"duration" bson:"duration"`
-	AdReqCount    uint8 `json:"adReqCount" bson:"adReqCount"`
-	AdEposedcount uint8 `json:"adEposedcount" bson:"adEposedcount"`
-	CreateTime    int   `json:"createTime" bson:"createTime"`
-}
-
-type UserBehavior struct {
-	Id                 string      `bson:"_id,omitempty"`
-	Gid                string      `bson:"gid" json:"gid"`
-	Pf                 string      `bson:"pf" json:"pf"`
-	OpenId             string      `bson:"openId" json:"openId"`
-	AdData             interface{} `bson:"adData" json:"adData"`
-	AdFromCount        int         `bson:"adFromCount" json:"adFromCount"`
-	TotalDuration      int         `bson:"totalDuration" json:"totalDuration"`
-	TotalAdReqCount    int         `bson:"totalAdReqCount" json:"totalAdReqCount"`
-	TotalAdEposedCount int         `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
-}
-
-func ReceiveUserBehavior(c *gin.Context) {
-	form := request.Check(c, &struct {
-		Gid                string      `form:"gid" json:"gid" binding:"required"`
-		Pf                 string      `form:"pf" json:"pf" binding:"required"`
-		OpenId             string      `form:"openId" json:"openId" binding:"required"`
-		AdData             interface{} `form:"adData" json:"adData" binding:""`
-		AdFromCount        int         `form:"adFromCount" json:"adFromCount" binding:""`
-		TotalDuration      int         `form:"totalDuration" json:"totalDuration" binding:""`
-		TotalAdReqCount    int         `form:"totalAdReqCount" json:"totalAdReqCount" binding:""`
-		TotalAdEposedCount int         `form:"totalAdEposedCount" json:"totalAdEposedCount" binding:""`
-	}{})
-
-	//if form.AdData != nil {
-	//	var AdData map[string]interface{}
-	//}
-	behavior := UserBehavior{
-		Id:                 form.Gid + "|" + form.Pf + "|" + form.OpenId,
-		Gid:                form.Gid,
-		Pf:                 form.Pf,
-		OpenId:             form.OpenId,
-		AdData:             form.AdData,
-		AdFromCount:        form.AdFromCount,
-		TotalDuration:      form.TotalDuration,
-		TotalAdReqCount:    form.TotalAdReqCount,
-		TotalAdEposedCount: form.TotalAdEposedCount,
-	}
-
-	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
-
-	filter := bson.M{"_id": behavior.Id}
-	result := make(map[string]interface{})
-	err := collection.FindOne(context.Background(), filter).Decode(&result)
-
-	if len(result) == 0 {
-		//新增
-		_, err = collection.InsertOne(context.Background(), behavior)
-		if err != nil {
-			response.Fail(c, 1003, "写入数据失败"+err.Error())
-			return
-		}
-	} else {
-		var newAdData AdData
-		adData, _ := json.Marshal(form.AdData)
-		json.Unmarshal(adData, &newAdData)
-
-		var oldAdData AdData
-		oldAdDatas, _ := json.Marshal(result["adData"])
-		json.Unmarshal(oldAdDatas, &oldAdData)
-
-		newAdData.Reported = oldAdData.Reported
-
-		//更新到MongoDB 中
-		update := bson.M{
-			"$set": struct {
-				Gid                string      `bson:"gid" json:"gid"`
-				Pf                 string      `bson:"pf" json:"pf"`
-				OpenId             string      `bson:"openId" json:"openId"`
-				AdData             interface{} `bson:"adData" json:"adData"`
-				AdFromCount        int         `bson:"adFromCount" json:"adFromCount"`
-				TotalDuration      int         `bson:"totalDuration" json:"totalDuration"`
-				TotalAdReqCount    int         `bson:"totalAdReqCount" json:"totalAdReqCount"`
-				TotalAdEposedCount int         `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
-			}{
-				Gid:                behavior.Gid,
-				Pf:                 behavior.Pf,
-				OpenId:             behavior.OpenId,
-				AdData:             newAdData,
-				AdFromCount:        behavior.AdFromCount,
-				TotalDuration:      behavior.TotalDuration,
-				TotalAdReqCount:    behavior.TotalAdReqCount,
-				TotalAdEposedCount: behavior.TotalAdEposedCount,
-			},
-		}
-		_, err = collection.UpdateOne(context.Background(), filter, update)
-		if err != nil {
-			response.Fail(c, 1003, "写入数据失败"+err.Error())
-			return
-		}
-	}
-
-	//如果是从广告获取进来的用户,则需要检测是否上传
-	if form.AdData != nil {
-		res := service.OceanReportAction(behavior.Id)
-		if res.Code != 0 {
-			//上传出现了问题,或者并没有上传
-			global.App.Log.Error(res.Code, res.Message)
-		}
-	}
-
-	response.Success(c, gin.H{})
-}
+//func ReceiveUserBehavior(c *gin.Context) {
+//	form := request.Check(c, &struct {
+//		Gid                string      `form:"gid" json:"gid" binding:"required"`
+//		Pf                 string      `form:"pf" json:"pf" binding:"required"`
+//		OpenId             string      `form:"openId" json:"openId" binding:"required"`
+//		AdData             interface{} `form:"adData" json:"adData" binding:""`
+//		AdFromCount        int         `form:"adFromCount" json:"adFromCount" binding:""`
+//		TotalDuration      int         `form:"totalDuration" json:"totalDuration" binding:""`
+//		TotalAdReqCount    int         `form:"totalAdReqCount" json:"totalAdReqCount" binding:""`
+//		TotalAdEposedCount int         `form:"totalAdEposedCount" json:"totalAdEposedCount" binding:""`
+//	}{})
+//
+//	//if form.AdData != nil {
+//	//	var AdData map[string]interface{}
+//	//}
+//	behavior := UserBehavior{
+//		Id:                 form.Gid + "|" + form.Pf + "|" + form.OpenId,
+//		Gid:                form.Gid,
+//		Pf:                 form.Pf,
+//		OpenId:             form.OpenId,
+//		AdData:             form.AdData,
+//		AdFromCount:        form.AdFromCount,
+//		TotalDuration:      form.TotalDuration,
+//		TotalAdReqCount:    form.TotalAdReqCount,
+//		TotalAdEposedCount: form.TotalAdEposedCount,
+//	}
+//
+//	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
+//
+//	filter := bson.M{"_id": behavior.Id}
+//	result := make(map[string]interface{})
+//	err := collection.FindOne(context.Background(), filter).Decode(&result)
+//
+//	if len(result) == 0 {
+//		//新增
+//		_, err = collection.InsertOne(context.Background(), behavior)
+//		if err != nil {
+//			response.Fail(c, 1003, "写入数据失败"+err.Error())
+//			return
+//		}
+//	} else {
+//		var newAdData AdData
+//		adData, _ := json.Marshal(form.AdData)
+//		json.Unmarshal(adData, &newAdData)
+//
+//		var oldAdData AdData
+//		oldAdDatas, _ := json.Marshal(result["adData"])
+//		json.Unmarshal(oldAdDatas, &oldAdData)
+//
+//		newAdData.Reported = oldAdData.Reported
+//
+//		//更新到MongoDB 中
+//		update := bson.M{
+//			"$set": struct {
+//				Gid                string      `bson:"gid" json:"gid"`
+//				Pf                 string      `bson:"pf" json:"pf"`
+//				OpenId             string      `bson:"openId" json:"openId"`
+//				AdData             interface{} `bson:"adData" json:"adData"`
+//				AdFromCount        int         `bson:"adFromCount" json:"adFromCount"`
+//				TotalDuration      int         `bson:"totalDuration" json:"totalDuration"`
+//				TotalAdReqCount    int         `bson:"totalAdReqCount" json:"totalAdReqCount"`
+//				TotalAdEposedCount int         `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
+//			}{
+//				Gid:                behavior.Gid,
+//				Pf:                 behavior.Pf,
+//				OpenId:             behavior.OpenId,
+//				AdData:             newAdData,
+//				AdFromCount:        behavior.AdFromCount,
+//				TotalDuration:      behavior.TotalDuration,
+//				TotalAdReqCount:    behavior.TotalAdReqCount,
+//				TotalAdEposedCount: behavior.TotalAdEposedCount,
+//			},
+//		}
+//		_, err = collection.UpdateOne(context.Background(), filter, update)
+//		if err != nil {
+//			response.Fail(c, 1003, "写入数据失败"+err.Error())
+//			return
+//		}
+//	}
+//
+//	//如果是从广告获取进来的用户,则需要检测是否上传
+//	if form.AdData != nil {
+//		res := service.OceanReportAction(behavior.Id)
+//		if res.Code != 0 {
+//			//上传出现了问题,或者并没有上传
+//			global.App.Log.Error(res.Code, res.Message)
+//		}
+//	}
+//
+//	response.Success(c, gin.H{})
+//}
 
 func CheckUserBehavior(c *gin.Context) {
 	gid := c.GetString("gid")
@@ -214,9 +192,9 @@ func UpdateUserBehavior(c *gin.Context) {
 	})
 }
 
-func FindUserBehavior(id string) *UserBehavior {
+func FindUserBehavior(id string) *model.UserBehavior {
 	filter := bson.M{"_id": id}
-	var result UserBehavior
+	var result model.UserBehavior
 	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
 	err := collection.FindOne(context.Background(), filter).Decode(&result)
 	if err != nil {
@@ -227,53 +205,81 @@ func FindUserBehavior(id string) *UserBehavior {
 }
 
 // 新建UserBehavior
-func CreateUserBehavior(gid string, pf string, openId string) error {
+func CreateUserBehavior(gid string, pf string, openId string, now time.Time) error {
 	id := gid + "|" + pf + "|" + openId
 
 	Behavior := FindUserBehavior(id)
 	if Behavior != nil {
-		return errors.New("该用户数据已经存在")
-	}
+		//更新start_num参数
+		collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
+		filter := bson.M{"_id": id}
+		update := bson.M{"$inc": bson.M{"startNum": 1}}
 
-	behavior := UserBehavior{
-		Id:                 id,
-		Gid:                gid,
-		Pf:                 pf,
-		OpenId:             openId,
-		AdData:             nil,
-		AdFromCount:        0,
-		TotalDuration:      0,
-		TotalAdReqCount:    0,
-		TotalAdEposedCount: 0,
-	}
-	//新增
-	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
-	_, err := collection.InsertOne(context.Background(), behavior)
-	if err != nil {
-		return err
+		_, err := collection.UpdateOne(context.Background(), filter, update)
+		if err != nil {
+			return err
+		}
+
+		//更新关联的广告数据
+		filter = bson.M{"userId": id}
+		collection = global.App.MongoDB.Database("chunhao").Collection("adRelated")
+		update = bson.M{"$inc": bson.M{"startNum": 1}}
+		_, err = collection.UpdateMany(context.Background(), filter, update)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	} else {
+		behavior := model.UserBehavior{
+			Id:                 id,
+			Gid:                gid,
+			Pf:                 pf,
+			OpenId:             openId,
+			TotalDuration:      0,
+			TotalAdReqCount:    0,
+			TotalAdEposedCount: 0,
+			RelatedAid:         0,
+			StartNum:           1,
+			CreateTime:         int(now.Unix()),
+			CreateDate:         now.Format("20060102"),
+			ActiveStatus:       false,
+			ConversionStatus:   false,
+			RemainData:         nil,
+		}
+		//新增
+		collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
+		_, err := collection.InsertOne(context.Background(), behavior)
+		if err != nil {
+			return err
+		}
 	}
+
+	//存储在线数据到redis中
+	global.App.Redis.Set(context.Background(), id+"|online", now.Unix(), time.Second*300)
+
 	return nil
 }
 
 // 更新UserBehavior 在线时间
 func UpdateUserBehaviorDuration(gid string, pf string, openId string, now time.Time, types int) error {
 	id := gid + "|" + pf + "|" + openId
+
+	fmt.Println(now.Format("2006-01-02 15:04:05"))
 	Behavior := FindUserBehavior(id)
 	if Behavior == nil {
 		return errors.New("该用户数据未保存,无法计算在线时间")
 	}
 
-	lastTime, err := global.App.Redis.Get(context.Background(), id+"|online").Result()
-	if err != nil {
-		return err
-	}
+	lastTime, _ := global.App.Redis.Get(context.Background(), id+"|online").Result()
 
 	//当状态为在线时,刷新redis数据,状态为2是离线,不刷新
 	if types == 1 {
-		global.App.Redis.Set(context.Background(), id+"|online", now.Unix(), 300)
+		global.App.Redis.Set(context.Background(), id+"|online", now.Unix(), time.Second*300)
 	} else {
 		global.App.Redis.Del(context.Background(), id+"|online")
 	}
+	//fmt.Println("types", types)
 
 	//如果redis中没有查到在线状态,那就视为刚刚登录,不增加时间
 	if lastTime == "" {
@@ -284,30 +290,28 @@ func UpdateUserBehaviorDuration(gid string, pf string, openId string, now time.T
 	lastTimeUnix, _ := strconv.ParseInt(lastTime, 10, 64)
 	duration := now.Unix() - lastTimeUnix
 
-	var adData interface{}
-	if Behavior.AdData != nil {
-		//存在adData
-		var newAdData AdData
-		oldAdDatas, _ := json.Marshal(Behavior.AdData)
-		json.Unmarshal(oldAdDatas, &newAdData)
-
-		newAdData.Duration = newAdData.Duration + duration
-		adData = newAdData
-	}
-
 	//更新到MongoDB 中
 	update := bson.M{
 		"$set": struct {
-			AdData        interface{} `bson:"adData" json:"adData"`
-			TotalDuration int         `bson:"duration" json:"duration"`
+			TotalDuration int `bson:"totalDuration" json:"totalDuration"`
 		}{
-			AdData:        adData,
 			TotalDuration: Behavior.TotalDuration + int(duration),
 		},
 	}
 	filter := bson.M{"_id": id}
 	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
-	_, err = collection.UpdateOne(context.Background(), filter, update)
+	_, err := collection.UpdateOne(context.Background(), filter, update)
+	if err != nil {
+		return err
+	}
+
+	//更新到当日数据中 todo
+
+	//更新到广告关联数据中
+	filter = bson.M{"userId": id}
+	collection = global.App.MongoDB.Database("chunhao").Collection("adRelated")
+	update = bson.M{"$inc": bson.M{"duration": int(duration)}}
+	_, err = collection.UpdateMany(context.Background(), filter, update)
 	if err != nil {
 		return err
 	}
@@ -324,51 +328,28 @@ func UpdateUserBehaviorAdInfo(gid string, pf string, openId string, adsState int
 
 	Behavior := FindUserBehavior(id)
 	if Behavior == nil {
-		return errors.New("该用户数据未保存,无法计算在线时间")
-	}
-	var adData interface{}
-	if Behavior.AdData != nil {
-		//存在adData
-		var newAdData AdData
-		oldAdDatas, _ := json.Marshal(Behavior.AdData)
-		json.Unmarshal(oldAdDatas, &newAdData)
-
-		newAdData.AdReqCount++
-		if adsState == 2 {
-			newAdData.AdEposedcount++
-		}
-
-		adData = newAdData
+		return errors.New("该用户数据未保存,无法累加看广告数据")
 	}
 
 	var AdReqCount int
 	var AdEposedcount int
-	var AdFormCount int
 	if adsState == 0 {
 		//展示不成功
-		AdReqCount = 1
-		AdFormCount = 1
 	} else if adsState == 1 {
 		//展示成功
 		AdReqCount = 1
-		AdFormCount = 1
 	} else if adsState == 2 {
 		//展示成功并且看完
 		AdReqCount = 1
-		AdFormCount = 1
 		AdEposedcount = 1
 	}
 
-	//更新到MongoDB 
+	//更新到总数据
 	update := bson.M{
 		"$set": struct {
-			AdData             interface{} `bson:"adData" json:"adData"`
-			AdFromCount        int         `bson:"adFromCount" json:"adFromCount"`
-			TotalAdReqCount    int         `bson:"totalAdReqCount" json:"totalAdReqCount"`
-			TotalAdEposedCount int         `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
+			TotalAdReqCount    int `bson:"totalAdReqCount" json:"totalAdReqCount"`
+			TotalAdEposedCount int `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
 		}{
-			AdData:             adData,
-			AdFromCount:        Behavior.AdFromCount + AdFormCount,
 			TotalAdReqCount:    Behavior.TotalAdReqCount + AdReqCount,
 			TotalAdEposedCount: Behavior.TotalAdEposedCount + AdEposedcount,
 		},
@@ -378,5 +359,38 @@ func UpdateUserBehaviorAdInfo(gid string, pf string, openId string, adsState int
 	if err != nil {
 		return err
 	}
+
+	//更新到当日数据中 todo
+
+	//更新到广告关联数据中
+	filter = bson.M{"userId": id}
+	collection = global.App.MongoDB.Database("chunhao").Collection("adRelated")
+	update = bson.M{"$inc": bson.M{"req_count": AdReqCount, "exp_count": AdEposedcount}}
+	_, err = collection.UpdateMany(context.Background(), filter, update)
+	if err != nil {
+		return err
+	}
+
 	return nil
 }
+
+// 上报给巨量对接系统
+func CheckUserActive(gid string, pf string, openId string) {
+	request := map[string]interface{}{
+		"gid":      gid,
+		"platform": pf,
+		"openId":   openId,
+	}
+	data, err := service.CurlPost(config.Get("app.check_user_active"), request, nil)
+	if err != nil {
+		global.App.Log.Error("CheckUserActive err: ", err)
+		return
+	}
+
+	res := make(map[string]interface{})
+	err = json.Unmarshal([]byte(data), &res)
+	if err != nil {
+		global.App.Log.Error("解析返回json错误: ", err)
+	}
+	fmt.Println(res)
+}

+ 70 - 39
controller/v1/user.go

@@ -28,17 +28,24 @@ func ReceiveGameMsg(c *gin.Context) {
 
 		Data string `form:"data" json:"data" binding:""`
 
-		AdsId    string `form:"adsId" json:"adsId" binding:""`
-		AdsType  string `form:"adsType" json:"adsType" binding:""`
-		AdsScene string `form:"adsScene" json:"adsScene" binding:""`
-		AdsState int    `form:"adsState" json:"adsState" binding:""`
+		AdsId       string `form:"adsId" json:"adsId" binding:""`
+		AdsType     string `form:"adsType" json:"adsType" binding:""`
+		AdsScene    string `form:"adsScene" json:"adsScene" binding:""`
+		AdsState    int    `form:"adsState" json:"adsState" binding:""`
+		AdStartTime int64  `form:"adStartTime" json:"adStartTime" binding:""`
 	}{})
 
 	gid := c.GetString("gid")
 	pf := c.GetString("pf")
 	openId := c.GetString("openid")
 
+	if gid == "linkup" {
+		global.App.Log.Info("用户请求ReceiveGameMsg,参数打印:")
+		utils.PrintStructFields(form)
+	}
+
 	now := time.UnixMilli(form.Timestamp)
+	adStartTime := time.UnixMilli(form.AdStartTime)
 	var err error
 	if form.Action == "login" {
 		//登录
@@ -51,13 +58,16 @@ func ReceiveGameMsg(c *gin.Context) {
 		err = offline(gid, pf, openId, form.UserId, now)
 	} else if form.Action == "seeAds" {
 		//观看广告
-		err = seeAds(gid, pf, openId, form.UserId, now, form.AdsId, form.AdsType, form.AdsScene, form.AdsState)
+		err = seeAds(gid, pf, openId, form.UserId, now, form.AdsId, form.AdsType, form.AdsScene, form.AdsState, adStartTime)
 	} else {
 		//自定义类型
 		//查询有无对应的行为记录
 		err = action(gid, pf, form.Action, form.UserId, now, form.Data)
 	}
 
+	//调用接口,检测需不需要上传到巨量
+	CheckUserActive(gid, pf, openId)
+
 	if err != nil {
 		response.Fail(c, 1003, err.Error())
 		return
@@ -139,6 +149,7 @@ func login(gid string, pf string, openId string, userId int, now time.Time) erro
 		user.Gid = gid
 		user.Pf = pf
 		user.CreatedAt = now
+		user.OpenId = openId
 		err = global.App.DB.Table("user").Create(&user).Error
 		if err != nil {
 			global.App.Log.Error("创建用户失败", err.Error(), user)
@@ -172,7 +183,7 @@ func login(gid string, pf string, openId string, userId int, now time.Time) erro
 		return err
 	}
 
-	err = CreateUserBehavior(gid, pf, openId)
+	err = CreateUserBehavior(gid, pf, openId, now)
 	if err != nil {
 		global.App.Log.Error("存储用户信息进入mongo失败", err.Error(), userOnline)
 	}
@@ -195,7 +206,6 @@ func online(gid string, pf string, openId string, userId int, now time.Time) err
 		global.App.Log.Error("存储用户活跃信息失败", err.Error(), userOnline)
 		return err
 	}
-
 	err = UpdateUserBehaviorDuration(gid, pf, openId, now, 1)
 	if err != nil {
 		global.App.Log.Error("存储用户在线时长mongo失败", err.Error(), userOnline)
@@ -227,7 +237,7 @@ func offline(gid string, pf string, openId string, userId int, now time.Time) er
 	return nil
 }
 
-func seeAds(gid string, pf string, openId string, userId int, now time.Time, AdsId string, AdsType string, AdsScene string, AdsState int) error {
+func seeAds(gid string, pf string, openId string, userId int, now time.Time, AdsId string, AdsType string, AdsScene string, AdsState int, adStartTime time.Time) error {
 	if AdsId == "" || AdsType == "" || AdsScene == "" {
 		return errors.New("参数缺失")
 	}
@@ -242,6 +252,7 @@ func seeAds(gid string, pf string, openId string, userId int, now time.Time, Ads
 		AdsType:   AdsType,
 		AdsState:  AdsState,
 		AdsScene:  AdsScene,
+		StartTime: adStartTime,
 	}
 	err := global.App.DB.Table("user_see_ads").Create(&userOnline).Error
 	if err != nil {
@@ -330,45 +341,65 @@ func ReceiveAdsInfo(c *gin.Context) {
 		Pid string `form:"pid" json:"pid" binding:""`
 		Cid string `form:"cid" json:"cid" binding:""`
 	}{})
+
+	//存储参数
+	global.App.Log.Info("ReceiveAdsInfo", form)
+
 	gid := c.GetString("gid")
 	pf := c.GetString("pf")
 	openId := c.GetString("openid")
 
-	id := gid + "|" + pf + "|" + openId
+	userId := gid + "|" + pf + "|" + openId
+	global.App.Log.Info("userId", userId)
 
-	collection := global.App.MongoDB.Database("chunhao").Collection("userBehavior")
-	filter := bson.M{"_id": id}
-	result := make(map[string]interface{})
-	err := collection.FindOne(context.Background(), filter).Decode(&result)
-	if err != nil {
-		response.Fail(c, 1003, err.Error())
-		return
-	}
+	id := userId + "|" + form.Aid
 
-	adData := AdData{
-		Aid:           form.Aid,
-		Pid:           form.Pid,
-		Cid:           form.Cid,
-		Reported:      false,
-		Duration:      0,
-		AdReqCount:    0,
-		AdEposedcount: 0,
-		CreateTime:    int(time.Now().Unix()),
-	}
+	aid, _ := strconv.Atoi(form.Aid)
+	pid, _ := strconv.Atoi(form.Pid)
 
-	//更新到MongoDB 中
-	update := bson.M{
-		"$set": struct {
-			AdData AdData `bson:"adData" json:"adData"`
-		}{
-			AdData: adData,
-		},
-	}
-	_, err = collection.UpdateOne(context.Background(), filter, update)
-	if err != nil {
-		response.Fail(c, 1003, "写入数据失败"+err.Error())
-		return
+	collection := global.App.MongoDB.Database("chunhao").Collection("adRelated")
+
+	var adRelated model.AdRelated
+	filter := bson.M{"_id": id}
+	collection.FindOne(context.Background(), filter).Decode(&adRelated)
+	if adRelated.Id == "" {
+		//新增
+		adData := model.AdRelated{
+			UserId:     userId,
+			Id:         id,
+			Aid:        int64(aid),
+			Pid:        int64(pid),
+			Cid:        form.Cid,
+			Duration:   0,
+			StartNum:   1,
+			Revenue:    0,
+			ReqCount:   0,
+			ExpCount:   0,
+			CreateTime: time.Now().Unix(),
+		}
+
+		//更新到MongoDB 中
+		_, err := collection.InsertOne(context.Background(), adData)
+		if err != nil {
+			response.Fail(c, 1003, "写入数据失败"+err.Error())
+			return
+		}
 	}
+	////关联到userInfo 上
+	//filter = bson.M{"_id": userId}
+	//update := bson.M{
+	//	"$set": struct {
+	//		RelatedAid int64 `bson:"relatedAid" json:"relatedAid"`
+	//	}{
+	//		RelatedAid: int64(aid),
+	//	},
+	//}
+	//collection = global.App.MongoDB.Database("chunhao").Collection("userBehavior")
+	//_, err := collection.UpdateOne(context.Background(), filter, update)
+	//if err != nil {
+	//	response.Fail(c, 1003, err.Error())
+	//	return
+	//}
 
 	response.Success(c, gin.H{"data": "success"})
 }

+ 220 - 0
crons/ecpm.go

@@ -0,0 +1,220 @@
+package crons
+
+import (
+	"context"
+	"designs/global"
+	"designs/service"
+	"encoding/json"
+	"github.com/pkg/errors"
+	"net/http"
+	"net/url"
+	"strconv"
+	"time"
+)
+
+func SetEcpm() {
+	gid := "linkup"
+	err := Ecpm(gid)
+	if err != nil {
+		global.App.Log.Error("Ecpm error", err)
+	}
+	global.App.Log.Info("ser Ecpm success")
+}
+
+func GetAccessToken(gid string, ttAppid string, ttSecret string) (string, error) {
+	tokenKey := gid + "||" + "ttToken"
+	AccessToken := global.App.Redis.Get(context.Background(), tokenKey).Val()
+	if AccessToken == "" {
+		reportDat := map[string]string{
+			"appid":      ttAppid,
+			"secret":     ttSecret,
+			"grant_type": "client_credential",
+		}
+		//请求接口获取token
+		content, err := service.CurlPost("https://minigame.zijieapi.com/mgplatform/api/apps/v2/token", reportDat, nil)
+		if err != nil {
+			return "", err
+		}
+		var resp struct {
+			Data struct {
+				AccessToken string `json:"access_token"`
+				ExpiresIn   int    `json:"expires_in"`
+				ExpiresAt   int    `json:"expiresAt"`
+			} `json:"data"`
+			ErrTips string `json:"err_tips"`
+			ErrNo   int    `json:"err_no"`
+		}
+		str2oErr := json.Unmarshal([]byte(content), &resp)
+		if str2oErr != nil {
+			return "", err
+		}
+
+		if resp.ErrNo != 0 {
+			return "", errors.New(resp.ErrTips)
+		}
+
+		AccessToken = resp.Data.AccessToken
+		global.App.Redis.Set(context.Background(), tokenKey, AccessToken, time.Second*30)
+	}
+	return AccessToken, nil
+}
+
+type UserJoinSeeAds struct {
+	ID        int       `json:"id" gorm:"not null;"`
+	Pf        string    `json:"pf" gorm:"not null;"`
+	Gid       string    `json:"gid" gorm:"not null;"`
+	UserId    int       `json:"userId" gorm:"not null;column:userId;"`
+	OpenId    string    `json:"openId" gorm:"not null;column:openId;"`
+	Date      string    `json:"date" gorm:"not null;"`
+	CreatedAt time.Time `json:"createdAt" gorm:"column:createdAt;"`
+	StartTime time.Time `json:"startTime" gorm:"column:startTime;"`
+	AdsId     string    `json:"adsId" gorm:"not null;column:adsId;"`
+	AdsType   string    `json:"adsType" gorm:"not null;column:adsType;"`
+	AdsScene  string    `json:"adsScene" gorm:"not null;column:adsScene;"`
+	AdsState  int       `json:"adsState" gorm:"not null;column:adsState;"`
+}
+
+func Ecpm(gid string) error {
+
+	res, _ := global.App.Redis.HGetAll(context.Background(), "gid:"+gid).Result()
+
+	//获取和存储accessToken
+	accessToken, err := GetAccessToken(gid, res["ttAppid"], res["ttSecret"])
+	if err != nil {
+		return err
+	}
+	//请求获取结果
+	dateHour := time.Now().AddDate(0, 0, 0).Format("2006-01-02")
+	ecpmRes, err := GetEcpm(res["ttAppid"], accessToken, dateHour)
+	if err != nil {
+		return err
+	}
+	//查询出当日的所有数据
+	var userSeeAds []UserJoinSeeAds
+
+	err = global.App.DB.Table("user_see_ads").
+		LeftJoin("user", "user.userId = user_see_ads.userId and user.gid = user_see_ads.gid").
+		Where("user_see_ads.createdAt", ">=", dateHour).
+		Select("user_see_ads.*", "user.openId").
+		Where("user_see_ads.gid", gid).
+		Where("user_see_ads.pf", "tt").
+		Order("user_see_ads.createdAt asc").
+		Scan(&userSeeAds).Error
+	if err != nil {
+		return err
+	}
+	//按照openId 分组
+	userSeeAdsMap := make(map[string][]UserJoinSeeAds)
+	for _, userSeeAd := range userSeeAds {
+		userSeeAdsMap[userSeeAd.OpenId] = append(userSeeAdsMap[userSeeAd.OpenId], userSeeAd)
+	}
+	ecpmMap := make(map[string][]EcpmItem)
+	for _, record := range ecpmRes.Data.Records {
+		ecpmMap[record.OpenId] = append(ecpmMap[record.OpenId], record)
+	}
+
+	for k, item := range ecpmMap {
+		//跳过数据对不上的
+		if len(userSeeAdsMap[k]) == 0 {
+			continue
+		}
+
+		gid := userSeeAdsMap[k][0].Gid
+		pf := userSeeAdsMap[k][0].Pf
+		openId := userSeeAdsMap[k][0].OpenId
+		userId := userSeeAdsMap[k][0].UserId
+		for key, v := range item {
+			ecpmData := struct {
+				Id int `json:"id" gorm:"not null;"`
+			}{}
+
+			global.App.DB.Table("ecpm_data").Where("ecmpId", v.Id).Select("id").First(&ecpmData)
+			if ecpmData.Id == 0 {
+				err = global.App.DB.Table("ecpm_data").Create(map[string]interface{}{
+					"gid":       gid,
+					"pf":        pf,
+					"openId":    openId,
+					"cost":      v.Cost,
+					"userId":    userId,
+					"ecmpId":    v.Id,
+					"createdAt": v.EventTime,
+				}).Error
+				if err != nil {
+					return err
+				}
+			}
+			//关联到看广告表中
+			if key < len(userSeeAdsMap[k]) {
+				err = global.App.DB.Table("user_see_ads").Where("id", userSeeAdsMap[k][key].ID).Update("cost", v.Cost).Error
+				if err != nil {
+					global.App.Log.Error("id:"+strconv.Itoa(userSeeAdsMap[k][key].ID)+"user_see_ads update cost err:", err.Error())
+					return err
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+type EcpmItem struct {
+	Aid       string `json:"aid"`
+	Cost      int    `json:"cost"`
+	Did       string `json:"did"`
+	EventName string `json:"event_name"`
+	EventTime string `json:"event_time"`
+	OpenId    string `json:"open_id"`
+	Id        int    `json:"id"`
+}
+
+type Result struct {
+	ErrMsg string `json:"err_msg"`
+	ErrNo  int    `json:"err_no"`
+	LogId  string `json:"log_id"`
+	Data   struct {
+		Total   int        `json:"total"`
+		Records []EcpmItem `json:"records"`
+	} `json:"data"`
+}
+
+// GetEcpm 获取用户的ecpm值,用于巨量引擎下小游戏的关键行为转化指标
+func GetEcpm(appId string, accessToken string, dateHour string) (*Result, error) {
+
+	pageNo := 0
+	pageSize := 100
+	var resData Result
+	header := http.Header{
+		"Content-type": []string{"application/json"},
+	}
+
+	for {
+		pageNo++
+		params := url.Values{
+			"access_token": {accessToken},
+			"mp_id":        {appId},
+			"date_hour":    {dateHour},
+			"page_no":      {strconv.Itoa(pageNo)},
+			"page_size":    {strconv.Itoa(pageSize)},
+		}
+
+		res, err := service.CurlGet("https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm", params, header)
+		if err != nil {
+			return nil, err
+		}
+
+		var resp Result
+		err = json.Unmarshal([]byte(res), &resp)
+		if err != nil {
+			return nil, err
+		}
+
+		resData.Data.Records = append(resData.Data.Records, resp.Data.Records...)
+		resData.Data.Total = resp.Data.Total
+		//当获取到所有数据的时候,停止获取数据
+		if pageNo*pageSize >= resp.Data.Total {
+			break
+		}
+	}
+
+	return &resData, nil
+}

+ 2 - 1
global/app.go

@@ -3,6 +3,7 @@ package global
 import (
 	"designs/utils"
 	"github.com/go-redis/redis/v8"
+	"github.com/robfig/cron/v3"
 	"go.mongodb.org/mongo-driver/v2/mongo"
 	"go.uber.org/zap"
 	"io"
@@ -22,7 +23,7 @@ type Application struct {
 	DB *utils.WtDB
 
 	Redis *redis.Client
-	//Cron      *cron.Cron
+	Cron  *cron.Cron
 	//Machinery *machinery.Server
 
 	//日志

+ 1 - 0
go.mod

@@ -11,6 +11,7 @@ require (
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
 	github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20240510055607-89e20ab7b6c6
 	github.com/pkg/errors v0.9.1
+	github.com/robfig/cron/v3 v3.0.0
 	github.com/sirupsen/logrus v1.9.3
 	go.mongodb.org/mongo-driver/v2 v2.0.0-beta2
 	go.uber.org/zap v1.27.0

+ 2 - 0
go.sum

@@ -90,6 +90,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
+github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

+ 2 - 0
main.go

@@ -59,6 +59,8 @@ func main() {
 		}
 	}()
 
+	bootstrap.InitializeCron()
+
 	bootstrap.RunServer()
 	// ginServer.RunTLS(":443", "your_certificate.crt", "your_private_key.key")
 }

+ 58 - 0
model/behavior.go

@@ -0,0 +1,58 @@
+package model
+
+// AdRelated 广告关联,每一条记录为一条当前用户来源关联的广告的详情
+type AdRelated struct {
+	Id         string `bson:"_id" json:"_id"`
+	UserId     string `bson:"userId" json:"userId"`
+	Aid        int64  `json:"aid" bson:"aid"` //广告的推广计划id,即广告ID,广告平台配置落地页参数
+	Cid        string `json:"cid" bson:"cid"` //openid的用户点击aid广告进入游戏时的唯一标识,广告平台提供
+	Pid        int64  `json:"pid" bson:"pid"` //广告的项目id(仅巨量引擎存在,腾讯广告时不存在该值),广告平台配置落地页参数
+	CreateTime int64  `bson:"create_time"`    //当前计划的创建时间
+
+	StartNum int     `bson:"startNum" json:"startNum"`   //启动次数
+	Revenue  float32 `bson:"revenue" json:"revenue"`     //当日预估收益
+	Duration int64   `bson:"duration" json:"duration"`   //当日在线时长
+	ReqCount int     `bson:"req_count" json:"req_count"` //当日的激励视频广告请求次数
+	ExpCount int     `bson:"exp_count" json:"exp_count"` //当日的激励视频广告曝光次数
+}
+
+type UserBehavior struct {
+	Id                 string `bson:"_id,omitempty"`
+	Gid                string `bson:"gid" json:"gid"`
+	Pf                 string `bson:"pf" json:"pf"`
+	OpenId             string `bson:"openId" json:"openId"`
+	RelatedAid         int64  `bson:"relatedAid" json:"relatedAid"` //目前关联的aid
+	TotalDuration      int    `bson:"totalDuration" json:"totalDuration"`
+	TotalAdReqCount    int    `bson:"totalAdReqCount" json:"totalAdReqCount"`
+	TotalAdEposedCount int    `bson:"totalAdEposedCount" json:"totalAdEposedCount"`
+	CreateDate         string `bson:"createDate" json:"createDate"`
+	CreateTime         int    `json:"createTime" bson:"createTime"`
+	StartNum           int    `bson:"startNum" json:"startNum"`
+
+	ActiveStatus     bool              `bson:"activeStatus" json:"activeStatus"`         //激活状态
+	ConversionStatus bool              `bson:"conversionStatus" json:"conversionStatus"` //转化状态
+	RemainData       map[string]string `json:"remainData" bson:"remainData"`             //留存数据
+}
+
+type UserBehaviorByDay struct {
+	Id            string `bson:"_id,omitempty"`
+	Date          string `bson:"date" json:"date"` //统计日期
+	UserId        string `bson:"userId" json:"userId"`
+	Duration      int    `bson:"duration" json:"duration"`
+	AdReqCount    int    `bson:"adReqCount" json:"adReqCount"`
+	AdEposedCount int    `bson:"adEposedCount" json:"adEposedCount"`
+}
+
+// ConversionCondition 转化条件
+type ConversionCondition struct {
+	Id               string  `bson:"_id" json:"id"`
+	Gid              string  `bson:"gid" json:"gid"`
+	Pid              int64   `bson:"pid" json:"pid"`
+	Aid              int64   `bson:"aid" json:"aid"`
+	Type             string  `bson:"type"  json:"type"`
+	StartNum         int     `bson:"start_num" json:"start_num"` //启动次数
+	EstimatedRevenue float32 `bson:"revenue" json:"revenue"`     //当日预估收益
+	Duration         int64   `bson:"duration" json:"duration"`   //当日在线时长
+	ReqRewardedAd    int     `bson:"req_count" json:"req_count"` //当日的激励视频广告请求次数
+	ExpRewardedAd    int     `bson:"exp_count" json:"exp_count"` //当日的激励视频广告曝光次数
+}

+ 2 - 0
model/user.go

@@ -29,6 +29,7 @@ type User struct {
 	Pf        string    `json:"pf" gorm:"not null;"`
 	Gid       string    `json:"gid" gorm:"not null;"`
 	UserId    int       `json:"userId" gorm:"not null;column:userId;"`
+	OpenId    string    `json:"openId" gorm:"not null;column:openId;"`
 	CreatedAt time.Time `json:"createdAt" gorm:"column:createdAt;"`
 }
 
@@ -57,6 +58,7 @@ type UserSeeAds struct {
 	UserId    int       `json:"userId" gorm:"not null;column:userId;"`
 	Date      string    `json:"date" gorm:"not null;"`
 	CreatedAt time.Time `json:"createdAt" gorm:"column:createdAt;"`
+	StartTime time.Time `json:"startTime" gorm:"column:startTime;"`
 	AdsId     string    `json:"adsId" gorm:"not null;column:adsId;"`
 	AdsType   string    `json:"adsType" gorm:"not null;column:adsType;"`
 	AdsScene  string    `json:"adsScene" gorm:"not null;column:adsScene;"`

+ 1 - 1
route/api.go

@@ -21,7 +21,7 @@ func SetApiGroupRoutes(router *gin.RouterGroup) {
 
 		GroupV1.POST("/user/receiveAdsInfo", v1.ReceiveAdsInfo)
 
-		GroupV1.POST("/user/receiveUserBehavior", v1.ReceiveUserBehavior)
+		//GroupV1.POST("/user/receiveUserBehavior", v1.ReceiveUserBehavior)
 		GroupV1.POST("/user/checkUserBehavior", v1.CheckUserBehavior)
 	}
 

+ 236 - 0
service/oceanEngine.go

@@ -0,0 +1,236 @@
+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"`
+}

+ 17 - 0
utils/map.go

@@ -1,5 +1,10 @@
 package utils
 
+import (
+	"fmt"
+	"reflect"
+)
+
 func MergeMaps(destMap, sourceMap map[string]interface{}) map[string]interface{} {
 	newMap := make(map[string]interface{})
 
@@ -15,3 +20,15 @@ func MergeMaps(destMap, sourceMap map[string]interface{}) map[string]interface{}
 
 	return newMap
 }
+
+func PrintStructFields(s interface{}) {
+	v := reflect.ValueOf(s)
+	if v.Kind() == reflect.Struct {
+		t := v.Type()
+		for i := 0; i < v.NumField(); i++ {
+			fieldName := t.Field(i).Name
+			fieldValue := v.Field(i).Interface()
+			fmt.Printf("Key: %s, Value: %v\n", fieldName, fieldValue)
+		}
+	}
+}