Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

504 lines
15 KiB
Go

package commentctrl
import (
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/models/l/operatorlgmod"
"91porn-server/models/v/cmtmod"
"91porn-server/models/v/likemod"
"91porn-server/models/v/vidmod"
"91porn-server/web/service/commentser"
"91porn-server/web/webg"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/tealeg/xlsx"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// List doc
// @Summary 评论管理
// @Description 评论列表
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param vidID query string false "视频id"
// @Param uid query integer false "用户id"
// @Param content query string false "评论内容"
// @Param status query integer false "审核状态"
// @Param pageNumber query integer true "页码"
// @Param pageSize query integer true "每页条数"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/comment/list [get]
func List(ctx *gin.Context) {
param := cmtmod.WebCmtListReqInfo{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := commentser.GetCommentList(param.VidID, param.UID, param.Content, param.IsRobot, param.IsAdvertiser, param.Status, param.Page)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Delete doc
// @Summary 评论管理 - 删除评论(逻辑删除)
// @Description 评论删除
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param ids formData array true "需要屏蔽的评论id"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/comment/delete [post]
func Delete(ctx *gin.Context) {
manager, err := common.GetAdminAct(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.AdminIDErr, err.Error())
return
}
param := cmtmod.WebCmtDeleteReqInfo{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if len(param.IDS) == 0 && param.UID == 0 {
err := errors.New(fmt.Sprintf("ids is null"))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if err := commentser.DeleteCommentsByID(param.IDS, param.UID, param.Content); err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
log, _ := json.Marshal(param)
_ = operatorlgmod.RecordOperation(manager, constant.VideoManageComment, constant.Delete, string(log), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, nil)
}
// Info doc
// @Summary 评论管理
// @Description 评论详情
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param id query string true "评论id"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/comment/info [get]
func Info(ctx *gin.Context) {
param := cmtmod.WebCmtReqInfo{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := commentser.GetCommentInfo(param.ID)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Access doc
// @Summary 评论管理
// @Description 通过审核
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param id query string true "评论id"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/comment/access [post]
func Access(ctx *gin.Context) {
param := cmtmod.WebCmtReqInfo{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if err := commentser.AccessComment(param.ID); err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// BatchAccess doc
// @Summary 评论管理
// @Description 批量通过审核
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param ids query []string true "评论ids"
// @Param status query integer true "审核状态"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/web/admin/comment/batch/access [post]
func BatchAccess(ctx *gin.Context) {
param := cmtmod.BatchPassReqInfo{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if err := commentser.BatchAccessComment(param.IDS, param.Status); err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// Update doc
// @Summary 修改评论管理
// @Description 修改评论为神评论
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param id query string true "评论id"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/web/admin/comment/update [post]
func Update(ctx *gin.Context) {
in := cmtmod.EditCond{}
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if code := commentser.UpdateComment(&in); code != stderr.Success {
log.Error("web service comment UpdateComment error", log.E(code), log.Any("id", in.ID))
common.ServeJSON(ctx, stderr.Failure, code)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// AddContentExcel
// 导入评论内容 doc
// @Summary 导入评论内容
// @Description 导入评论内容
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param upload formData file true "需要导入的文件 只支持 .xls .xlsx文件"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/web/admin/comment/importExcel [post]
func AddContentExcel(ctx *gin.Context) {
now := time.Now()
pwd, err := os.Getwd()
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err)
return
}
file, fHeader, err := ctx.Request.FormFile("upload")
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err)
return
}
ext := filepath.Ext(fHeader.Filename)
if !strings.ContainsAny(ext, "xls") {
common.ServeJSON(ctx, stderr.ErrMimeType, nil)
return
}
fPath := pwd + "/" + fHeader.Filename
//defer os.Remove(fPath)
// header调用Filename方法,就可以得到文件名
out, err := os.Create(fPath)
if err != nil {
return
}
//defer out.Close()
// 将file的内容拷贝到out
_, err = io.Copy(out, file)
if err != nil {
return
}
var citys = webg.Static.Cities
var uids = webg.Static.SysUser
xlFile, err := xlsx.OpenFile(fPath)
if err != nil {
return
}
rand.Seed(time.Now().Unix())
comments := make([]*cmtmod.Comment, 0)
likes := make([]*likemod.Like, 0)
commentCountMap := make(map[primitive.ObjectID]int)
// 遍历sheet页读取
for _, sheet := range xlFile.Sheets {
fmt.Println("sheet name: ", sheet.Name)
//遍历行读取
for _, row := range sheet.Rows {
// 遍历每行的列读取
var content string
var vid string
var likeCount int64
for k, cell := range row.Cells {
str := cell.String()
if str == "" {
break
}
if k == 0 {
content = strings.TrimSpace(cell.String())
}
if k == 1 {
vid = strings.TrimSpace(cell.String())
}
if k == 2 {
likeCount, _ = strconv.ParseInt(cell.String(), 10, 64)
}
}
v, err := vidmod.GetVideoInfo(vid)
if err != nil || v.ID.IsZero() {
continue
}
if _, ok := commentCountMap[v.ID]; !ok {
commentCountMap[v.ID] = 0
}
commentCountMap[v.ID] = commentCountMap[v.ID] + 1
reviewT := v.ReviewAt.UTC().Unix()
duration := time.Now().UTC().Unix() - reviewT
rangeMinu := duration / 60
id := primitive.NewObjectID()
comments = append(comments, &(cmtmod.Comment{
ID: id,
ObjID: v.ID,
Type: "video",
PublisherID: v.PublisherID,
UserID: uids[rand.Intn(len(uids)-1)],
AuthorMark: true,
IsAuthor: false,
Content: content,
Level: 1,
IPAddr: fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255)),
Status: 1,
City: citys[rand.Intn(len(citys)-1)],
IsDelete: false,
IsRobot: true,
CreatedAt: now.Add(-time.Minute * time.Duration(rand.Intn(int(rangeMinu)+1))),
}))
for c := int64(0); c < likeCount; c++ {
like := likemod.Like{
Type: "comment",
ObjID: id,
UserID: uids[rand.Intn(len(uids)-1)],
LikedUserID: v.PublisherID,
CreatedAt: time.Now(),
}
like.Uniq = likemod.Unique(like.UserID, like.Type, v.ID)
likes = append(likes, &like)
}
}
}
contentCount, _ := cmtmod.InsertManyBulket(comments)
likeCount, _ := likemod.InsertManyBulk(likes)
_, _ = vidmod.UpdateManyForContentCount(commentCountMap)
common.ServeJSON(ctx, stderr.Success, gin.H{
"contentCount": contentCount,
"likeCount": likeCount,
})
}
// ImportComment
// 导入评论内容 doc
// @Summary 导入评论内容
// @Description 导入评论内容
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param upload formData file true "需要导入的文件 只支持 .xls .xlsx文件"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/web/admin/comment/importComment [post]
func ImportComment(ctx *gin.Context) {
//now := time.Now()
pwd, err := os.Getwd()
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err)
return
}
file, fHeader, err := ctx.Request.FormFile("upload")
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err)
return
}
ext := filepath.Ext(fHeader.Filename)
if !strings.ContainsAny(ext, "xls") {
common.ServeJSON(ctx, stderr.ErrMimeType, nil)
return
}
fPath := pwd + "/" + fHeader.Filename
//defer os.Remove(fPath)
// header调用Filename方法,就可以得到文件名
out, err := os.Create(fPath)
if err != nil {
return
}
//defer out.Close()
// 将file的内容拷贝到out
_, err = io.Copy(out, file)
if err != nil {
return
}
var citys = webg.Static.Cities
//var uids = webg.Static.SysUser
xlFile, err := xlsx.OpenFile(fPath)
if err != nil {
return
}
rand.Seed(time.Now().Unix())
comments := make([]*cmtmod.Comment, 0)
//likes := make([]*likemod.Like, 0)
commentCountMap := make(map[primitive.ObjectID]int)
// 遍历sheet页读取
for _, sheet := range xlFile.Sheets {
fmt.Println("sheet name: ", sheet.Name)
//遍历行读取
for _, row := range sheet.Rows {
// 遍历每行的列读取
var content string
var vid string
var contentDateStr string
var uid uint64
for k, cell := range row.Cells {
str := cell.String()
if str == "" {
break
}
if k == 0 {
// 用户ID
cuid, _ := strconv.ParseInt(cell.String(), 10, 64)
uid = uint64(cuid)
}
if k == 1 {
// 评论内容
content = strings.TrimSpace(cell.String())
}
if k == 2 {
// 评论对象
vid = strings.TrimSpace(cell.String())
}
if k == 3 {
// 评论时间
contentDateStr = strings.TrimSpace(cell.String())
}
}
v, err := vidmod.GetVideoInfo(vid)
if err != nil || v.ID.IsZero() {
continue
}
if _, ok := commentCountMap[v.ID]; !ok {
commentCountMap[v.ID] = 0
}
commentCountMap[v.ID] = commentCountMap[v.ID] + 1
//reviewT := v.ReviewAt.UTC().Unix()
//duration := time.Now().UTC().Unix() - reviewT
//rangeMinu := duration / 60
t, _ := time.Parse("2006-01-02 15:04:05", contentDateStr)
t = t.Add(-time.Hour * 8)
id := primitive.NewObjectID()
comments = append(comments, &(cmtmod.Comment{
ID: id,
ObjID: v.ID,
Type: "video",
PublisherID: v.PublisherID,
UserID: uid,
AuthorMark: true,
IsAuthor: false,
Content: content,
Level: 1,
IPAddr: fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255)),
Status: 1,
City: citys[rand.Intn(len(citys)-1)],
IsDelete: false,
IsRobot: true,
CreatedAt: t,
}))
//for c := int64(0); c < likeCount; c++ {
// like := likemod.Like{
// Type: "comment",
// ObjID: id,
// UserID: uids[rand.Intn(len(uids)-1)],
// LikedUserID: v.PublisherID,
// CreatedAt: time.Now(),
// }
// like.Uniq = likemod.Unique(like.UserID, like.Type, v.ID)
// likes = append(likes, &like)
//}
}
}
var contentCount int64
if len(comments) > 0 {
contentCount, _ = cmtmod.InsertManyBulket(comments)
}
//likeCount, _ := likemod.InsertManyBulk(likes)
_, _ = vidmod.UpdateManyForContentCount(commentCountMap)
common.ServeJSON(ctx, stderr.Success, gin.H{
"contentCount": contentCount,
//"likeCount": likeCount,
})
}
// Send doc
// @Summary 评论模块 - 发表评论
// @Description 用户评论
// @Tags Web-Comment
// @Accept mpfd,json
// @Produce json,html
// @Param objID formData string true "评论对象的ID"
// @Param cid formData string false "此评论是对某条评论的评论或回复,如果为空,则为对该视频的评论"
// @Param rid formData string false "被回复的评论id"
// @Param level formData integer false "评论层级 1:一级评论 2:二级评论"
// @Param toUserID formData integer false "对某用户回复评论 用户ID"
// @Param content formData string true "评论内容"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/web/admin/comment/send [post]
func Send(ctx *gin.Context) {
manager, err := common.GetAdminAct(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.AdminIDErr, err.Error())
return
}
param := cmtmod.PublishReqInfo{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code, err := commentser.PublishComment(param)
log, _ := json.Marshal(param)
_ = operatorlgmod.RecordOperation(manager, constant.VideoManageComment, constant.Add, string(log), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, code, err)
}