Initial commit: web_hghl_server

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 16:28:14 +08:00
co-authored by Claude Opus 4.7
commit 65fca60622
653 changed files with 135901 additions and 0 deletions
+417
View File
@@ -0,0 +1,417 @@
package srv_commend
import (
"context"
"strconv"
"time"
"videoweb/pub/common/enum/redisconst"
"videoweb/pub/common/log"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/commentlikemod"
"videoweb/pub/models/commentmod"
"videoweb/pub/models/playletchaptermod"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/videoinformationmod"
"videoweb/pub/mongos"
"videoweb/pub/pubdef"
"videoweb/pub/redis"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
func SearchComandList(ctx context.Context, param *CommendSearch) (*CommendSearchResp, *srvdef.SrvErr) {
filter := bson.M{}
opt := &options.FindOptions{}
if param.ObjectId != nil {
filter[commentmod.FN.Comment.ObjectId] = *param.ObjectId
}
if param.ObjectType != nil {
filter[commentmod.FN.Comment.ObjectType] = *param.ObjectType
}
if param.Level != nil {
filter[commentmod.FN.Comment.Level] = *param.Level
}
if param.UserName != nil {
filter[commentmod.FN.Comment.UserName] = bson.M{"$regex": *param.UserName}
}
if param.Text != nil {
filter[commentmod.FN.Comment.Text] = bson.M{"$regex": *param.Text}
}
if param.ReviewStatus != nil {
filter[commentmod.FN.Comment.ReviewStatus] = *param.ReviewStatus
}
sort := bson.D{
{commentmod.FN.Comment.TopRank, -1},
{commentmod.FN.Comment.CreatedAt, -1},
}
opt.SetSort(sort)
skip := (param.PageNum - 1) * param.PageSize
opt.SetSkip(skip)
opt.SetLimit(param.PageSize)
total, serr := commentmod.CountComments(ctx, filter)
if serr != nil {
return nil, serr
}
if total == 0 {
return &CommendSearchResp{}, nil
}
commends, serr := commentmod.FindManyComment(ctx, filter, opt)
if serr != nil {
return nil, serr
}
resp := &CommendSearchResp{
Total: total,
List: make([]*PostCommentInfo, 0),
}
for _, commendInfo := range commends {
tmp := &PostCommentInfo{
Id: commendInfo.Id,
ObjectId: commendInfo.ObjectId,
ObjectName: commendInfo.ObjectName,
UserName: commendInfo.UserName,
Level: commendInfo.Level,
ParentsId: commendInfo.ParentsId,
ReplyUserName: commendInfo.ReplyUserName,
Text: commendInfo.Text,
ReviewStatus: commendInfo.ReviewStatus,
UpdatedAt: commendInfo.UpdatedAt,
Ip: commendInfo.Ip,
CreatedAt: commendInfo.CreatedAt,
ObjectType: commendInfo.ObjectType,
Likes: commendInfo.CountLike,
}
resp.List = append(resp.List, tmp)
}
return resp, nil
}
// DelCommend 删除一条评论
func DelCommend(ctx context.Context, commendId int64) *srvdef.SrvErr {
commendInfo, serr := commentmod.FindOneComment(ctx, bson.M{commentmod.FN.Comment.Id: commendId})
if serr != nil {
return serr
}
_, serr = commentmod.DelOneComment(ctx, bson.M{commentmod.FN.Comment.Id: commendId})
if serr != nil {
return serr
}
if serr = commentlikemod.DeleteByCommentIds(ctx, []int64{commendId}); serr != nil {
return serr
}
clearCommentListCacheByComments(commentmod.CommentModelList{commendInfo})
if err := refreshParentReplyCounts(ctx, commentmod.CommentModelList{commendInfo}); err != nil {
return err
}
return updateCommentCountByComments(ctx, commentmod.CommentModelList{commendInfo})
}
// DelCommend 删除用户所有评论
func DelUserCommend(ctx context.Context, ip string) *srvdef.SrvErr {
commendList, serr := commentmod.FindManyComment(ctx, bson.M{commentmod.FN.Comment.Ip: ip})
if serr != nil {
return serr
}
_, serr = commentmod.DelManyComment(ctx, bson.M{commentmod.FN.Comment.Ip: ip})
if serr != nil {
return serr
}
if serr = commentlikemod.DeleteByCommentIds(ctx, commentIdsFromModels(commendList)); serr != nil {
return serr
}
clearCommentListCacheByComments(commendList)
if err := refreshParentReplyCounts(ctx, commendList); err != nil {
return err
}
return updateCommentCountByComments(ctx, commendList)
}
func commentIdsFromModels(comments commentmod.CommentModelList) []int64 {
ids := make([]int64, 0, len(comments))
for _, comment := range comments {
if comment != nil {
ids = append(ids, comment.Id)
}
}
return ids
}
// AddPostComment 发布评论
func AddPostComment(ctx context.Context, req *CommendReplay) *srvdef.SrvErr {
now := time.Now()
if req.Score > 10 {
log.Error("AddPostComment ErrParamError", log.Any("Score", req.Score))
return srvdef.New(srvdef.ErrParamError, "", "")
}
// 2. 生成评论相关信息
commentInfo := &commentmod.CommentModel{
Id: mongos.Lid.Comment.Get(),
ObjectId: req.ObjectId,
ObjectType: pubdef.CommentObjectTypeVideo,
ObjectName: pubdef.CommentObjectTypeName(pubdef.CommentObjectTypeVideo),
UserName: req.UserName,
ParentsId: req.ParentsId,
Text: req.Text,
Level: 1,
UpdatedAt: now,
CreatedAt: now,
}
if commentInfo.ParentsId > 0 {
commentInfo.Level = 2
} else {
commentInfo.Level = 1
}
commentInfo.ReviewStatus = 1 // 管理端的默认审核通过
// 3. 插入数据库
serr := commentmod.InsertOneComment(ctx, commentInfo)
if serr != nil {
log.Error("AddPostComment InsertOnePostComment", log.Any("req", req), log.Any("err", serr))
return serr
}
clearCommentListCacheByComments(commentmod.CommentModelList{commentInfo})
go func() {
_ = updateCommentCountByComments(ctx, commentmod.CommentModelList{commentInfo})
// 4. 修改回复条数
if req.ParentsId > 0 {
filter := bson.M{
commentmod.FN.Comment.Id: req.ParentsId,
}
updateDoc := bson.M{
"$inc": bson.M{
commentmod.FN.Comment.CommentCount: 1,
},
}
serr = commentmod.UpdateOneComment(ctx, filter, updateDoc)
if serr != nil {
log.Error("AddPostComment UpdateOnePostComment", log.Any("req", req), log.Any("err", serr))
return
}
}
}()
return nil
}
// 置顶评论
func TopPostComment(ctx context.Context, id int64) *srvdef.SrvErr {
filter := bson.M{
commentmod.FN.Comment.Id: id,
}
comment, serr := commentmod.FindOneComment(ctx, filter)
if serr != nil {
return serr
}
//获取置顶的一条评论
topRankComment, serr := getTopRankComment(ctx, comment.ObjectId)
if serr != nil {
return serr
}
if topRankComment.Id == id && topRankComment.TopRank != 0 {
return nil
}
serr = commentmod.UpdateOneComment(ctx, filter, bson.M{
"$set": bson.M{
commentmod.FN.Comment.TopRank: topRankComment.TopRank + 1,
},
})
if serr != nil {
return serr
}
clearCommentListCacheByComments(commentmod.CommentModelList{comment})
return nil
}
func getTopRankComment(ctx context.Context, objectId int64) (*commentmod.CommentModel, *srvdef.SrvErr) {
filter := bson.M{
commentmod.FN.Comment.ObjectId: objectId,
}
opts := options.FindOne().SetSort(bson.M{
commentmod.FN.Comment.TopRank: -1,
})
comment, serr := commentmod.FindOneComment(ctx, filter, opts)
if serr != nil {
return nil, serr
}
return comment, nil
}
func ReviewComment(ctx context.Context, id int64, reviewStatus int) *srvdef.SrvErr {
comment, err := commentmod.FindOneComment(ctx, bson.M{commentmod.FN.Comment.Id: id})
if err != nil {
return err
}
updateDoc := bson.M{
commentmod.FN.Comment.ReviewStatus: reviewStatus,
}
filter := bson.M{
commentmod.FN.Comment.Id: id,
}
serr := commentmod.UpdateOneComment(ctx, filter, bson.M{"$set": updateDoc})
if serr != nil {
log.Error("审核评论失败", log.Any("id", id), log.Any("reviewStatus", reviewStatus), log.Any("err", serr))
return serr
}
clearCommentListCacheByComments(commentmod.CommentModelList{comment})
if comment.ReviewStatus != reviewStatus {
if err := refreshParentReplyCounts(ctx, commentmod.CommentModelList{comment}); err != nil {
return err
}
}
return updateCommentCountByComments(ctx, commentmod.CommentModelList{comment})
}
// ReviewCommendBatch 批量更新
func ReviewCommendBatch(ctx context.Context, commendIds *[]int64, reviewStatus int) *srvdef.SrvErr {
if commendIds == nil || len(*commendIds) == 0 {
return srvdef.New(srvdef.ErrParamError, "", "")
}
commentList, err := commentmod.FindManyComment(ctx, bson.M{
commentmod.FN.Comment.Id: bson.M{"$in": *commendIds},
})
if err != nil {
return err
}
updateDoc := bson.M{
commentmod.FN.Comment.ReviewStatus: reviewStatus,
}
filter := bson.M{commentmod.FN.Comment.Id: bson.M{"$in": commendIds}}
serr := commentmod.UpdateManyComment(ctx, filter, bson.M{"$set": updateDoc})
if serr != nil {
log.Error("批量审核评论失败", log.Any("commendId", *commendIds), log.Any("reviewStatus", reviewStatus), log.Any("err", serr))
return serr
}
clearCommentListCacheByComments(commentList)
if err := refreshParentReplyCounts(ctx, commentList); err != nil {
return err
}
return updateCommentCountByComments(ctx, commentList)
}
// refreshParentReplyCounts 按已审核的直接回复重算父评论回复数
func refreshParentReplyCounts(ctx context.Context, comments commentmod.CommentModelList) *srvdef.SrvErr {
seen := make(map[int64]struct{}, len(comments))
for _, comment := range comments {
if comment == nil || comment.ParentsId <= 0 {
continue
}
if _, ok := seen[comment.ParentsId]; ok {
continue
}
seen[comment.ParentsId] = struct{}{}
count, err := commentmod.CountComments(ctx, bson.M{
commentmod.FN.Comment.ParentsId: comment.ParentsId,
commentmod.FN.Comment.ReviewStatus: 1,
})
if err != nil {
return err
}
if err := commentmod.UpdateOneComment(ctx,
bson.M{commentmod.FN.Comment.Id: comment.ParentsId},
bson.M{"$set": bson.M{commentmod.FN.Comment.CommentCount: count}}); err != nil && err.Code != srvdef.ErrNoData {
return err
}
}
return nil
}
// clearCommentListCacheByComments 按对象ID和对象类型精确清理评论列表缓存
func clearCommentListCacheByComments(commentList commentmod.CommentModelList) {
seen := make(map[string]struct{}, len(commentList))
for _, comment := range commentList {
if comment == nil {
continue
}
key := commentCacheUniqueKey(comment.ObjectId, comment.ObjectType)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
cacheKey := redisconst.CommentListCacheKey(comment.ObjectId, comment.ObjectType)
if err := redis.Del(cacheKey); err != nil {
log.Warn("清理评论列表缓存失败", log.Any("cacheKey", cacheKey), log.E(err))
}
}
}
// commentCacheUniqueKey 生成对象维度去重key,避免批量审核时重复清理同一对象缓存
func commentCacheUniqueKey(objectId int64, objectType pubdef.CommentObjectType) string {
return strconv.FormatInt(objectId, 10) + "_" + strconv.FormatInt(int64(objectType), 10)
}
// UpdateCommentCount 评论审核通过/删除后,同步更新对应媒体内容的真实评论数
// objectType: 0=视频 4=短剧 11=短剧剧集
func UpdateCommentCount(ctx context.Context, commendIds []int64) *srvdef.SrvErr {
commentList, _ := commentmod.FindManyComment(ctx, bson.M{
commentmod.FN.Comment.Id: bson.M{"$in": commendIds},
})
return updateCommentCountByComments(ctx, commentList)
}
func updateCommentCountByComments(ctx context.Context, commentList commentmod.CommentModelList) *srvdef.SrvErr {
seen := make(map[string]struct{}, len(commentList))
for _, comment := range commentList {
if comment == nil {
continue
}
key := commentCacheUniqueKey(comment.ObjectId, comment.ObjectType)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
count, countErr := commentmod.CountComments(ctx, bson.M{
commentmod.FN.Comment.ObjectId: comment.ObjectId,
commentmod.FN.Comment.ObjectType: comment.ObjectType,
commentmod.FN.Comment.ReviewStatus: 1,
})
if countErr != nil {
return countErr
}
var serr *srvdef.SrvErr
switch comment.ObjectType {
case pubdef.CommentObjectTypeVideo:
serr = videoinformationmod.UpdateOneVideoInformation(ctx,
bson.M{videoinformationmod.FN.VideoInformation.Id: comment.ObjectId},
bson.M{"$set": bson.M{videoinformationmod.FN.VideoInformation.Real.Comments: count}},
)
case pubdef.CommentObjectTypePlaylet:
serr = playletmod.UpdateOnePlaylet(ctx,
bson.M{playletmod.FN.Playlet.Id: comment.ObjectId},
bson.M{"$set": bson.M{playletmod.FN.Playlet.CountComment: count}},
)
case pubdef.CommentObjectTypePlayletChapter:
serr = playletchaptermod.UpdateOnePlayletChapter(ctx,
bson.M{playletchaptermod.FN.PlayletChapter.Id: comment.ObjectId},
bson.M{"$set": bson.M{playletchaptermod.FN.PlayletChapter.CountComment: count}},
)
}
if serr != nil {
log.Error("UpdateCommentCount 更新评论数失败",
log.Any("objectId", comment.ObjectId),
log.Any("objectType", comment.ObjectType),
log.Any("err", serr),
)
return serr
}
}
return nil
}
+73
View File
@@ -0,0 +1,73 @@
package srv_commend
import (
"time"
"videoweb/pub/pubdef"
)
// CommendSearch 查询评论
type CommendSearch struct {
ObjectId *int64 `json:"objectId" bson:"objectId"` // 对象ID
ObjectType *pubdef.CommentObjectType `json:"objectType" bson:"objectType"` // 对象类型 0=视频 4=短剧 11=短剧剧集
UserName *string `json:"userName" bson:"userName"` // 用户名称
Level *int64 `json:"level" bson:"level"` // 评论级别
Text *string `json:"text" bson:"text"` // 评论内容
ReviewStatus *int `json:"reviewStatus"`
PageNum int64 `json:"pageNum"`
PageSize int64 `json:"pageSize"`
}
type PostCommentInfo struct {
Id int64 `json:"id" bson:"_id,omitempty"` // 评论ID
Ip string `json:"ip" bson:"ip"` // 评论IP
ObjectType pubdef.CommentObjectType `json:"objectType" bson:"objectType"` // 对象类型 0=视频 4=短剧 11=短剧剧集
ObjectId int64 `json:"objectId" bson:"objectId"` // 对象ID
ObjectName string `json:"objectName" bson:"objectName"` // 对象类型名称 视频/短剧
UserName string `json:"userName" bson:"userName"` // 用户名称
Level int64 `json:"level" bson:"level"` // 评论级别
ParentsId int64 `json:"parentsId" bson:"parentsId"` // 上级评论ID
ReplyUserName string `json:"replyUserName" bson:"replyUserName"` // 回复名称
Text string `json:"text" bson:"text"` // 评论内容
ReviewStatus int `json:"reviewStatus" bson:"reviewStatus"` // 审核状态 0 未审核 1审核成功 2 审核失败
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 更新时间
CreatedAt time.Time `bson:"createdAt" json:"createdAt"` // 创建时间
TopRank int64 `json:"topRank" bson:"topRank"` // 置顶排序
Likes int64 `json:"likes" bson:"likes"` // 点赞数
Status pubdef.UserStatus `json:"status" bson:"status"` // 评论用户的状态
}
type CommendSearchResp struct {
Total int64 `json:"total"`
List []*PostCommentInfo `json:"list"`
}
type CommendDelete struct {
Id *int64 `json:"id" bson:"_id" binding:"required"` // Id
}
type CommendReplay struct {
ObjectId int64 `json:"objectId"` // 对象ID
UserName string `json:"userName"` // 用户名
ParentsId int64 `json:"parentsId"` // 评论ID
Text string `json:"text"` // 评论内容
Score int64 `json:"score"` // 评分
}
type CommendUpdate struct {
Score *int64 `json:"score" bson:"score,omitempty"` // 评分
Likes *int64 `json:"likes" bson:"likes,omitempty"` // 点赞数量
}
type CommendUpdateReq struct {
Param CommendUpdate `json:"param" bson:"param"` // 参数
Id int64 `json:"id" bson:"_id,omitempty"` // 评论ID
}
type CommendTopReq struct {
Id int64 `json:"id" bson:"_id,omitempty"` // 评论ID
}
type CommendReviewReq struct {
Id int64 `json:"id"`
ReviewStatus int `json:"reviewStatus"`
}