package reportser import ( "context" "crypto/md5" "encoding/hex" "encoding/json" "fmt" "strconv" "time" "videoweb/pub/common" "videoweb/pub/common/enum/consts" "videoweb/pub/common/ipaddr" "videoweb/pub/common/log" "videoweb/pub/common/useragent" "videoweb/pub/dataReport" "videoweb/pub/models/eventmod" "videoweb/pub/models/playletcategorymod" "videoweb/pub/models/playletchaptermod" "videoweb/pub/models/playletmod" "videoweb/pub/models/usermod" "videoweb/pub/pubdef" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) const videoContentTypePlaylet = "short_video" // ReportUserRegister 用户注册事件 func ReportUserRegister(user *usermod.User, ua useragent.UA, ip, registerType string) { if user == nil { return } if registerType == "" { registerType = "deviceid" } ua = fillUserUA(user, ua) createAt := user.CreatedAt if !user.RegisterTime.IsZero() { createAt = user.RegisterTime } eventId := newEventId(pubdef.TrackerAppId(), user.Id, "", createAt.Unix()) event := dataReport.NewCommonField(eventId, dataReport.EventTypeUserRegister, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) event.Payload = dataReport.JsonRawMessage(&dataReport.UserRegisterEvent{ Type: registerType, CreateTime: dataReport.JsonNumber(createAt.Unix()), }) insertEvent(event) } // ReportUserLogin 用户登录事件 func ReportUserLogin(user *usermod.User, ua useragent.UA, ip string, loginType pubdef.LoginType) { if user == nil { return } ua = fillUserUA(user, ua) createTime := time.Now().Unix() eventId := newEventId(pubdef.TrackerAppId(), user.Id, "", createTime) event := dataReport.NewCommonField(eventId, dataReport.EventTypeUserLogin, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) event.Payload = dataReport.JsonRawMessage(&dataReport.UserLoginEvent{ Type: loginTypeEventValue(loginType), }) insertEvent(event) } // ReportPlayletLike 短剧点赞,复用 video_like func ReportPlayletLike(ctx context.Context, userId, playletId, flag int64) { reportPlayletVideoEvent(ctx, userId, playletId, 0, dataReport.EventTypeVideoLike, flag, "") } // ReportPlayletCollect 短剧收藏,复用 video_collect func ReportPlayletCollect(ctx context.Context, userId, playletId, flag int64) { reportPlayletVideoEvent(ctx, userId, playletId, 0, dataReport.EventTypeVideoCollect, flag, "") } // ReportPlayletChapterLike 短剧剧集点赞,复用 video_like func ReportPlayletChapterLike(ctx context.Context, userId, playletId, chapterId, flag int64) { reportPlayletVideoEvent(ctx, userId, playletId, chapterId, dataReport.EventTypeVideoLike, flag, "") } // ReportPlayletChapterCollect 短剧剧集收藏,复用 video_collect func ReportPlayletChapterCollect(ctx context.Context, userId, playletId, chapterId, flag int64) { reportPlayletVideoEvent(ctx, userId, playletId, chapterId, dataReport.EventTypeVideoCollect, flag, "") } // ReportPlayletComment 短剧/剧集评论,复用 video_comment func ReportPlayletComment(ctx context.Context, user *usermod.User, playletId, chapterId int64, content string) { if user == nil || playletId <= 0 { return } pub := buildPlayletVideoPub(ctx, playletId, chapterId) if pub.VideoID == "" { return } ua, ip := uaAndIPFromCtx(ctx) ua = fillUserUA(user, ua) event := dataReport.NewCommonField(dataReport.GenerateGlobalEventID(), dataReport.EventTypeVideoComment, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) event.Payload = dataReport.JsonRawMessage(&dataReport.VideoCommentEvent{ VideoEventPub: pub, CommentContent: content, }) insertEvent(event) } // ReportPlayletUnlock 短剧剧集金币解锁:同时上报金币消耗和购买 func ReportPlayletUnlock(ctx context.Context, user *usermod.User, playlet *playletmod.Playlet, chapter *playletchaptermod.PlayletChapter, amount, balanceBefore, balanceAfter int64, orderNo string) { if user == nil || playlet == nil || chapter == nil || amount <= 0 { return } if full, err := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: user.Id}); err == nil && full != nil { user = full } ua, ip := uaAndIPFromCtx(ctx) ua = fillUserUA(user, ua) productId := strconv.FormatInt(chapter.Id, 10) productName := playletUnlockProductName(playlet, chapter) reportCoinConsume(user, ua, ip, productId, productName, amount, balanceBefore, balanceAfter, KeyPlayletUnlock, orderNo) reportPlayletPurchase(user, ua, ip, playlet, chapter, amount, orderNo) } // ReportPlayletStatusChange 短剧上下架,复用 video_status_change func ReportPlayletStatusChange(playlet *playletmod.Playlet, flag int64) { if playlet == nil || playlet.Id == 0 || flag == 0 { return } typeID, typeName := resolvePlayletType(context.Background(), playlet) event := dataReport.NewCommonField("", dataReport.EventTypeVideoStatusChange, "", useragent.UA{}, "", "") event.Payload = dataReport.JsonRawMessage(&dataReport.VideoStatusChangeEvent{ MediaId: playletMediaId(playlet), VideoID: strconv.FormatInt(playlet.Id, 10), VideoTitle: playlet.Title, VideoTypeID: typeID, VideoTypeName: typeName, Flag: dataReport.JsonNumber(flag), }) insertEvent(event) } // PlayletStatusFlag 短剧上下架映射上报 flag:1上架 2下架 func PlayletStatusFlag(status playletmod.Status) int64 { switch status { case playletmod.StatusUp: return 1 case playletmod.StatusDown: return 2 default: return 0 } } func reportPlayletVideoEvent(ctx context.Context, userId, playletId, chapterId int64, eventType string, flag int64, content string) { if userId <= 0 || playletId <= 0 { return } user, err := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: userId}) if err != nil || user == nil { return } pub := buildPlayletVideoPub(ctx, playletId, chapterId) if pub.VideoID == "" { return } ua, ip := uaAndIPFromCtx(ctx) ua = fillUserUA(user, ua) event := dataReport.NewCommonField(dataReport.GenerateGlobalEventID(), eventType, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) switch eventType { case dataReport.EventTypeVideoLike: event.Payload = dataReport.JsonRawMessage(&dataReport.VideoLikeEvent{ VideoEventPub: pub, Flag: dataReport.JsonNumber(flag), }) case dataReport.EventTypeVideoCollect: event.Payload = dataReport.JsonRawMessage(&dataReport.VideoCollectEvent{ VideoEventPub: pub, Flag: dataReport.JsonNumber(flag), }) case dataReport.EventTypeVideoComment: event.Payload = dataReport.JsonRawMessage(&dataReport.VideoCommentEvent{ VideoEventPub: pub, CommentContent: content, }) default: return } insertEvent(event) } func reportCoinConsume(user *usermod.User, ua useragent.UA, ip, productId, productName string, amount, balanceBefore, balanceAfter int64, reason Key, orderId string) { if user == nil || amount == 0 { return } if productId == "" { if productName == "" { productName = "金币消耗" } productId = md5String(productName) } createTime := time.Now().Unix() eventId := newEventId(pubdef.TrackerAppId(), user.Id, orderId, createTime) event := dataReport.NewCommonField(eventId, dataReport.EventTypeCoinConsume, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) event.Payload = dataReport.JsonRawMessage(&dataReport.CoinConsumeEvent{ ProductID: productId, ProductName: productName, CoinConsumeAmount: dataReport.JsonNumber(amount), CoinBalanceBefore: dataReport.JsonNumber(balanceBefore), CoinBalanceAfter: dataReport.JsonNumber(balanceAfter), ConsumeReasonKey: reason.Key(), ConsumeReasonName: reason.Name(), CreateTime: dataReport.JsonNumber(createTime), OrderId: orderId, }) insertEvent(event) } func reportPlayletPurchase(user *usermod.User, ua useragent.UA, ip string, playlet *playletmod.Playlet, chapter *playletchaptermod.PlayletChapter, amount int64, orderId string) { pub := playletToVideoPub(playlet, chapter) event := dataReport.NewCommonField(dataReport.GenerateGlobalEventID(), dataReport.EventTypeVideoPurchase, user.DistrictCode, ua, strconv.FormatInt(user.Id, 10), ip) event.Payload = dataReport.JsonRawMessage(&dataReport.VideoPurchaseEvent{ MediaId: pub.MediaId, VideoID: pub.VideoID, VideoTitle: pub.VideoTitle, VideoTypeID: pub.VideoTypeID, VideoTypeName: pub.VideoTypeName, VideoContentType: pub.VideoContentType, RecommendTraceId: pub.RecommendTraceId, CoinQuantity: dataReport.JsonNumber(amount), OrderID: orderId, }) insertEvent(event) } func buildPlayletVideoPub(ctx context.Context, playletId, chapterId int64) dataReport.VideoEventPub { playlet, err := playletmod.FindOnePlaylet(ctx, bson.M{playletmod.FN.Playlet.Id: playletId}, options.FindOne().SetProjection(bson.D{ {Key: playletmod.FN.Playlet.Id, Value: 1}, {Key: playletmod.FN.Playlet.Title, Value: 1}, {Key: playletmod.FN.Playlet.CategoryIds, Value: 1}, {Key: playletmod.FN.Playlet.MediaCenterId, Value: 1}, })) if err != nil || playlet == nil { return dataReport.VideoEventPub{} } var chapter *playletchaptermod.PlayletChapter if chapterId > 0 { chapter, _ = playletchaptermod.FindOnePlayletChapter(ctx, bson.M{playletchaptermod.FN.PlayletChapter.Id: chapterId}, options.FindOne().SetProjection(bson.D{ {Key: playletchaptermod.FN.PlayletChapter.Id, Value: 1}, {Key: playletchaptermod.FN.PlayletChapter.PlayletId, Value: 1}, {Key: playletchaptermod.FN.PlayletChapter.Title, Value: 1}, {Key: playletchaptermod.FN.PlayletChapter.CurrentEpisode, Value: 1}, })) } return playletToVideoPub(playlet, chapter) } func playletToVideoPub(playlet *playletmod.Playlet, chapter *playletchaptermod.PlayletChapter) dataReport.VideoEventPub { if playlet == nil { return dataReport.VideoEventPub{} } typeID, typeName := resolvePlayletType(context.Background(), playlet) videoID := strconv.FormatInt(playlet.Id, 10) title := playlet.Title if chapter != nil && chapter.Id > 0 { videoID = strconv.FormatInt(chapter.Id, 10) title = playletUnlockProductName(playlet, chapter) } return dataReport.VideoEventPub{ MediaId: playletMediaId(playlet), VideoID: videoID, VideoTitle: title, VideoTypeID: typeID, VideoTypeName: typeName, VideoContentType: videoContentTypePlaylet, } } func playletUnlockProductName(playlet *playletmod.Playlet, chapter *playletchaptermod.PlayletChapter) string { title := "" if playlet != nil { title = playlet.Title } if chapter == nil { return title } if chapter.Title != "" { if title == "" { return chapter.Title } return fmt.Sprintf("%s %s", title, chapter.Title) } if chapter.CurrentEpisode > 0 { if title == "" { return fmt.Sprintf("第%d集", chapter.CurrentEpisode) } return fmt.Sprintf("%s 第%d集", title, chapter.CurrentEpisode) } return title } func playletMediaId(playlet *playletmod.Playlet) string { if playlet == nil || playlet.MediaCenterId == 0 { return "" } return strconv.FormatUint(uint64(playlet.MediaCenterId), 10) } func resolvePlayletType(ctx context.Context, playlet *playletmod.Playlet) (string, string) { if playlet == nil || len(playlet.CategoryIds) == 0 { return "default", "默认分类" } queryCtx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() category, err := playletcategorymod.FindOnePlayletCategory(queryCtx, bson.M{ playletcategorymod.FN.PlayletCategory.Id: playlet.CategoryIds[0], }) if err != nil || category == nil { return "default", "默认分类" } return strconv.FormatInt(category.Id, 10), category.Name } func uaAndIPFromCtx(ctx context.Context) (useragent.UA, string) { gc, ok := ctx.(*gin.Context) if !ok { if v := ctx.Value(consts.CtxUA); v != nil { if ua, ok := v.(useragent.UA); ok { return ua, "" } } return useragent.UA{}, "" } ua, _ := common.GetUA(gc) return ua, ipaddr.ResolveClientIP(gc) } func fillUserUA(user *usermod.User, ua useragent.UA) useragent.UA { if user == nil { return ua } if ua.DevID == "" { ua.DevID = user.DevID } if ua.DevType == "" { ua.DevType = user.Device } if ua.SysType == "" { ua.SysType = user.SysType } return ua } func loginTypeEventValue(loginType pubdef.LoginType) string { switch loginType { case pubdef.LoginTypeAccount: return "username" case pubdef.LoginTypeEmail: return "email" default: return "deviceid" } } func newEventId(appId string, uid int64, orderId interface{}, createTime int64) string { input := fmt.Sprintf("%v%v%v%v", appId, uid, orderId, createTime) return md5String(input) } func md5String(input string) string { h := md5.New() h.Write([]byte(input)) return hex.EncodeToString(h.Sum(nil)) } func insertEvent(event dataReport.CommonField) { b, _ := json.Marshal(event) if err := eventmod.InsertOneEvent(context.Background(), &eventmod.Event{ EventId: event.EventID, EventType: event.Event, Data: string(b), Status: eventmod.Default, CreatedAt: time.Now(), UpdatedAt: time.Now(), }); err != nil { log.Error("reportser insertEvent failed", log.Any("eventType", event.Event), log.Any("eventId", event.EventID), log.E(err)) } }