Files
huangguo_server/web/service/vidser/vid.go
T
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

1556 lines
45 KiB
Go

package vidser
import (
"91porn-server/app/service/taskser"
"91porn-server/app/service/vidhelpser"
sli "91porn-server/common/slice"
"91porn-server/models/v/dailytaskmod"
"91porn-server/models/v/modulesectionmod"
"91porn-server/models/v/vidtimeonlinemod"
"bytes"
"encoding/json"
"errors"
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/bxcodec/faker/v3/support/slice"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/gin-gonic/gin"
"91porn-server/app/appg"
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/constant/redisconst"
"91porn-server/common/file"
"91porn-server/common/httputil"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/common/timeutil"
"91porn-server/models/commod"
"91porn-server/models/v/imagetopmod"
"91porn-server/models/v/locmod"
"91porn-server/models/v/modulevidmod"
"91porn-server/models/v/operationlogmod"
"91porn-server/models/v/pushmod"
"91porn-server/models/v/tagmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
"91porn-server/web/service/updownloadser"
"91porn-server/web/vidhelp"
"91porn-server/web/webg"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// SubmitBase 提交基本信息
func SubmitBase(req vidmod.WebSubmitReq) (code stderr.Code, vid primitive.ObjectID) {
tags := make([]primitive.ObjectID, 0, len(req.Tags))
for _, t := range req.Tags {
tid, suc := updownloadser.HandleTag(t, req.Cover)
if !suc {
continue
}
tags = append(tags, tid)
}
l := locmod.Location{
Longitude: req.Location.Longitude,
Latitude: req.Location.Latitude,
Country: req.Location.Country,
CountryCode: req.Location.CountryCode,
Province: req.Location.Province,
ProvinceCode: req.Location.ProvinceCode,
City: req.Location.City,
CityCode: req.Location.CityCode,
Address: req.Location.Address,
Visit: 0,
}
if err := locmod.InsertLocationInfo(l); err != nil {
return stderr.ErrDbInsertError, primitive.NilObjectID
}
lid, err := locmod.GetLocationIDByCity(req.Location.City)
if err != nil {
return stderr.ErrDbQueryError, primitive.NilObjectID
}
series := []string{}
if len(req.SeriesCover) != 0 {
series = req.SeriesCover
}
oid, _ := primitive.ObjectIDFromHex(req.SourceID)
v := vidmod.VideoModel{
ID: oid,
NewsType: req.NewsType,
PublisherID: req.UID,
Title: req.Title,
Content: req.Content,
MimeType: req.MimeType,
Tags: tags,
SourceID: req.SourceID,
SourceURL: req.SourceURL,
PlayTime: req.PlayTime,
Cover: req.Cover,
CoverThumb: req.CoverThumb,
Activity: []primitive.ObjectID{},
SeriesCover: series,
SeriesNum: len(series),
Via: req.Via,
Size: req.Size,
Rating: 0,
PlayCount: 0,
Coins: req.Coins,
LikeCount: 0,
CommentCount: 0,
ShareCount: 0,
Status: 0,
Location: lid,
FreeTime: req.FreeTime,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Width: req.Width,
Height: req.Height,
MDSID: req.MDSID,
VerticalCover: req.VerticalCover,
Ratio: req.Ratio,
Resolution: req.Resolution,
SeedLinkUrl: req.SeedLinkUrl,
SeedSize: req.SeedSize,
SeedPlayTime: req.SeedPlayTime,
RichText: req.RichText,
PreviewStart: req.PreviewStart,
}
if len(v.Tags) > 0 && req.Status == 1 {
v.Status = req.Status
v.ReviewAt = time.Now()
v.ReviewAccount = req.Account
} else if req.Status == vidmod.OnlinePass {
v.Status = req.Status
}
if vid, err = vidmod.InsertBase(v); err != nil {
return stderr.ErrDbInsertError, primitive.NilObjectID
}
for _, t := range tags {
if err = tagmod.IncreaseTagVidCount(t, 1); err != nil {
log.Error("SubmitBase error", log.Any("req", req), log.E(err))
continue
}
}
return stderr.Success, vid
}
// SendBaseInfo2AWs 上传基本文件信息到aws
func SendBaseInfo2AWs(req vidmod.WebSubmitReq) (code stderr.Code) {
info := vidmod.BaseVid4Aws{
ID: req.SourceID,
Tags: req.Tags,
CheckSum: req.MD5,
Title: req.Title,
Actors: []string{req.Actor},
AddedTime: time.Now().Format("2006-01-02"),
PlayTime: uint64(req.PlayTime),
Size: req.Size,
Filename: req.Filename,
Via: "q1",
Type: "sp",
}
params, _ := common.ToJsonM(info)
respBody := commod.Resp{}
if _, err := httputil.DefaultClientPostJsonWithResp(&respBody, webg.Conf.URL.FileInfoUrl, nil, params); err != nil {
log.Error("SendBaseInfo2AWs error", log.Any("req", req), log.E(err))
return stderr.Failure
}
if respBody.Code != stderr.Success {
log.Error("SendBaseInfo2AWs status error", log.Any("req", req), log.Any("respBody.Code", respBody.Code))
return stderr.Failure
}
return stderr.Success
}
// MAP 别名
type MAP map[string]interface{}
func ExportVidList(cond MAP, modeList SortModeList) (buff bytes.Buffer, code stderr.Code) {
var err error
var secID primitive.ObjectID
secID, cond, _, err = dealVidListCond(cond)
if code = err.(stderr.Code); code != stderr.Success {
return
}
sort := make(primitive.D, len(modeList))
for i, v := range modeList {
sort[i] = bson.E{Key: v.Field, Value: v.Value}
}
step := 900
page := 1
vModels, total, err := vidmod.GetvideoListByMapCond(cond, sort, uint64(page), uint64(step))
if err != nil {
code = stderr.ErrDbQueryError
return
}
if total > 30000 {
code = stderr.ErrDbTooMuchRecords
return
}
allVideoInfos, err := vidhelp.EncodeVideoInfo(vModels, secID)
if err != nil {
code = stderr.ErrDbQueryError
return
}
wg := sync.WaitGroup{}
totalPages := int(total)/step + 1
out := make(chan []*vidmod.WebVideo, totalPages)
for page++; page <= totalPages; page++ {
wg.Add(1)
common.GoParam(page, func(i int) {
defer wg.Done()
vModels, _, err = vidmod.GetvideoListByMapCond(cond, sort, uint64(i), uint64(step))
if err != nil {
log.Error("vidmod.GetvideoListByMapCond", log.Any("cond", cond), log.Any("sort", sort),
log.Any("page", page), log.Any("step", step), log.E(err))
return
}
infos, err := vidhelp.EncodeVideoInfo(vModels, secID)
if err != nil {
return
}
out <- infos
})
}
common.Go(func() {
wg.Wait()
close(out)
})
for vs := range out {
allVideoInfos = append(allVideoInfos, vs...)
}
excelFile := file.WriteStruct2Xlsx("帖子列表", allVideoInfos)
defer excelFile.Close()
_ = excelFile.DeleteSheet("Sheet1")
_, _ = excelFile.WriteTo(&buff)
return
}
// GetVidList 获取视频列表
func GetVidList(cond MAP, modeList SortModeList, page, size uint64, isPush bool) (code stderr.Code, data vidmod.ListResp) {
// 为了不影响查询结果,点赞率与成交率优先处理
var purchaseRate, likeRate int
if item, ok := cond["purchaseRate"]; ok {
// 防止panic,断言未命中默认为类型默认值
purchaseRate, _ = item.(int)
delete(cond, "purchaseRate")
}
if item, ok := cond["likeRate"]; ok {
likeRate, _ = item.(int)
delete(cond, "likeRate")
}
secID, cond, videoIDs, err := dealVidListCond(cond)
if code = err.(stderr.Code); code != stderr.Success {
return
}
s := make(primitive.D, len(modeList))
for i, v := range modeList {
s[i] = bson.E{Key: v.Field, Value: v.Value}
}
//是否强推
if isPush {
//查询强推视频
pushList, _, _, err := pushmod.GetPushList(page, size, bson.M{}, bson.D{{Key: "createdAt", Value: -1}})
if err != nil {
return stderr.ErrDbQueryError, data
}
if len(pushList) <= 0 {
return stderr.Success, data
}
vidIds := make([]primitive.ObjectID, len(pushList))
for i, p := range pushList {
vidIds[i] = p.VideoID
}
cond["_id"] = bson.M{"$in": vidIds}
}
log.Info("---", log.Any("cond", cond), log.Any("sort", s))
// 这里会打乱之前排序
vModels, total, err := vidmod.GetvideoListByMapCond(cond, s, page, size)
if err != nil {
return stderr.ErrDbQueryError, data
}
// 因为查询数据库后数据会打乱,因此此处重新根据专题序号进行排序
if !secID.IsZero() {
newVideos := make([]*vidmod.VideoModel, 0, len(videoIDs))
for _, v := range videoIDs {
for _, vv := range vModels {
if v == vv.ID {
newVideos = append(newVideos, vv)
break
}
}
}
vModels = newVideos
}
infos, err := vidhelp.EncodeVideoInfo(vModels, secID)
if err != nil {
return stderr.ErrDbQueryError, data
}
// 当前搜索结果按照成交率排序,正序
if purchaseRate == 1 {
sort.Slice(infos, func(i, j int) bool {
return infos[i].PurchaseRate < infos[j].PurchaseRate
})
}
// 倒序
if purchaseRate == -1 {
sort.Slice(infos, func(i, j int) bool {
return infos[i].PurchaseRate > infos[j].PurchaseRate
})
}
// 当前搜索结果按照点击率排序,正序
if likeRate == 1 {
sort.Slice(infos, func(i, j int) bool {
return infos[i].LikeRate < infos[j].LikeRate
})
}
// 倒序
if likeRate == -1 {
sort.Slice(infos, func(i, j int) bool {
return infos[i].LikeRate > infos[j].LikeRate
})
}
return stderr.Success, vidmod.ListResp{VInfos: infos, Total: total}
}
func dealVidListCond(cond MAP) (primitive.ObjectID, MAP, []primitive.ObjectID, error) {
var secID primitive.ObjectID
var err error
sectionID, ok := cond["sectionID"]
if ok {
secID, err = primitive.ObjectIDFromHex(sectionID.(string))
if err != nil {
log.Error("primitive.ObjectIDFromHex", log.Any("sectionID", sectionID), log.E(err))
return secID, cond, nil, stderr.ErrDbQueryError
}
var flag bool
isSorted, ok := cond["isSortedUnderModule"]
if ok {
flag = isSorted.(bool)
}
videoIDs, err := modulevidmod.GetBySectionID(secID, flag)
if err != nil {
return secID, cond, nil, stderr.ErrDbQueryError
}
cond["_id"] = bson.M{"$in": videoIDs}
delete(cond, "sectionID")
delete(cond, "isSortedUnderModule")
return secID, cond, videoIDs, stderr.Success
}
delete(cond, "sectionID")
delete(cond, "isSortedUnderModule")
return secID, cond, nil, stderr.Success
}
func updateTagVideoCnt(oldTags, newTags []primitive.ObjectID) {
mOld := make(map[primitive.ObjectID]bool)
mNew := make(map[primitive.ObjectID]bool)
for _, v := range newTags {
mNew[v] = true
}
for _, v := range oldTags {
mOld[v] = true
}
for _, v := range newTags {
if !mOld[v] {
_ = tagmod.IncreaseTagVidCount(v, 1)
}
}
for _, v := range oldTags {
if !mNew[v] {
_ = tagmod.IncreaseTagVidCount(v, -1)
}
}
}
func updatePublisherRewarded(id string, specReward int) {
info, err := vidmod.GetVideoInfo(id)
if err != nil {
return
}
_ = usermod.IncRewarded(info.PublisherID, decimal.NewFromInt(int64(specReward)))
}
// UpdateVidInfo 更新视频信息
func UpdateVidInfo(req vidmod.EditReq, manager string) (code stderr.Code, data interface{}) {
vidInfo, err := vidmod.GetVideoInfo(req.ID)
if err != nil {
log.Error("UpdateVidInfo error", log.Any("req", req), log.E(err))
return stderr.CommentVideoInfoErr, nil
}
// —— 上架相关时做敏感词校验 ——
// 触发条件:
// 1. status 切换到 上架(CheckPass=1) / 上架并免费(Free=3)
// 2. 当前已上架(1/3) 且本次更新涉及 Title/Content/RichText
// 校验文本: req 提供则用新值, 否则用 DB 现值(即"落库后"的文本快照)
if shouldCheckSensitiveOnUpdate(vidInfo, req) {
title := vidInfo.Title
content := vidInfo.Content
richText := vidInfo.RichText
if req.Title != nil {
title = *req.Title
}
if req.Content != nil {
content = *req.Content
}
if req.RichText != nil {
richText = *req.RichText
}
if err := CheckShelvingFields(vidInfo.ID, title, content, richText); err != nil {
return stderr.ContentSensitiveHit, err.Error()
}
}
if req.Type == 2 {
if !vidInfo.ID.IsZero() && req.RichText != nil {
if _, err = vidmod.UpdateVideoByMapCond(vidInfo.ID, bson.M{"richText": req.RichText}); err != nil {
return stderr.ErrDbUpdateError, nil
}
return stderr.Success, nil
}
return stderr.ErrParamError, nil
}
if req.Status != nil && *req.Status != vidInfo.Status {
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Status,
BeforeContent: fmt.Sprintf("%v", vidInfo.Status),
AfterContent: fmt.Sprintf("%v", *req.Status),
CreatedUser: manager,
}); err != nil {
return stderr.ErrDbInsertError, nil
}
}
if req.Coins != nil && int64(*req.Coins) != vidInfo.Coins {
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Price,
BeforeContent: fmt.Sprintf("%v", vidInfo.Coins),
AfterContent: fmt.Sprintf("%v", *req.Coins),
CreatedUser: manager,
}); err != nil {
return stderr.ErrDbInsertError, nil
}
}
if req.Title != nil && *req.Title != vidInfo.Title {
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Content,
BeforeContent: fmt.Sprintf("%v", vidInfo.Title),
AfterContent: fmt.Sprintf("%v", *req.Title),
CreatedUser: manager,
}); err != nil {
return stderr.ErrDbInsertError, nil
}
}
oid, err := primitive.ObjectIDFromHex(req.ID)
if err != nil {
log.Error("UpdateVidInfo error", log.Any("req", req), log.E(err))
return stderr.ErrParamError, nil
}
if req.FakeLikeCount != nil {
gt, err := vidmod.FakeLikeGteLike(req.ID, *req.FakeLikeCount)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if !gt {
return stderr.ErrParamError, "fakelikecount need gte likecount."
}
}
oldTags, err := vidmod.GetVideoTag(req.ID)
if err != nil {
return stderr.ErrDbQueryError, nil
}
set := bson.M{}
if req.SeriesCover != nil {
sn := len(*req.SeriesCover)
req.SeriesNum = &sn
}
videoInfo, err := vidmod.GetVideoInfo(req.ID)
if err != nil {
return
}
if req.WorksSort != nil {
if *req.WorksSort != 0 && videoInfo.WorksSort != *req.WorksSort {
// 查询并修改重复排序
err = vidmod.UpdateWorksSort(videoInfo.PublisherID, videoInfo.WorksSort, *req.WorksSort)
if err != nil {
return
}
}
}
if req.Type == 0 {
set, _ = common.ToBsonM(req.EditInfo)
}
newTags := []primitive.ObjectID{}
if len(req.Tags) > 0 {
newTags = vidhelp.AddTags(req.ID, req.Tags)
set["tags"] = newTags
}
if len(req.TagSort) > 0 {
set["tagSort"] = covertTagSort(req)
}
if req.CreatedAt != nil && len(*req.CreatedAt) != 0 {
createdAt := timeutil.StrTimeToTime(*req.CreatedAt)
set["createdAt"] = createdAt
}
if req.Status != nil && *req.Status == vidmod.CheckPass && *req.Status != vidInfo.Status {
set["reviewAt"] = time.Now()
}
if req.PublisherID != nil {
set["publisherID"] = req.PublisherID
}
if req.Content != nil {
set["content"] = req.Content
}
if req.PreviewURL != nil {
set["previewURL"] = *req.PreviewURL
}
if req.UpTag != nil {
set["upTag"] = *req.UpTag
}
if req.NewsType != nil {
set["newsType"] = req.NewsType
}
if req.TimeNodeList != nil {
set["timeNodeList"] = *req.TimeNodeList
}
//if req.LiaoBaTop != nil {
// set["liaoBaTop"] = *req.LiaoBaTop
//}
applyLiaoBaTopSort(set, req.LiaoBaTopSort)
if req.ActivityID != nil {
if *req.ActivityID == "" {
set["activityId"] = primitive.NilObjectID
} else {
activityId, err := primitive.ObjectIDFromHex(*req.ActivityID)
if err != nil {
log.Error(fmt.Sprintf("解析参赛作品活动ID异常:%v", err), log.Any("activityID:", *req.ActivityID))
return stderr.ErrParamError, nil
}
set["activityId"] = activityId
}
}
if req.HappinessPlazaTop != nil {
set["happinessPlazaTop"] = req.HappinessPlazaTop
id, _ := primitive.ObjectIDFromHex(req.ID)
if *req.HappinessPlazaTop == 1 {
//插入幸福广场置顶
imgTop := imagetopmod.ImageTopModel{
ID: primitive.NilObjectID,
NewSort: 0,
NewEndTime: &time.Time{},
RecommendSort: 0,
RecommendEndTime: &time.Time{},
UpdatedAt: time.Now(),
}
imgTop.ImageId = id
//插入数据
if err = imagetopmod.InsertOne(&imgTop); err != nil {
return stderr.ErrDbInsertError, nil
}
} else {
//删除幸福广场置顶
if err = imagetopmod.DeleteOne(id); err != nil {
return stderr.ErrDbDeleteError, nil
}
}
}
if req.ShareSort != nil {
set["shareSort"] = *req.ShareSort
}
delete(set, "fakeRewarded")
if _, err = vidmod.UpdateVideoByMapCond(oid, set); err != nil {
return stderr.ErrDbUpdateError, nil
}
if req.NewsType != nil {
// 更新
modulevidmod.UpdateNewsTypeByVideoID(oid, *req.NewsType)
}
updateTagVideoCnt(oldTags, newTags)
if req.FakeRewarded != nil {
updatePublisherRewarded(req.ID, *req.FakeRewarded)
_ = vidmod.IncSpecFakeRewarded(oid, decimal.NewFromInt(int64(*req.FakeRewarded)))
}
format := redisconst.VideoInfoKey()
s := fmt.Sprintf(format, videoInfo.ID.Hex())
if _, err = webg.Redis.Del(s); err != nil {
return stderr.ErrDbUpdateError, nil
}
return stderr.Success, nil
}
// applyLiaoBaTopSort only builds the update for the video currently being
// edited. Duplicate sort values are intentional and must not mutate another
// video that already uses the same value.
func applyLiaoBaTopSort(set bson.M, sortCode *int) {
if sortCode == nil {
return
}
set["liaoBaTop"] = *sortCode > 0
set["liaoBaTopSort"] = *sortCode
}
func covertTagSort(info vidmod.EditReq) bson.M {
set := make(bson.M, 10)
for _, v := range info.TagSort {
set[v.TagId] = v.SortCode
}
return set
}
// DeleteManyByTTL 删除视频 被删除的视屏归属为officialUID,未审核或审核未通过的视屏通过TTL删除
func DeleteManyByTTL(ids ...primitive.ObjectID) (code stderr.Code, data interface{}) {
delSetDoc := (&vidmod.SetDoc{}).SetPublisherID(constant.DeleteUID).SetDeleteAt(time.Now()).SetStatus(vidmod.CheckFailure)
if _, err := vidmod.ModifyMany(ids, *delSetDoc); err != nil {
return stderr.ErrDbUpdateError, nil
}
if err := modulevidmod.DeleteManyByVids(ids); err != nil {
return stderr.ErrDbDeleteError, nil
}
return stderr.Success, nil
}
func BatchUpdateReco(req vidmod.BatchUpdateRecoRequest) error {
return vidmod.BatchUpdateReco(req.IDs, req.Reco)
}
// UpdateVids 批量更新视频信息
func UpdateVids(req vidmod.EditManyReq) (code stderr.Code, data interface{}) {
if len(req.IDs) == 0 {
return
}
oids := make([]primitive.ObjectID, 0, len(req.IDs))
for _, id := range req.IDs {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
log.Error("UpdateVids error", log.Any("req", req), log.E(err))
continue
}
oids = append(oids, oid)
}
// 上架(UpStatus=1 上架 / 3 上架并免费)前置敏感词校验
if req.UpStatus != nil && (*req.UpStatus == vidmod.CheckPass || *req.UpStatus == vidmod.Free) {
if err := CheckShelvingByIDs(oids); err != nil {
return stderr.ContentSensitiveHit, err.Error()
}
}
newTags := vidhelp.AddTags(req.IDs[0], req.Tags)
req.TagIds = append(req.TagIds, newTags...)
req.TagIds = common.Deduplication(req.TagIds)
var (
count int64
err error
)
if req.Field != "" { // 只更新某个状态
count, err = vidmod.UpdateVideoBatch(oids, req.Field, req.Status)
if err != nil {
if err.Error() == "overLiaoBaLimit" {
return stderr.LiaoBaTopOverLimit, nil
}
return stderr.ErrDbUpdateError, nil
}
} else if req.OriginPublisherID != nil && req.PublisherID != nil {
filter := bson.M{"publisherID": *req.OriginPublisherID}
updateData := bson.M{
"$set": bson.M{"publisherID": *req.PublisherID},
}
// 进行视频转移
err = vidmod.UpdateMany(filter, updateData)
if err != nil {
log.Error("帖子批量转移失败",
log.Any("OriginPublisherID", *req.OriginPublisherID),
log.Any("PublisherID", *req.PublisherID),
log.E(err),
)
return stderr.ErrDbUpdateError, nil
}
} else {
// 更新tag或者价格
var updater = bson.M{
"updatedAt": time.Now(),
}
if req.Coins != nil {
updater["coins"] = req.Coins
}
if req.PublisherID != nil {
updater["publisherID"] = req.PublisherID
}
if req.UpStatus != nil {
updater["status"] = *req.UpStatus
}
if req.FreeTime != nil {
updater["freeTime"] = *req.FreeTime
}
if req.DownloadAllow != nil {
updater["downloadAllow"] = *req.DownloadAllow
}
// 这里不去判断tag存不存在,因为tag列表是从服务器拉下去的数据,选中上来的tag也是在数据中存在的
if len(req.TagIds) > 0 {
updater["tags"] = req.TagIds
// 获取相关视频
videoList, err := vidmod.GetVideoListByIDsNoStatus(oids)
if err != nil {
log.Error(fmt.Sprintf("UpdateVidTags vidmod.GetVideoListByIDsNoStatus err:%v, ids:%v", err, req.IDs))
return stderr.ErrDbQueryError, nil
}
var tagMap = map[primitive.ObjectID]int{}
for _, v := range videoList {
for _, v := range v.Tags {
tagMap[v] = tagMap[v] - 1
}
}
for _, tid := range req.TagIds {
tagMap[tid] = len(videoList)
}
// 修改旧tag与新tag的视频数量
tagmod.IncTagVidCountByMap(tagMap)
}
count, err = vidmod.UpdateManyVideo(oids, updater)
if err != nil {
log.Error(fmt.Sprintf("UpdateVids vidmod.UpdateManyVideo err:%v, ids:%v", err, req.IDs))
return stderr.ErrDbQueryError, nil
}
}
return stderr.Success, vidmod.OperateResult{Count: count}
}
// PassAndEditVids 批量审核更新更新视频信息
func PassAndEditVids(req vidmod.PassManyReq, manager string) (code stderr.Code, data interface{}) {
// 上架(含直接通过 / 通过并免费)前置敏感词校验
if req.Pass == vidmod.CheckPass || req.Pass == vidmod.Free {
ids := make([]string, 0, len(req.Infos))
for _, info := range req.Infos {
ids = append(ids, info.ID)
}
if err := CheckShelvingByIDs(parseObjectIDs(ids)); err != nil {
return stderr.ContentSensitiveHit, err.Error()
}
}
var cnt int64 = 0
var mergingCnt, convertingCnt, mergeErrorCnt, convertErrorCnt int64
for _, info := range req.Infos {
oid, err := primitive.ObjectIDFromHex(info.ID)
if err != nil {
log.Error("UpdateVids error", log.Any("req", req), log.E(err))
continue
}
//非图集才需要同步
isCover, err := vidmod.IsNewsCover(info.ID)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if req.Pass == 3 || req.Pass == 1 {
if !isCover {
if code := SyncFileFromFs(info.ID); code != stderr.UpLoadFileComplete {
switch code {
case stderr.FileMerging:
mergingCnt++
case stderr.FileConverting:
convertingCnt++
case stderr.ErrMergeFile:
mergeErrorCnt++
case stderr.ErrConvertFile:
convertErrorCnt++
}
continue
}
}
}
if req.Pass == 2 {
if !isCover {
_ = SyncFileFromFsForNoPassed(info.ID)
}
}
set := make(bson.M)
set["status"] = req.Pass
set["coins"] = info.Coins
if req.Pass == vidmod.Free {
set["status"] = vidmod.CheckPass
set["coins"] = 0
}
now := time.Now()
set["reason"] = info.Reason
set["freeTime"] = info.FreeTime
set["updatedAt"] = now
set["reviewAt"] = now
set["reviewAccount"] = manager
// 查询视频信息
videoInfo, err := vidmod.GetVideoInfo(oid.Hex())
if err != nil {
return stderr.ErrDbQueryError, nil
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Status,
BeforeContent: fmt.Sprintf("%v", videoInfo.Status),
AfterContent: fmt.Sprintf("%v", req.Pass),
CreatedUser: manager,
}); err != nil {
return stderr.ErrDbInsertError, nil
}
if err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Remark,
BeforeContent: fmt.Sprintf("%v", ""),
AfterContent: fmt.Sprintf("%v", info.Reason),
CreatedUser: manager,
}); err != nil {
return stderr.ErrDbInsertError, nil
}
if set["status"] == vidmod.CheckPass {
tagSort := make(bson.M, len(videoInfo.Tags))
for _, v := range videoInfo.Tags {
tagSort[v.Hex()] = 0
}
set["tagSort"] = tagSort
}
_, err = vidmod.UpdateVideoByMapCond(oid, set)
if err != nil {
continue
}
cnt++
}
or := vidmod.OperateResult{Count: cnt, MergingCnt: mergingCnt, ConvertingCnt: convertingCnt, MerErrCnt: mergeErrorCnt, ConvertErrCnt: convertErrorCnt}
return stderr.Success, or
}
// PassVid 通过审核
func PassVid(req vidmod.BatchReq, manager string) (code stderr.Code, data interface{}) {
var mergingCnt, convertingCnt, mergeErrorCnt, convertErrorCnt int64
if req.Pass != 1 && req.Pass != 2 && req.Pass != 3 {
log.Error("PassVid error", log.Any("req", req))
return stderr.ErrParamError, nil
}
// 上架(含直接通过 / 通过并免费)前置敏感词校验
if req.Pass == vidmod.CheckPass || req.Pass == vidmod.Free {
if err := CheckShelvingByIDs(parseObjectIDs(req.IDs)); err != nil {
return stderr.ContentSensitiveHit, err.Error()
}
}
oids := make([]primitive.ObjectID, 0, len(req.IDs))
for _, id := range req.IDs {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
log.Error("PassVid2 error", log.Any("req", req), log.E(err))
continue
}
//非图集才需要同步
info, err := vidmod.GetVideoInfo(id)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if req.Pass == 3 || req.Pass == 1 {
if !info.IsCover() {
if code = SyncFileFromFs(id); code != stderr.UpLoadFileComplete {
switch code {
case stderr.FileMerging:
mergingCnt++
case stderr.FileConverting:
convertingCnt++
case stderr.ErrMergeFile:
mergeErrorCnt++
case stderr.ErrConvertFile:
convertErrorCnt++
}
log.Info(fmt.Sprintf("check vid pass code[%v]", code))
continue
}
}
}
if req.Pass == 2 {
if !info.IsCover() {
_ = SyncFileFromFsForNoPassed(id)
}
}
//此行代码 明白意思 再移动顺序,没有明白则,不要随便移动
oids = append(oids, oid)
tagSort := make(bson.M, len(info.Tags))
for _, v := range info.Tags {
tagSort[v.Hex()] = 0
}
_, err = vidmod.PassVids(oid, req.Pass, req.Reason, manager, tagSort)
if err != nil {
return stderr.ErrDbUpdateError, nil
}
// 完成发布任务
_ = taskser.CompleteDailyTask(nil, info.PublisherID, dailytaskmod.DailyPublish)
}
if len(oids) > 0 {
err := operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Status,
//BeforeContent: fmt.Sprintf("%v", videoInfo.Status),
AfterContent: fmt.Sprintf("%v", req.Pass),
CreatedUser: manager,
})
if err != nil {
return stderr.ErrDbInsertError, nil
}
err = operationlogmod.Insert(operationlogmod.OperationLog{
ID: primitive.NewObjectID(),
OperationType: constant.Media_Edit_Remark,
BeforeContent: fmt.Sprintf("%v", ""),
AfterContent: fmt.Sprintf("%v", req.Reason),
CreatedUser: manager,
})
if err != nil {
return stderr.ErrDbInsertError, nil
}
}
or := vidmod.OperateResult{Count: 1, MergingCnt: mergingCnt, ConvertingCnt: convertingCnt, MerErrCnt: mergeErrorCnt, ConvertErrCnt: convertErrorCnt}
return stderr.Success, or
}
// PushVid 推送视频
func PushVid(req pushmod.AddReq) (code stderr.Code, data interface{}) {
oid, err := primitive.ObjectIDFromHex(req.VideoID)
if err != nil {
log.Error("PushVid error", log.Any("req", req), log.E(err))
return stderr.ErrParamError, nil
}
doc := pushmod.PushModel{
VideoID: oid,
Title: req.Title,
Type: vidmod.SP,
CreatedAt: time.Now(),
}
if err = pushmod.InsertOne(doc); err != nil {
return stderr.ErrDbInsertError, ""
}
return stderr.Success, nil
}
// PushVidList 获取推送列表
func PushVidList(req pushmod.ListReq) (code stderr.Code, data interface{}) {
cond := bson.M{}
if len(req.VideoID) != 0 {
oid, err := primitive.ObjectIDFromHex(req.VideoID)
if err != nil {
log.Error("PushVidList error", log.Any("req", req), log.E(err))
return stderr.ErrParamError, nil
}
cond = bson.M{"videoID": oid}
}
sort := bson.D{{Key: "createdAt", Value: -1}}
infos, _, total, err := pushmod.GetPushList(req.PageNumber, req.PageSize, cond, sort)
if err != nil {
return stderr.ErrDbQueryError, nil
}
return stderr.Success, pushmod.ListResp{VInfos: infos, Total: total}
}
// DeletePushVid 删除推送视频
func DeletePushVid(ids []string) (code stderr.Code, data interface{}) {
oids := make([]primitive.ObjectID, 0, len(ids))
for _, id := range ids {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
log.Error("DeletePushVid error", log.Any("ids", ids), log.E(err))
continue
}
oids = append(oids, oid)
}
if oids == nil {
return stderr.ErrParamError, nil
}
cnt, err := pushmod.Delete(bson.M{"videoID": bson.M{"$in": oids}})
if err != nil {
return stderr.ErrDbDeleteError, ""
}
return stderr.Success, pushmod.OperateResult{Count: cnt}
}
// SyncFileFromFs 从文件服务器同步文件信息未审核通过
func SyncFileFromFsForNoPassed(vID string) (code stderr.Code) {
v, _ := vidmod.GetVideoInfo(vID)
resp, err := updownloadser.PullFileInfo(v.SourceID)
if err != nil {
return stderr.ErrConnectToFs
}
var resolution, actor, direction, quality string
oid, _ := primitive.ObjectIDFromHex(resp.Data.ID)
sourceID := resp.Data.ID
playTime := resp.Data.PlayTime
freeTime := vidmod.GetFreeTime(resp.Data.PlayTime)
if v.FreeTime != 0 && freeTime != v.FreeTime {
freeTime = v.FreeTime
}
seriesCover := resp.Data.CoverImg
width := resp.Data.Width
height := resp.Data.Height
if width != 0 && height != 0 {
resolution = strconv.FormatInt(int64(resp.Data.Width), 10) + "*" + strconv.FormatInt(int64(resp.Data.Height), 10)
identifi := common.IdentifiVideoInfo(int64(resp.Data.Width), int64(resp.Data.Height))
direction = string(identifi.Direction)
quality = string(identifi.Quality)
}
md5 := resp.Data.CheckSum
if len(resp.Data.Actors) > 0 {
actor = strings.Join(resp.Data.Actors, ",")
}
size := resp.Data.Size
filename := resp.Data.Filename
via := resp.Data.Via
ratio := resp.Data.Ratio
update := vidmod.WebVideoUpdateDoc{
ID: oid,
SourceID: &sourceID,
PlayTime: &playTime,
FreeTime: &freeTime,
SeriesCover: &seriesCover,
Resolution: &resolution,
Width: &width,
Height: &height,
MD5: &md5,
Actor: &actor,
Size: &size,
Filename: &filename,
Via: &via,
Ratio: &ratio,
Direction: &direction,
Quality: &quality,
}
if resp.Code == stderr.UpLoadFileComplete {
if v.Cover == "" {
cover := resp.Data.FieldNameFs + "-1.jpg"
update.Cover = &cover
}
if v.CoverThumb == "" {
coverThumb := resp.Data.FieldNameFs + "-2.jpg"
update.CoverThumb = &coverThumb
}
}
_, _ = vidmod.UpdateVideoResolutionPlayTime(v.ID, update)
return resp.Code
}
// SyncFileFromFs 从文件服务器同步文件信息
func SyncFileFromFs(vID string) (code stderr.Code) {
v, _ := vidmod.GetVideoInfo(vID)
resp, err := updownloadser.PullFileInfo(v.SourceID)
if err != nil {
return stderr.ErrConnectToFs
}
if resp.Code == stderr.UpLoadFileComplete {
oid, _ := primitive.ObjectIDFromHex(resp.Data.ID)
sourceID := resp.Data.ID
playTime := resp.Data.PlayTime
freeTime := vidmod.GetFreeTime(resp.Data.PlayTime)
if v.FreeTime != 0 && freeTime != v.FreeTime {
freeTime = v.FreeTime
}
seriesCover := resp.Data.CoverImg
resolution := strconv.FormatInt(int64(resp.Data.Width), 10) + "*" + strconv.FormatInt(int64(resp.Data.Height), 10)
width := resp.Data.Width
height := resp.Data.Height
identifi := common.IdentifiVideoInfo(int64(resp.Data.Width), int64(resp.Data.Height))
direction := string(identifi.Direction)
quality := string(identifi.Quality)
md5 := resp.Data.CheckSum
actor := strings.Join(resp.Data.Actors, ",")
size := resp.Data.Size
filename := resp.Data.Filename
via := resp.Data.Via
ratio := resp.Data.Ratio
update := vidmod.WebVideoUpdateDoc{
ID: oid,
SourceID: &sourceID,
PlayTime: &playTime,
FreeTime: &freeTime,
VideoCover: &seriesCover,
Resolution: &resolution,
Width: &width,
Height: &height,
MD5: &md5,
Actor: &actor,
Size: &size,
Filename: &filename,
Via: &via,
Ratio: &ratio,
Direction: &direction,
Quality: &quality,
}
if v.Cover == "" {
cover := resp.Data.FieldNameFs + "-1.jpg"
update.Cover = &cover
}
if v.CoverThumb == "" {
coverThumb := resp.Data.FieldNameFs + "-2.jpg"
update.CoverThumb = &coverThumb
}
if v.SeriesCover == nil || len(v.SeriesCover) <= 0 {
update.SeriesCover = &seriesCover
}
_, _ = vidmod.UpdateVideoResolutionPlayTime(v.ID, update)
}
return resp.Code
}
// SyncBaseInfoFromFs 从文件服务器同步基本信息
func SyncBaseInfoFromFs(vID string) (code stderr.Code) {
oid, _ := primitive.ObjectIDFromHex(vID)
resp, err := updownloadser.PullFileInfo(vID)
if err != nil {
return stderr.ErrConnectToFs
}
if resp.Code == stderr.UpLoadFileComplete {
update := vidmod.WebSyncServerFileEdit{
PlayTime: resp.Data.PlayTime,
Resolution: strconv.FormatInt(int64(resp.Data.Width), 10) + "*" + strconv.FormatInt(int64(resp.Data.Height), 10),
MD5: resp.Data.CheckSum,
Size: resp.Data.Size,
Ratio: resp.Data.Ratio,
}
set, _ := common.ToBsonM(update)
if _, err = vidmod.UpdateVideoByMapCond(oid, set); err != nil {
return stderr.Failure
}
}
return resp.Code
}
// 查看视频转码状态
func ViewVideoFromFs(id string) (code stderr.Code, data interface{}) {
resp, err := updownloadser.PullFileInfo(id)
if err != nil {
return stderr.ErrConnectToFs, nil
}
back := make(map[string]interface{}, 10)
back["playTime"] = resp.Data.PlayTime
back["size"] = resp.Data.Size
back["width"] = resp.Data.Width
back["height"] = resp.Data.Height
back["status"] = resp.Data.Status
back["ratio"] = resp.Data.Ratio
fileName := resp.Data.Filename
fieldNameFs := resp.Data.FieldNameFs
back["fileName"] = filepath.Join(filepath.Dir(fieldNameFs), fileName)
switch resp.Data.Status {
case vidmod.UploadLoadingToFs:
back["statusDesc"] = "正在上传中"
case vidmod.UploadCompleted:
back["statusDesc"] = "上传完成"
case vidmod.FileUploadError:
back["statusDesc"] = "上传错误"
case vidmod.Merging:
back["statusDesc"] = "正在合并中"
case vidmod.MergeCompleted:
back["statusDesc"] = "合并完成"
case vidmod.MergeError:
back["statusDesc"] = "合并失败"
case vidmod.Converting:
back["statusDesc"] = "正在转码中"
case vidmod.ConvertError:
back["statusDesc"] = "转码失败"
case vidmod.ConvertCompleted, vidmod.Completed:
back["statusDesc"] = "完成状态"
}
return stderr.Success, back
}
type (
GetVideoListByTagReq struct {
TagID string `form:"tagID" json:"tagID" binding:"required"` // 标签ID
SortType int `form:"sortType" json:"sortType" binding:"required"` // 排序类型 1:最新上架 2:最多收藏 3:本月最热 4:最多观看
NewsType string `json:"newsType" form:"newsType" binding:"-"` // 帖子类型: SP、SHORT、COVER
commod.Page
}
GetVideoListByTagRep struct {
List []*vidmod.VideoInfo `json:"list"` // 视频列表
HasNext bool `json:"hasNext"` // 是否还有下一页
}
)
// GetVideoListByTag 通过标签id获取视频列表信息
func (req *GetVideoListByTagReq) GetVideoListByTag() (rep GetVideoListByTagRep, err error) {
excludedModuleIDs, err := excludedTagSearchModuleIDs()
if err != nil {
return rep, err
}
redisKey := tagSearchCacheKey(
redisconst.GetTagSortVideoKey(req.TagID, req.NewsType, req.SortType, req.Skip(), req.Limit()),
excludedModuleIDs,
)
s, err := appg.Redis.Get(redisKey)
if err != nil {
log.Error("GetVideoListByTag redis get error", log.E(err), log.Any("req", req))
}
if s != nil && *s != "" {
err = json.Unmarshal([]byte(*s), &rep)
if err == nil {
return rep, nil
}
log.Error("GetVideoListByTag json unmarshal error", log.E(err), log.Any("val", *s))
}
// --------
tid, _ := primitive.ObjectIDFromHex(req.TagID)
filter := bson.M{
"tags": tid,
"status": 1, //状态,0 未审核 1通过 2审核失败 3视为免费 默认为0
}
applyTagSearchVisibilityFilter(filter, excludedModuleIDs)
if req.NewsType != "" {
filter["newsType"] = req.NewsType
}
// 本项目用到以下3个
//1: 最新:按最新上架时间
//2: 收藏:按总收藏量排序
//4: 观看:播放量最多的数据
var sortCond bson.D
switch req.SortType {
case 2:
sortCond = bson.D{{Key: "collectCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case 3:
filter["reviewAt"] = bson.M{"$gte": time.Now().AddDate(0, -3, 0)}
sortCond = bson.D{{Key: "likeCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case 4:
sortCond = bson.D{{"playCount", -1}, {Key: "reviewAt", Value: -1}}
case 5:
sortCond = bson.D{{Key: "hot", Value: -1}, {Key: "reviewAt", Value: -1}}
case 1: // 最新
fallthrough
default:
sortCond = bson.D{{Key: "reviewAt", Value: -1}}
}
opt := options.Find().
SetSkip(int64(req.Skip())).
SetLimit(int64(req.Limit())).
SetProjection(bson.M{"chosenDate": 0}).
SetSort(sortCond)
out, next, err := vidmod.FindList(filter, opt)
if err != nil {
return rep, err
}
rep.HasNext = next
rep.List = vidhelpser.EncodeVideoInfoNoUID(out)
go func() {
b, _ := json.Marshal(rep)
_ = appg.Redis.Set(redisKey, string(b), redisconst.GetTagSortVideoExpire())
}()
return rep, nil
}
// BatchAuditVidTimeOnlineInfo 批量新增自动上架视频信息
func BatchAuditVidTimeOnlineInfo(req vidtimeonlinemod.BatchAuditReq, price int, manager string) (code stderr.Code, data interface{}) {
if len(req.Vids) > 20 {
return stderr.BatchAuditLengthTooLong, nil
}
// 定时上架前置敏感词校验
if err := CheckShelvingByIDs(req.Vids); err != nil {
return stderr.ContentSensitiveHit, err.Error()
}
//now := time.Now()
//if !req.OnlineTime.After(now) {
// req.OnlineTime = now
//}
// 获取专题
section, err := modulesectionmod.GetBySectionByID(req.SectionID)
if err != nil {
return stderr.ErrDbQueryError, nil
}
var adds []vidtimeonlinemod.VidTimeOnlineModel
var updateIds []primitive.ObjectID
var reErr stderr.Code
var vIds []primitive.ObjectID
reErr = stderr.Success
for _, id := range req.Vids {
vid, _ := primitive.ObjectIDFromHex(id.Hex())
//查询视频信息
vidInfo, err := vidmod.FindOneByFilter(bson.M{"_id": vid})
if err != nil {
log.Error("AddVidTimeOnlineInfo vidmod.FindOneByFilter error", log.Any("req", req), log.Any("err", err))
reErr = stderr.ErrDbQueryError
break
}
if vidInfo == nil || vidInfo.ID.IsZero() {
reErr = stderr.VidNotExists
break
}
// 取消限制长视频
//if vidInfo.PlayTime > 300 && vidInfo.PublisherID > 150000 {
// return stderr.OnlyOfficialVideo, nil
//}
// 自动上架支持类型短视频、帖子、长视频
if vidInfo.NewsType != vidmod.SP && vidInfo.NewsType != vidmod.COVER && vidInfo.NewsType != vidmod.SHORT {
reErr = stderr.VideoTypeDissatisfy
return reErr, nil
}
if len(vidInfo.Tags) <= 0 {
return stderr.VideoTagNoExists, nil
}
//查询上传用户信息
publisherInfo, err1 := usermod.FindUserByUID(vidInfo.PublisherID)
if err1 != nil {
log.Error("AddVidTimeOnlineInfo usermod.FindUserByUID error", log.Any("req", req), log.Any("err", err), log.Any("publisherID", vidInfo.PublisherID))
//reErr = stderr.ErrDbQueryError
continue
}
// 查询该帖子是否已经在定时上架列表,如果存在则更新
videos, err := vidtimeonlinemod.GetByVidIds([]primitive.ObjectID{vidInfo.ID})
if err != nil {
return stderr.ErrDbQueryError, nil
}
vIds = append(vIds, vidInfo.ID)
if len(videos) > 0 {
// 不需要添加只需要修改上线时间
updateIds = append(updateIds, vidInfo.ID)
} else {
add := vidtimeonlinemod.VidTimeOnlineModel{
ID: primitive.NewObjectID(),
PublisherID: vidInfo.PublisherID,
Portrait: publisherInfo.Portrait,
VidType: vidInfo.NewsType,
VidId: vidInfo.ID,
VidTitle: vidInfo.Title,
Cover: vidInfo.Cover,
CreatedAt: time.Now(),
ReviewAccount: manager,
OnlineTime: req.OnlineTime,
SectionId: req.SectionID,
}
if vidInfo.NewsType == vidmod.SP || vidInfo.NewsType == vidmod.SHORT {
add.SourceURL = vidInfo.SourceURL
} else {
add.SeriesCover = vidInfo.SeriesCover
}
adds = append(adds, add)
}
reErr = stderr.Success
}
if reErr != stderr.Success {
return reErr, nil
}
if len(adds) > 0 {
err := vidtimeonlinemod.InsertMany(adds)
if err != nil {
log.Error("AddVidTimeOnlineInfo vidTimeOnlineMod InsertMany error", log.Any("err", err))
return stderr.ErrDbInsertError, nil
}
}
if len(updateIds) > 0 {
updateData := bson.M{
"onlineTime": req.OnlineTime,
}
if !section.ID.IsZero() {
updateData["sectionId"] = section.ID
}
err := vidtimeonlinemod.UpdateMany(updateIds, updateData)
if err != nil {
log.Error("AddVidTimeOnlineInfo vidTimeOnlineMod UpdateMany error", log.Any("err", err))
return stderr.ErrDbInsertError, nil
}
}
if len(vIds) > 0 {
// 批量更新视频信息
update := bson.M{}
update["updatedAt"] = time.Now()
update["status"] = vidmod.OnlinePass // 定时贴上架标记
applyScheduledVideoModuleID(update, section.SubModuleID)
if price >= 0 {
update["coins"] = price
}
result, err := vidmod.UpdateManyVideo(vIds, update)
if err != nil {
return stderr.Failure, err
}
if result == 0 {
return stderr.Failure, errors.New("video update failed")
}
}
return stderr.Success, nil
}
func applyScheduledVideoModuleID(update bson.M, moduleID primitive.ObjectID) {
if !moduleID.IsZero() {
update["mId"] = moduleID.Hex()
}
}
// GetTimeOnlineVidList 获取定时上线视频列表
func GetTimeOnlineVidList(req *vidtimeonlinemod.ListReq) (code stderr.Code, data interface{}) {
var err error
cond := bson.M{}
res := gin.H{"vidModels": "", "total": 0}
if req.UID > 0 {
cond["publisherID"] = req.UID
}
if req.VidId != "" {
vid, _ := primitive.ObjectIDFromHex(req.VidId)
cond["vidId"] = vid
}
if req.Tag != nil {
id, err := tagmod.GetTagIDByName(*req.Tag)
if err != nil {
return stderr.ErrDbQueryError, res
}
cond["tags"] = id
}
vidModels, total, err := vidtimeonlinemod.GetAllByPage(cond, req.Page)
if err != nil {
return stderr.ErrDbQueryError, res
}
var (
tags []primitive.ObjectID
resp []vidtimeonlinemod.VidTimeOnlineModelResp
)
if len(vidModels) > 0 {
for _, v := range vidModels {
tags = append(tags, v.Tags...)
}
} else {
return stderr.Success, res
}
mTags := vidhelp.GetTagsByIDs2Map(tags)
for _, v := range vidModels {
t := vidhelp.GetPartTags(v.Tags, mTags)
resp = append(resp, vidtimeonlinemod.VidTimeOnlineModelResp{
ID: v.ID,
Tags: t,
VidType: v.VidType,
PublisherID: v.PublisherID,
Portrait: v.Portrait,
VidTitle: v.VidTitle,
VidId: v.VidId,
OnlineSort: v.OnlineSort,
SourceURL: v.SourceURL,
Cover: v.Cover,
SeriesCover: v.SeriesCover,
OnlineTime: v.OnlineTime,
CreatedAt: v.CreatedAt,
ReviewAccount: v.ReviewAccount,
SectionId: v.SectionId,
})
}
res["total"] = total
res["vidModels"] = resp
return stderr.Success, res
}
// DeleteTimeOnlineVid 删除定时上线视频
func DeleteTimeOnlineVid(ids []primitive.ObjectID) (code stderr.Code) {
list, err := vidtimeonlinemod.GetByVidIds(ids)
if err != nil {
return stderr.ErrDbQueryError
}
vids := []primitive.ObjectID{}
for _, v := range list {
vids = append(vids, v.VidId)
}
err = vidtimeonlinemod.DeleteByIds(ids)
if err != nil {
return stderr.ErrDbDeleteError
}
// 把帖子状态更新为下架
err = vidmod.UpdateMany(bson.M{"_id": bson.M{"$in": vids}}, bson.M{"$set": bson.M{"status": vidmod.CheckFailure}})
if err != nil {
return stderr.ErrDbDeleteError
}
return stderr.Success
}
// UpdateVidNewsType 批量修改帖子类型
func UpdateVidNewsType(req vidmod.BatchUpdateVidNewsTypeReq) (code stderr.Code) {
newsTypeList := []string{
vidmod.SP,
vidmod.SHORT,
vidmod.PIC,
vidmod.COVER,
vidmod.AD_COVER,
vidmod.AD_SP,
vidmod.SEED_LINK,
}
if !slice.Contains(newsTypeList, req.NewsType) {
return stderr.ErrParamError
}
err := vidmod.UpdateVideosByMapCond(req.Ids, bson.M{
"newsType": req.NewsType,
})
if err != nil {
log.Error("UpdateVideosByMapCond error", log.Any("req", req), log.E(err))
}
return stderr.Success
}
// UpdateVidTags 批量更新视频信息
func UpdateVidTags(req vidmod.EditVideTagsReq) (code stderr.Code) {
if len(req.Ids) <= 0 || len(req.Tags) <= 0 {
return stderr.ErrParamError
}
ids := make([]primitive.ObjectID, 0, len(req.Ids))
for _, id := range req.Ids {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
log.Error("UpdateVids error", log.Any("req", req), log.E(err))
continue
}
ids = append(ids, oid)
}
// 获取相关视频
videoList, err := vidmod.GetVideoListByIDsNoStatus(ids)
if err != nil {
log.Error(fmt.Sprintf("UpdateVidTags vidmod.GetVideoListByIDsNoStatus err:%v, ids:%v", err, req.Ids))
return
}
var newTags []primitive.ObjectID
var oldTags []primitive.ObjectID
update := bson.M{"updatedAt": time.Now()}
if req.Tags != nil {
newTags = vidhelp.AddTags(req.Ids[0], req.Tags)
}
for _, v := range videoList {
oldTags = v.Tags
// 获取旧的tags
if req.Overwrite {
update["tags"] = newTags
} else {
update["tags"] = sli.RemoveRep(append(v.Tags, newTags...))
}
// 修改视频tag
_, err = vidmod.UpdateOneByID(v.ID, update)
if err != nil {
log.Error(fmt.Sprintf("UpdateVidTags vidmod.UpdateOneByID err:%v, id:%v, update: %v", err, v.ID, update))
continue
}
// 修改旧tag与新tag的视频数量
updateTagVideoCnt(oldTags, newTags)
}
return stderr.Success
}