package shareser import ( "91porn-server/app/appg" "91porn-server/app/service/versionser" "91porn-server/models/v/mediacontentmod" "91porn-server/models/v/mediamod" "91porn-server/models/v/moduleconfmod" "91porn-server/models/v/sourcemod" "context" "fmt" "math/rand" "strconv" "time" "go.mongodb.org/mongo-driver/bson/primitive" "91porn-server/common/log" "91porn-server/common/stderr" "91porn-server/models/e/sharemod" "91porn-server/models/v/vidmod" "github.com/skip2/go-qrcode" ) // GeneratorQrCode 生成二维码 func GeneratorQrCode(uid uint64, content, videoID string) (code stderr.Code, data interface{}) { return GeneratorQrCodeContext(context.Background(), uid, content, videoID, "") } // GeneratorQrCodeContext 生成二维码,并按分享事件幂等累计短视频推荐分。 func GeneratorQrCodeContext( ctx context.Context, uid uint64, content, videoID, eventID string, ) (code stderr.Code, data interface{}) { return generatorQrCodeContextWithScore( ctx, uid, content, videoID, eventID, func( ctx context.Context, uid uint64, videoID primitive.ObjectID, eventID string, now time.Time, ) error { return incrementRecommendShareOnce( ctx, appg.Redis, uid, videoID, eventID, now, ) }, ) } // GeneratorDramaQrCodeContext 生成短剧分享二维码并幂等累计分享互动分。 func GeneratorDramaQrCodeContext( ctx context.Context, uid uint64, content, mediaID, contentID, eventID string, ) (code stderr.Code, data interface{}) { id, err := primitive.ObjectIDFromHex(mediaID) if err != nil { return stderr.ErrParamError, "" } media, err := mediamod.GetInfo(id) if err != nil || media.ID.IsZero() || media.MediaType != mediamod.MediaTypeDrama || media.Status != 1 || media.IsDelete { return stderr.ErrParamError, "" } if contentID != "" { cid, parseErr := primitive.ObjectIDFromHex(contentID) if parseErr != nil { return stderr.ErrParamError, "" } episode, episodeErr := mediacontentmod.GetInfo(cid, true) if episodeErr != nil || episode.MediaID != id || episode.MediaType != mediamod.MediaTypeDrama || episode.IsDelete { return stderr.ErrParamError, "" } } return generatorQrCodeContextWithScore( ctx, uid, content, mediaID, eventID, func(ctx context.Context, uid uint64, mediaID primitive.ObjectID, eventID string, now time.Time) error { return incrementRecommendShareOnceWith( ctx, appg.Redis, uid, mediaID, eventID, now, func(_ context.Context, id primitive.ObjectID) error { return mediamod.IncreaseCountShareByID(id, 1) }, ) }, ) } type recommendShareScoreFunc func( context.Context, uint64, primitive.ObjectID, string, time.Time, ) error func generatorQrCodeContextWithScore( ctx context.Context, uid uint64, content, videoID, eventID string, score recommendShareScoreFunc, ) (code stderr.Code, data interface{}) { tmp := strconv.FormatUint(uid, 10) url := content + "&share=true&shareuid=" + tmp png, err := qrcode.Encode(url, qrcode.Medium, 256) if err != nil { e := fmt.Sprintf("GeneratorQrCode error: uid(%d), content(%s), err(%+v)\n", uid, content, err) log.Error(e) return stderr.ErrNetWorkBusy, "" } if id, parseErr := primitive.ObjectIDFromHex(videoID); parseErr == nil && score != nil { if incErr := score(ctx, uid, id, eventID, time.Now()); incErr != nil { // 二维码已经生成,推荐统计是旁路能力;瞬时 Redis/Mongo // 故障不能破坏旧分享主流程,失败通过日志和监控告警。 log.Error("increment recommend share failed", log.E(incErr), log.Any("videoID", videoID)) } } return stderr.Success, sharemod.VShareResp{QrCode: png} } // GetShareCnt 获取视频分享次数 func GetShareCnt(videoID string) (code stderr.Code, data interface{}) { cnt, err := vidmod.GetShareCount(videoID) if err != nil { return stderr.ErrNetWorkBusy, "" } data = sharemod.VShareCntResp{VidelID: videoID, Cnt: cnt} return stderr.Success, data } // Info 获取分享的视频信息以及推荐列表 func Info(id primitive.ObjectID, sysType string) (*sharemod.List, *stderr.CustomErr) { video, err := vidmod.GetByIDFromRedis(id.Hex()) if err != nil { return nil, stderr.NewCustomErr(stderr.ErrDbQueryError, err.Error(), "查找视频失败") } if video.ID.IsZero() { return nil, stderr.NewCustomErr(stderr.Failure, "视频未找到", "视频未找到") } blockedModules, err := moduleconfmod.BlockedOutsideSearchModuleIDs([]string{video.MID}, time.Now()) if err != nil { return nil, stderr.NewCustomErr(stderr.ErrDbQueryError, err.Error(), "查找视频失败") } if _, blocked := blockedModules[video.MID]; blocked { return nil, stderr.NewCustomErr(stderr.CodeEmptyData, "视频未找到", "视频未找到") } var picUrl, landUrl string _, pl := sourcemod.PingList() if len(pl) > 0 { for _, v := range pl { if v == nil { continue } if landUrl != "" && picUrl != "" { break } if v.Type == sourcemod.Image && len(v.Domain) > 0 { picUrl = v.Domain[rand.Intn(len(v.Domain))].Url continue } if v.Type == sourcemod.ShareURL && len(v.Domain) > 0 { landUrl = v.Domain[rand.Intn(len(v.Domain))].Url } } } vsis, err := vidmod.GetShareListFromRedis() if err != nil { return nil, stderr.NewCustomErr(stderr.ErrDbQueryError, err.Error(), "查询分享视频推荐列表失败") } vsis, err = filterShareRecommendations(vsis) if err != nil { return nil, stderr.NewCustomErr(stderr.ErrDbQueryError, err.Error(), "查询分享视频推荐列表失败") } ver, _, _, _, _, _, err := versionser.AdvVersionAnnounThreeServer("1.0.1", sysType) if err != nil { log.Error("查询版本信息失败", log.E(err)) return nil, stderr.NewCustomErr(stderr.Failure, err.Error(), "查询下载链接失败") } if len(ver.DownloadLink) == 0 { return nil, stderr.NewCustomErr(stderr.Failure, "", "无有效的app下载地址") } l := sharemod.List{ Title: video.Title, Cover: video.Cover, SourceUrl: video.SourceURL, LandUrl: landUrl, PicUrl: picUrl, Tags: []string{"暗网幼女", "人兽重口", "校园霸凌", "灌醉迷奸", "处女破处", "捉奸现场", "疯狂孕妇", "黑鬼草人妻", "缅北禁区", "全家乱操", "黑料吃瓜", "摄像破解", "群P激战", "AI换脸", "SM调教", "反差母狗"}, DownloadUrl: ver.DownloadLink[0], List: vsis, } return &l, nil } func filterShareRecommendations(items []vidmod.ShareInfo) ([]vidmod.ShareInfo, error) { if len(items) == 0 { return items, nil } ids := make([]primitive.ObjectID, 0, len(items)) for _, item := range items { ids = append(ids, item.ID) } videos, err := vidmod.GetVideosByIDs(ids) if err != nil { return nil, err } excludedModules, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true) if err != nil { return nil, err } excludedSet := make(map[string]struct{}, len(excludedModules)) for _, moduleID := range excludedModules { excludedSet[moduleID] = struct{}{} } excludedVideos := make(map[primitive.ObjectID]struct{}) for _, video := range videos { if video == nil { continue } if _, excluded := excludedSet[video.MID]; excluded { excludedVideos[video.ID] = struct{}{} } } filtered := make([]vidmod.ShareInfo, 0, len(items)) for _, item := range items { if _, excluded := excludedVideos[item.ID]; excluded { continue } filtered = append(filtered, item) } return filtered, nil }