🎨 update code structure
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
package captcha_controller
|
|
||||||
|
|
||||||
type CaptchaController struct{}
|
|
@@ -19,6 +19,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CaptchaController struct{}
|
||||||
|
|
||||||
// GenerateRotateCaptcha 生成旋转验证码
|
// GenerateRotateCaptcha 生成旋转验证码
|
||||||
// @Summary 生成旋转验证码
|
// @Summary 生成旋转验证码
|
||||||
// @Description 生成旋转验证码
|
// @Description 生成旋转验证码
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
package client_controller
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type ClientController struct{}
|
|
||||||
|
|
||||||
var mu sync.Mutex
|
|
@@ -8,9 +8,14 @@ import (
|
|||||||
"schisandra-cloud-album/common/result"
|
"schisandra-cloud-album/common/result"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/utils"
|
"schisandra-cloud-album/utils"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ClientController struct{}
|
||||||
|
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
// GenerateClientId 生成客户端ID
|
// GenerateClientId 生成客户端ID
|
||||||
// @Summary 生成客户端ID
|
// @Summary 生成客户端ID
|
||||||
// @Description 生成客户端ID
|
// @Description 生成客户端ID
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
package comment_controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"schisandra-cloud-album/service/impl"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommentController struct{}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var mx sync.Mutex
|
|
||||||
var commentReplyService = impl.CommentReplyServiceImpl{}
|
|
||||||
|
|
||||||
// CommentImages 评论图片
|
|
||||||
type CommentImages struct {
|
|
||||||
TopicId string `json:"topic_id" bson:"topic_id" required:"true"`
|
|
||||||
CommentId int64 `json:"comment_id" bson:"comment_id" required:"true"`
|
|
||||||
UserId string `json:"user_id" bson:"user_id" required:"true"`
|
|
||||||
Images [][]byte `json:"images" bson:"images" required:"true"`
|
|
||||||
CreatedAt string `json:"created_at" bson:"created_at" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommentContent 评论内容
|
|
||||||
type CommentContent struct {
|
|
||||||
NickName string `json:"nickname"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Level int `json:"level,omitempty"`
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
UserId string `json:"user_id"`
|
|
||||||
TopicId string `json:"topic_id"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
ReplyTo int64 `json:"reply_to,omitempty"`
|
|
||||||
ReplyId int64 `json:"reply_id,omitempty"`
|
|
||||||
ReplyUser string `json:"reply_user,omitempty"`
|
|
||||||
ReplyUsername string `json:"reply_username,omitempty"`
|
|
||||||
Author int `json:"author"`
|
|
||||||
Likes int64 `json:"likes"`
|
|
||||||
ReplyCount int64 `json:"reply_count"`
|
|
||||||
CreatedTime time.Time `json:"created_time"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
Browser string `json:"browser"`
|
|
||||||
OperatingSystem string `json:"operating_system"`
|
|
||||||
IsLiked bool `json:"is_liked" default:"false"`
|
|
||||||
Images []string `json:"images,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommentResponse 评论返回值
|
|
||||||
type CommentResponse struct {
|
|
||||||
Size int `json:"size"`
|
|
||||||
Total int64 `json:"total"`
|
|
||||||
Current int `json:"current"`
|
|
||||||
Comments []CommentContent `json:"comments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var likeChannel = make(chan CommentLikeRequest, 1000)
|
|
||||||
var cancelLikeChannel = make(chan CommentLikeRequest, 1000)
|
|
@@ -1,25 +1,21 @@
|
|||||||
package comment_controller
|
package comment_controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/acmestack/gorm-plus/gplus"
|
|
||||||
ginI18n "github.com/gin-contrib/i18n"
|
ginI18n "github.com/gin-contrib/i18n"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/mssola/useragent"
|
"github.com/mssola/useragent"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"schisandra-cloud-album/common/enum"
|
"schisandra-cloud-album/common/enum"
|
||||||
"schisandra-cloud-album/common/result"
|
"schisandra-cloud-album/common/result"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/model"
|
"schisandra-cloud-album/model"
|
||||||
"schisandra-cloud-album/mq"
|
"schisandra-cloud-album/service/impl"
|
||||||
"schisandra-cloud-album/utils"
|
"schisandra-cloud-album/utils"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CommentController struct{}
|
||||||
|
|
||||||
|
var commentReplyService = impl.CommentReplyServiceImpl{}
|
||||||
|
|
||||||
// CommentSubmit 提交评论
|
// CommentSubmit 提交评论
|
||||||
// @Summary 提交评论
|
// @Summary 提交评论
|
||||||
// @Description 提交评论
|
// @Description 提交评论
|
||||||
@@ -67,13 +63,6 @@ func (CommentController) CommentSubmit(c *gin.Context) {
|
|||||||
isAuthor = 1
|
isAuthor = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
commentReply := model.ScaCommentReply{
|
commentReply := model.ScaCommentReply{
|
||||||
Content: commentRequest.Content,
|
Content: commentRequest.Content,
|
||||||
UserId: commentRequest.UserID,
|
UserId: commentRequest.UserID,
|
||||||
@@ -87,56 +76,11 @@ func (CommentController) CommentSubmit(c *gin.Context) {
|
|||||||
OperatingSystem: operatingSystem,
|
OperatingSystem: operatingSystem,
|
||||||
Agent: userAgent,
|
Agent: userAgent,
|
||||||
}
|
}
|
||||||
// 使用 goroutine 进行异步评论保存
|
response := commentReplyService.SubmitCommentService(&commentReply, commentRequest.TopicId, commentRequest.UserID, commentRequest.Images)
|
||||||
errCh := make(chan error, 2)
|
if !response {
|
||||||
go func() {
|
|
||||||
errCh <- commentReplyService.CreateCommentReplyService(&commentReply)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 等待评论回复的创建
|
|
||||||
if err = <-errCh; err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
||||||
tx.Rollback()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图片异步上传
|
|
||||||
if len(commentRequest.Images) > 0 {
|
|
||||||
imagesDataCh := make(chan [][]byte)
|
|
||||||
go func() {
|
|
||||||
imagesData, err := processImages(commentRequest.Images)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
imagesDataCh <- nil // 发送失败信号
|
|
||||||
return
|
|
||||||
}
|
|
||||||
imagesDataCh <- imagesData // 发送处理成功的数据
|
|
||||||
}()
|
|
||||||
|
|
||||||
imagesData := <-imagesDataCh
|
|
||||||
if imagesData == nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
|
||||||
tx.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commentImages := CommentImages{
|
|
||||||
TopicId: commentRequest.TopicId,
|
|
||||||
CommentId: commentReply.Id,
|
|
||||||
UserId: commentRequest.UserID,
|
|
||||||
Images: imagesData,
|
|
||||||
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 goroutine 进行异步图片保存
|
|
||||||
go func() {
|
|
||||||
if _, err = global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").InsertOne(context.Background(), commentImages); err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
tx.Commit()
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -188,13 +132,6 @@ func (CommentController) ReplySubmit(c *gin.Context) {
|
|||||||
isAuthor = 1
|
isAuthor = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
commentReply := model.ScaCommentReply{
|
commentReply := model.ScaCommentReply{
|
||||||
Content: replyCommentRequest.Content,
|
Content: replyCommentRequest.Content,
|
||||||
UserId: replyCommentRequest.UserID,
|
UserId: replyCommentRequest.UserID,
|
||||||
@@ -210,60 +147,11 @@ func (CommentController) ReplySubmit(c *gin.Context) {
|
|||||||
OperatingSystem: operatingSystem,
|
OperatingSystem: operatingSystem,
|
||||||
Agent: userAgent,
|
Agent: userAgent,
|
||||||
}
|
}
|
||||||
// 使用 goroutine 进行异步评论保存
|
response := commentReplyService.SubmitCommentService(&commentReply, replyCommentRequest.TopicId, replyCommentRequest.UserID, replyCommentRequest.Images)
|
||||||
errCh := make(chan error)
|
if !response {
|
||||||
go func() {
|
|
||||||
|
|
||||||
errCh <- commentReplyService.CreateCommentReplyService(&commentReply)
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
|
|
||||||
errCh <- commentReplyService.UpdateCommentReplyCountService(replyCommentRequest.ReplyId)
|
|
||||||
}()
|
|
||||||
// 等待评论回复的创建
|
|
||||||
if err = <-errCh; err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
||||||
tx.Rollback()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图片异步上传
|
|
||||||
if len(replyCommentRequest.Images) > 0 {
|
|
||||||
imagesDataCh := make(chan [][]byte)
|
|
||||||
go func() {
|
|
||||||
imagesData, err := processImages(replyCommentRequest.Images)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
imagesDataCh <- nil // 发送失败信号
|
|
||||||
return
|
|
||||||
}
|
|
||||||
imagesDataCh <- imagesData // 发送处理成功的数据
|
|
||||||
}()
|
|
||||||
|
|
||||||
imagesData := <-imagesDataCh
|
|
||||||
if imagesData == nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
|
||||||
tx.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commentImages := CommentImages{
|
|
||||||
TopicId: replyCommentRequest.TopicId,
|
|
||||||
CommentId: commentReply.Id,
|
|
||||||
UserId: replyCommentRequest.UserID,
|
|
||||||
Images: imagesData,
|
|
||||||
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 goroutine 进行异步图片保存
|
|
||||||
go func() {
|
|
||||||
if _, err = global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").InsertOne(context.Background(), commentImages); err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
tx.Commit()
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -315,13 +203,6 @@ func (CommentController) ReplyReplySubmit(c *gin.Context) {
|
|||||||
isAuthor = 1
|
isAuthor = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
commentReply := model.ScaCommentReply{
|
commentReply := model.ScaCommentReply{
|
||||||
Content: replyReplyRequest.Content,
|
Content: replyReplyRequest.Content,
|
||||||
UserId: replyReplyRequest.UserID,
|
UserId: replyReplyRequest.UserID,
|
||||||
@@ -338,57 +219,11 @@ func (CommentController) ReplyReplySubmit(c *gin.Context) {
|
|||||||
OperatingSystem: operatingSystem,
|
OperatingSystem: operatingSystem,
|
||||||
Agent: userAgent,
|
Agent: userAgent,
|
||||||
}
|
}
|
||||||
|
response := commentReplyService.SubmitCommentService(&commentReply, replyReplyRequest.TopicId, replyReplyRequest.UserID, replyReplyRequest.Images)
|
||||||
errCh := make(chan error, 2)
|
if !response {
|
||||||
go func() {
|
|
||||||
errCh <- commentReplyService.CreateCommentReplyService(&commentReply)
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
errCh <- commentReplyService.UpdateCommentReplyCountService(replyReplyRequest.ReplyId)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err = <-errCh; err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
||||||
tx.Rollback()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(replyReplyRequest.Images) > 0 {
|
|
||||||
imagesDataCh := make(chan [][]byte)
|
|
||||||
go func() {
|
|
||||||
imagesData, err := processImages(replyReplyRequest.Images)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
imagesDataCh <- nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
imagesDataCh <- imagesData
|
|
||||||
}()
|
|
||||||
|
|
||||||
imagesData := <-imagesDataCh
|
|
||||||
if imagesData == nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitFailed"), c)
|
|
||||||
tx.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commentImages := CommentImages{
|
|
||||||
TopicId: replyReplyRequest.TopicId,
|
|
||||||
CommentId: commentReply.Id,
|
|
||||||
UserId: replyReplyRequest.UserID,
|
|
||||||
Images: imagesData,
|
|
||||||
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理图片保存
|
|
||||||
go func() {
|
|
||||||
if _, err = global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").InsertOne(context.Background(), commentImages); err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
tx.Commit()
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentSubmitSuccess"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -408,162 +243,7 @@ func (CommentController) CommentList(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 查询评论列表
|
response := commentReplyService.GetCommentListService(commentListRequest.UserID, commentListRequest.TopicId, commentListRequest.Page, commentListRequest.Size, commentListRequest.IsHot)
|
||||||
query, u := gplus.NewQuery[model.ScaCommentReply]()
|
|
||||||
page := gplus.NewPage[model.ScaCommentReply](commentListRequest.Page, commentListRequest.Size)
|
|
||||||
if commentListRequest.IsHot {
|
|
||||||
query.OrderByDesc(&u.CommentOrder).OrderByDesc(&u.Likes).OrderByDesc(&u.ReplyCount)
|
|
||||||
} else {
|
|
||||||
query.OrderByDesc(&u.CommentOrder).OrderByDesc(&u.CreatedTime)
|
|
||||||
}
|
|
||||||
query.Eq(&u.TopicId, commentListRequest.TopicId).Eq(&u.CommentType, enum.COMMENT)
|
|
||||||
page, pageDB := gplus.SelectPage(page, query)
|
|
||||||
if pageDB.Error != nil {
|
|
||||||
global.LOG.Errorln(pageDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(page.Records) == 0 {
|
|
||||||
result.OkWithData(CommentResponse{Comments: []CommentContent{}, Size: page.Size, Current: page.Current, Total: page.Total}, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userIds := make([]string, 0, len(page.Records))
|
|
||||||
commentIds := make([]int64, 0, len(page.Records))
|
|
||||||
for _, comment := range page.Records {
|
|
||||||
userIds = append(userIds, comment.UserId)
|
|
||||||
commentIds = append(commentIds, comment.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 结果存储
|
|
||||||
userInfoMap := make(map[string]model.ScaAuthUser)
|
|
||||||
likeMap := make(map[int64]bool)
|
|
||||||
commentImagesMap := make(map[int64]CommentImages)
|
|
||||||
|
|
||||||
// 使用 WaitGroup 等待协程完成
|
|
||||||
wg.Add(3)
|
|
||||||
|
|
||||||
// 查询评论用户信息
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
queryUser, n := gplus.NewQuery[model.ScaAuthUser]()
|
|
||||||
queryUser.Select(&n.UID, &n.Avatar, &n.Nickname).In(&n.UID, userIds)
|
|
||||||
userInfos, userInfosDB := gplus.SelectList(queryUser)
|
|
||||||
if userInfosDB.Error != nil {
|
|
||||||
global.LOG.Errorln(userInfosDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, userInfo := range userInfos {
|
|
||||||
userInfoMap[*userInfo.UID] = *userInfo
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 查询评论点赞状态
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if len(page.Records) > 0 {
|
|
||||||
queryLike, l := gplus.NewQuery[model.ScaCommentLikes]()
|
|
||||||
queryLike.Eq(&l.TopicId, commentListRequest.TopicId).Eq(&l.UserId, commentListRequest.UserID).In(&l.CommentId, commentIds)
|
|
||||||
likes, likesDB := gplus.SelectList(queryLike)
|
|
||||||
if likesDB.Error != nil {
|
|
||||||
global.LOG.Errorln(likesDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, like := range likes {
|
|
||||||
likeMap[like.CommentId] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 查询评论图片信息
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // 设置超时,2秒
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cursor, err := global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").Find(ctx, bson.M{"comment_id": bson.M{"$in": commentIds}})
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("Failed to get images for comments: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func(cursor *mongo.Cursor, ctx context.Context) {
|
|
||||||
err := cursor.Close(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(cursor, ctx)
|
|
||||||
|
|
||||||
for cursor.Next(ctx) {
|
|
||||||
var commentImages CommentImages
|
|
||||||
if err = cursor.Decode(&commentImages); err != nil {
|
|
||||||
global.LOG.Errorf("Failed to decode comment images: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
commentImagesMap[commentImages.CommentId] = commentImages
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 等待所有查询完成
|
|
||||||
wg.Wait()
|
|
||||||
commentChannel := make(chan CommentContent, len(page.Records))
|
|
||||||
|
|
||||||
for _, comment := range page.Records {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(comment model.ScaCommentReply) {
|
|
||||||
defer wg.Done()
|
|
||||||
// 将图片转换为base64
|
|
||||||
var imagesBase64 []string
|
|
||||||
if imgData, ok := commentImagesMap[comment.Id]; ok {
|
|
||||||
// 将图片转换为base64
|
|
||||||
for _, img := range imgData.Images {
|
|
||||||
mimeType := getMimeType(img)
|
|
||||||
base64Img := base64.StdEncoding.EncodeToString(img)
|
|
||||||
base64WithPrefix := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Img)
|
|
||||||
imagesBase64 = append(imagesBase64, base64WithPrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfo, exist := userInfoMap[comment.UserId]
|
|
||||||
if !exist {
|
|
||||||
global.LOG.Errorf("Failed to get user info for comment: %s", comment.UserId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
commentContent := CommentContent{
|
|
||||||
Avatar: *userInfo.Avatar,
|
|
||||||
NickName: *userInfo.Nickname,
|
|
||||||
Id: comment.Id,
|
|
||||||
UserId: comment.UserId,
|
|
||||||
TopicId: comment.TopicId,
|
|
||||||
Content: comment.Content,
|
|
||||||
ReplyCount: comment.ReplyCount,
|
|
||||||
Likes: comment.Likes,
|
|
||||||
CreatedTime: comment.CreatedTime,
|
|
||||||
Author: comment.Author,
|
|
||||||
Location: comment.Location,
|
|
||||||
Browser: comment.Browser,
|
|
||||||
OperatingSystem: comment.OperatingSystem,
|
|
||||||
Images: imagesBase64,
|
|
||||||
IsLiked: likeMap[comment.Id],
|
|
||||||
}
|
|
||||||
commentChannel <- commentContent
|
|
||||||
}(*comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(commentChannel)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var commentsWithImages []CommentContent
|
|
||||||
for commentContent := range commentChannel {
|
|
||||||
commentsWithImages = append(commentsWithImages, commentContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
response := CommentResponse{
|
|
||||||
Comments: commentsWithImages,
|
|
||||||
Size: page.Size,
|
|
||||||
Current: page.Current,
|
|
||||||
Total: page.Total,
|
|
||||||
}
|
|
||||||
result.OkWithData(response, c)
|
result.OkWithData(response, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -583,168 +263,7 @@ func (CommentController) ReplyList(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
query, u := gplus.NewQuery[model.ScaCommentReply]()
|
response := commentReplyService.GetCommentReplyService(replyListRequest.UserID, replyListRequest.TopicId, replyListRequest.CommentId, replyListRequest.Page, replyListRequest.Size)
|
||||||
page := gplus.NewPage[model.ScaCommentReply](replyListRequest.Page, replyListRequest.Size)
|
|
||||||
query.Eq(&u.TopicId, replyListRequest.TopicId).Eq(&u.ReplyId, replyListRequest.CommentId).Eq(&u.CommentType, enum.REPLY).OrderByDesc(&u.Likes).OrderByAsc(&u.CreatedTime)
|
|
||||||
page, pageDB := gplus.SelectPage(page, query)
|
|
||||||
if pageDB.Error != nil {
|
|
||||||
global.LOG.Errorln(pageDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(page.Records) == 0 {
|
|
||||||
result.OkWithData(CommentResponse{Comments: []CommentContent{}, Size: page.Size, Current: page.Current, Total: page.Total}, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userIdsSet := make(map[string]struct{}) // 使用集合去重用户 ID
|
|
||||||
commentIds := make([]int64, 0, len(page.Records))
|
|
||||||
// 收集用户 ID 和评论 ID
|
|
||||||
for _, comment := range page.Records {
|
|
||||||
userIdsSet[comment.UserId] = struct{}{} // 去重
|
|
||||||
commentIds = append(commentIds, comment.Id)
|
|
||||||
if comment.ReplyUser != "" {
|
|
||||||
userIdsSet[comment.ReplyUser] = struct{}{} // 去重
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 将用户 ID 转换为切片
|
|
||||||
userIds := make([]string, 0, len(userIdsSet))
|
|
||||||
for userId := range userIdsSet {
|
|
||||||
userIds = append(userIds, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
likeMap := make(map[int64]bool, len(page.Records))
|
|
||||||
commentImagesMap := make(map[int64]CommentImages)
|
|
||||||
userInfoMap := make(map[string]model.ScaAuthUser, len(userIds))
|
|
||||||
|
|
||||||
wg.Add(3)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
// 查询评论用户信息
|
|
||||||
queryUser, n := gplus.NewQuery[model.ScaAuthUser]()
|
|
||||||
queryUser.Select(&n.UID, &n.Avatar, &n.Nickname).In(&n.UID, userIds)
|
|
||||||
userInfos, userInfosDB := gplus.SelectList(queryUser)
|
|
||||||
if userInfosDB.Error != nil {
|
|
||||||
global.LOG.Errorln(userInfosDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, userInfo := range userInfos {
|
|
||||||
userInfoMap[*userInfo.UID] = *userInfo
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
// 查询评论点赞状态
|
|
||||||
|
|
||||||
if len(page.Records) > 0 {
|
|
||||||
queryLike, l := gplus.NewQuery[model.ScaCommentLikes]()
|
|
||||||
queryLike.Eq(&l.TopicId, replyListRequest.TopicId).Eq(&l.UserId, replyListRequest.UserID).In(&l.CommentId, commentIds)
|
|
||||||
likes, likesDB := gplus.SelectList(queryLike)
|
|
||||||
if likesDB.Error != nil {
|
|
||||||
global.LOG.Errorln(likesDB.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, like := range likes {
|
|
||||||
likeMap[like.CommentId] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) // 设置超时,2秒
|
|
||||||
defer cancel()
|
|
||||||
cursor, err := global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").Find(ctx, bson.M{"comment_id": bson.M{"$in": commentIds}})
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("Failed to get images for comments: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func(cursor *mongo.Cursor, ctx context.Context) {
|
|
||||||
warn := cursor.Close(ctx)
|
|
||||||
if warn != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(cursor, ctx)
|
|
||||||
|
|
||||||
for cursor.Next(ctx) {
|
|
||||||
var commentImages CommentImages
|
|
||||||
if e := cursor.Decode(&commentImages); e != nil {
|
|
||||||
global.LOG.Errorf("Failed to decode comment images: %v", e)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
commentImagesMap[commentImages.CommentId] = commentImages
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
replyChannel := make(chan CommentContent, len(page.Records)) // 使用通道传递回复内容
|
|
||||||
|
|
||||||
for _, reply := range page.Records {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(reply model.ScaCommentReply) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
var imagesBase64 []string
|
|
||||||
if imgData, ok := commentImagesMap[reply.Id]; ok {
|
|
||||||
// 将图片转换为base64
|
|
||||||
for _, img := range imgData.Images {
|
|
||||||
mimeType := getMimeType(img)
|
|
||||||
base64Img := base64.StdEncoding.EncodeToString(img)
|
|
||||||
base64WithPrefix := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Img)
|
|
||||||
imagesBase64 = append(imagesBase64, base64WithPrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
userInfo, exist := userInfoMap[reply.UserId]
|
|
||||||
if !exist {
|
|
||||||
global.LOG.Errorf("Failed to get user info for comment: %s", reply.UserId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
replyUserInfo, e := userInfoMap[reply.ReplyUser]
|
|
||||||
if !e {
|
|
||||||
global.LOG.Errorf("Failed to get reply user info for comment: %s", reply.ReplyUser)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
commentContent := CommentContent{
|
|
||||||
Avatar: *userInfo.Avatar,
|
|
||||||
NickName: *userInfo.Nickname,
|
|
||||||
Id: reply.Id,
|
|
||||||
UserId: reply.UserId,
|
|
||||||
TopicId: reply.TopicId,
|
|
||||||
Content: reply.Content,
|
|
||||||
ReplyUsername: *replyUserInfo.Nickname,
|
|
||||||
ReplyCount: reply.ReplyCount,
|
|
||||||
Likes: reply.Likes,
|
|
||||||
CreatedTime: reply.CreatedTime,
|
|
||||||
ReplyUser: reply.ReplyUser,
|
|
||||||
ReplyId: reply.ReplyId,
|
|
||||||
ReplyTo: reply.ReplyTo,
|
|
||||||
Author: reply.Author,
|
|
||||||
Location: reply.Location,
|
|
||||||
Browser: reply.Browser,
|
|
||||||
OperatingSystem: reply.OperatingSystem,
|
|
||||||
Images: imagesBase64,
|
|
||||||
IsLiked: likeMap[reply.Id],
|
|
||||||
}
|
|
||||||
replyChannel <- commentContent // 发送到通道
|
|
||||||
}(*reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(replyChannel) // 关闭通道
|
|
||||||
}()
|
|
||||||
|
|
||||||
var repliesWithImages []CommentContent
|
|
||||||
for replyContent := range replyChannel {
|
|
||||||
repliesWithImages = append(repliesWithImages, replyContent) // 从通道获取回复内容
|
|
||||||
}
|
|
||||||
|
|
||||||
response := CommentResponse{
|
|
||||||
Comments: repliesWithImages,
|
|
||||||
Size: page.Size,
|
|
||||||
Current: page.Current,
|
|
||||||
Total: page.Total,
|
|
||||||
}
|
|
||||||
result.OkWithData(response, c)
|
result.OkWithData(response, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -764,44 +283,11 @@ func (CommentController) CommentLikes(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
res := commentReplyService.CommentLikeService(likeRequest.UserID, likeRequest.CommentId, likeRequest.TopicId)
|
||||||
mx.Lock()
|
if !res {
|
||||||
defer mx.Unlock()
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentLikeFailed"), c)
|
||||||
|
|
||||||
likes := model.ScaCommentLikes{
|
|
||||||
CommentId: likeRequest.CommentId,
|
|
||||||
UserId: likeRequest.UserID,
|
|
||||||
TopicId: likeRequest.TopicId,
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
res := global.DB.Create(&likes) // 假设这是插入数据库的方法
|
|
||||||
if res.Error != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
global.LOG.Errorln(res.Error)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 异步更新点赞计数
|
|
||||||
go func() {
|
|
||||||
if err = commentReplyService.UpdateCommentLikesCountService(likeRequest.CommentId, likeRequest.TopicId); err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
marshal, err := json.Marshal(likes)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mq.CommentLikeProducer(marshal)
|
|
||||||
|
|
||||||
tx.Commit()
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentLikeSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentLikeSuccess"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -820,34 +306,11 @@ func (CommentController) CancelCommentLikes(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mx.Lock()
|
res := commentReplyService.CommentDislikeService(cancelLikeRequest.UserID, cancelLikeRequest.CommentId, cancelLikeRequest.TopicId)
|
||||||
defer mx.Unlock()
|
if !res {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CommentDislikeFailed"), c)
|
||||||
tx := global.DB.Begin()
|
return
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
query, u := gplus.NewQuery[model.ScaCommentLikes]()
|
|
||||||
query.Eq(&u.CommentId, cancelLikeRequest.CommentId).
|
|
||||||
Eq(&u.UserId, cancelLikeRequest.UserID).
|
|
||||||
Eq(&u.TopicId, cancelLikeRequest.TopicId)
|
|
||||||
|
|
||||||
res := gplus.Delete[model.ScaCommentLikes](query)
|
|
||||||
if res.Error != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return // 返回错误而非打印
|
|
||||||
}
|
}
|
||||||
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentDislikeSuccess"), c)
|
||||||
// 异步更新点赞计数
|
|
||||||
go func() {
|
|
||||||
if err := commentReplyService.DecrementCommentLikesCountService(cancelLikeRequest.CommentId, cancelLikeRequest.TopicId); err != nil {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
tx.Commit()
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CommentLikeCancelSuccess"), c)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package comment_controller
|
package comment_controller
|
||||||
|
|
||||||
|
// CommentRequest 评论请求参数
|
||||||
type CommentRequest struct {
|
type CommentRequest struct {
|
||||||
Content string `json:"content" binding:"required"`
|
Content string `json:"content" binding:"required"`
|
||||||
Images []string `json:"images"`
|
Images []string `json:"images"`
|
||||||
@@ -9,6 +10,8 @@ type CommentRequest struct {
|
|||||||
Key string `json:"key" binding:"required"`
|
Key string `json:"key" binding:"required"`
|
||||||
Point []int64 `json:"point" binding:"required"`
|
Point []int64 `json:"point" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplyCommentRequest 回复评论请求参数
|
||||||
type ReplyCommentRequest struct {
|
type ReplyCommentRequest struct {
|
||||||
Content string `json:"content" binding:"required"`
|
Content string `json:"content" binding:"required"`
|
||||||
Images []string `json:"images"`
|
Images []string `json:"images"`
|
||||||
@@ -21,6 +24,7 @@ type ReplyCommentRequest struct {
|
|||||||
Point []int64 `json:"point" binding:"required"`
|
Point []int64 `json:"point" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplyReplyRequest 回复回复请求参数
|
||||||
type ReplyReplyRequest struct {
|
type ReplyReplyRequest struct {
|
||||||
Content string `json:"content" binding:"required"`
|
Content string `json:"content" binding:"required"`
|
||||||
Images []string `json:"images"`
|
Images []string `json:"images"`
|
||||||
@@ -34,6 +38,7 @@ type ReplyReplyRequest struct {
|
|||||||
Point []int64 `json:"point" binding:"required"`
|
Point []int64 `json:"point" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommentListRequest 评论列表请求参数
|
||||||
type CommentListRequest struct {
|
type CommentListRequest struct {
|
||||||
UserID string `json:"user_id" binding:"required"`
|
UserID string `json:"user_id" binding:"required"`
|
||||||
TopicId string `json:"topic_id" binding:"required"`
|
TopicId string `json:"topic_id" binding:"required"`
|
||||||
@@ -41,6 +46,8 @@ type CommentListRequest struct {
|
|||||||
Size int `json:"size" default:"5"`
|
Size int `json:"size" default:"5"`
|
||||||
IsHot bool `json:"is_hot" default:"true"`
|
IsHot bool `json:"is_hot" default:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplyListRequest 回复列表请求参数
|
||||||
type ReplyListRequest struct {
|
type ReplyListRequest struct {
|
||||||
UserID string `json:"user_id" binding:"required"`
|
UserID string `json:"user_id" binding:"required"`
|
||||||
TopicId string `json:"topic_id" binding:"required"`
|
TopicId string `json:"topic_id" binding:"required"`
|
||||||
@@ -48,6 +55,8 @@ type ReplyListRequest struct {
|
|||||||
Page int `json:"page" default:"1"`
|
Page int `json:"page" default:"1"`
|
||||||
Size int `json:"size" default:"5"`
|
Size int `json:"size" default:"5"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommentLikeRequest 点赞评论的请求参数
|
||||||
type CommentLikeRequest struct {
|
type CommentLikeRequest struct {
|
||||||
TopicId string `json:"topic_id" binding:"required"`
|
TopicId string `json:"topic_id" binding:"required"`
|
||||||
CommentId int64 `json:"comment_id" binding:"required"`
|
CommentId int64 `json:"comment_id" binding:"required"`
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
package permission_controller
|
|
||||||
|
|
||||||
type PermissionController struct{}
|
|
@@ -8,6 +8,8 @@ import (
|
|||||||
"schisandra-cloud-album/service/impl"
|
"schisandra-cloud-album/service/impl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PermissionController struct{}
|
||||||
|
|
||||||
var permissionService = impl.PermissionServiceImpl{}
|
var permissionService = impl.PermissionServiceImpl{}
|
||||||
|
|
||||||
// AddPermissions 批量添加权限
|
// AddPermissions 批量添加权限
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
package role_controller
|
|
||||||
|
|
||||||
type RoleController struct{}
|
|
@@ -9,6 +9,8 @@ import (
|
|||||||
"schisandra-cloud-album/service/impl"
|
"schisandra-cloud-album/service/impl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type RoleController struct{}
|
||||||
|
|
||||||
var roleService = impl.RoleServiceImpl{}
|
var roleService = impl.RoleServiceImpl{}
|
||||||
|
|
||||||
// CreateRole 创建角色
|
// CreateRole 创建角色
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
package sms_controller
|
|
||||||
|
|
||||||
type SmsController struct{}
|
|
@@ -15,6 +15,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SmsController struct{}
|
||||||
|
|
||||||
// SendMessageByAli 发送短信验证码
|
// SendMessageByAli 发送短信验证码
|
||||||
// @Summary 发送短信验证码
|
// @Summary 发送短信验证码
|
||||||
// @Description 发送短信验证码
|
// @Description 发送短信验证码
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package user_controller
|
package user_controller
|
||||||
|
|
||||||
import "encoding/json"
|
|
||||||
|
|
||||||
// RefreshTokenRequest 刷新token请求
|
// RefreshTokenRequest 刷新token请求
|
||||||
type RefreshTokenRequest struct {
|
type RefreshTokenRequest struct {
|
||||||
RefreshToken string `json:"refresh_token" binding:"required"`
|
RefreshToken string `json:"refresh_token" binding:"required"`
|
||||||
@@ -37,19 +35,3 @@ type ResetPasswordRequest struct {
|
|||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
Repassword string `json:"repassword" binding:"required"`
|
Repassword string `json:"repassword" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseData 返回数据
|
|
||||||
type ResponseData struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
ExpiresAt int64 `json:"expires_at"`
|
|
||||||
UID *string `json:"uid"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res ResponseData) MarshalBinary() ([]byte, error) {
|
|
||||||
return json.Marshal(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res ResponseData) UnmarshalBinary(data []byte) error {
|
|
||||||
return json.Unmarshal(data, &res)
|
|
||||||
}
|
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
package user_controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"schisandra-cloud-album/service/impl"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserController struct{}
|
|
||||||
|
|
||||||
var mu sync.Mutex
|
|
||||||
var userService = impl.UserServiceImpl{}
|
|
||||||
var userDeviceService = impl.UserDeviceServiceImpl{}
|
|
@@ -13,11 +13,18 @@ import (
|
|||||||
"schisandra-cloud-album/common/result"
|
"schisandra-cloud-album/common/result"
|
||||||
"schisandra-cloud-album/global"
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/model"
|
"schisandra-cloud-album/model"
|
||||||
|
"schisandra-cloud-album/service/impl"
|
||||||
"schisandra-cloud-album/utils"
|
"schisandra-cloud-album/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UserController struct{}
|
||||||
|
|
||||||
|
var mu sync.Mutex
|
||||||
|
var userService = impl.UserServiceImpl{}
|
||||||
|
var userDeviceService = impl.UserDeviceServiceImpl{}
|
||||||
|
|
||||||
// GetUserList
|
// GetUserList
|
||||||
// @Summary 获取所有用户列表
|
// @Summary 获取所有用户列表
|
||||||
// @Tags 用户模块
|
// @Tags 用户模块
|
||||||
@@ -258,31 +265,8 @@ func (UserController) RefreshHandler(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refreshToken := request.RefreshToken
|
data, res := userService.RefreshTokenService(request.RefreshToken)
|
||||||
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
|
if !res {
|
||||||
if err != nil || !isUpd {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID
|
|
||||||
token, err := redis.Get(tokenKey).Result()
|
|
||||||
if err != nil || token == "" {
|
|
||||||
global.LOG.Errorln(err)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ResponseData{
|
|
||||||
AccessToken: accessTokenString,
|
|
||||||
RefreshToken: refreshToken,
|
|
||||||
UID: parseRefreshToken.UserID,
|
|
||||||
}
|
|
||||||
if err := redis.Set(tokenKey, data, time.Hour*24*7).Err(); err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -329,11 +313,6 @@ func (UserController) ResetPassword(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := tx.Error; err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "DatabaseError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
|
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
|
||||||
if code == "" {
|
if code == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
@@ -353,7 +332,7 @@ func (UserController) ResetPassword(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := userService.QueryUserByPhoneService(phone)
|
user := userService.QueryUserByPhoneService(phone)
|
||||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
if user.ID == 0 {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WebsocketController struct {
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PingInterval = 5 * time.Second // 客户端心跳间隔
|
PingInterval = 5 * time.Second // 客户端心跳间隔
|
||||||
HeartbeatWaitTimeout = 10 * time.Second // 心跳等待超时时间
|
HeartbeatWaitTimeout = 10 * time.Second // 心跳等待超时时间
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
package websocket_controller
|
|
||||||
|
|
||||||
type WebsocketController struct {
|
|
||||||
}
|
|
@@ -1,10 +1,19 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import "schisandra-cloud-album/model"
|
import (
|
||||||
|
"schisandra-cloud-album/model"
|
||||||
|
"schisandra-cloud-album/service/impl"
|
||||||
|
)
|
||||||
|
|
||||||
type CommentReplyService interface {
|
type CommentReplyService interface {
|
||||||
CreateCommentReplyService(comment *model.ScaCommentReply) error
|
// GetCommentReplyService 获取评论回复
|
||||||
UpdateCommentReplyCountService(replyId int64) error
|
GetCommentReplyService(uid string, topicId string, commentId int64, pageNum int, size int) *impl.CommentResponse
|
||||||
UpdateCommentLikesCountService(commentId int64, topicId string) error
|
// GetCommentListService 获取评论列表
|
||||||
DecrementCommentLikesCountService(commentId int64, topicId string) error
|
GetCommentListService(uid string, topicId string, pageNum int, size int, isHot bool) *impl.CommentResponse
|
||||||
|
// CommentLikeService 评论点赞
|
||||||
|
CommentLikeService(uid string, commentId int64, topicId string) bool
|
||||||
|
// CommentDislikeService 评论取消点赞
|
||||||
|
CommentDislikeService(uid string, commentId int64, topicId string) bool
|
||||||
|
// SubmitCommentService 提交评论
|
||||||
|
SubmitCommentService(comment *model.ScaCommentReply, topicId string, uid string, images []string) bool
|
||||||
}
|
}
|
||||||
|
@@ -1,32 +1,518 @@
|
|||||||
package impl
|
package impl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/acmestack/gorm-plus/gplus"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"schisandra-cloud-album/common/enum"
|
||||||
"schisandra-cloud-album/dao/impl"
|
"schisandra-cloud-album/dao/impl"
|
||||||
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/model"
|
"schisandra-cloud-album/model"
|
||||||
|
"schisandra-cloud-album/utils"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commentReplyDao = impl.CommentReplyDaoImpl{}
|
var commentReplyDao = impl.CommentReplyDaoImpl{}
|
||||||
|
|
||||||
type CommentReplyServiceImpl struct{}
|
type CommentReplyServiceImpl struct{}
|
||||||
|
|
||||||
// CreateCommentReplyService 创建评论回复
|
var wg sync.WaitGroup
|
||||||
func (CommentReplyServiceImpl) CreateCommentReplyService(comment *model.ScaCommentReply) error {
|
var mx sync.Mutex
|
||||||
return commentReplyDao.CreateCommentReply(comment)
|
|
||||||
|
|
||||||
|
// CommentImages 评论图片
|
||||||
|
type CommentImages struct {
|
||||||
|
TopicId string `json:"topic_id" bson:"topic_id" required:"true"`
|
||||||
|
CommentId int64 `json:"comment_id" bson:"comment_id" required:"true"`
|
||||||
|
UserId string `json:"user_id" bson:"user_id" required:"true"`
|
||||||
|
Images [][]byte `json:"images" bson:"images" required:"true"`
|
||||||
|
CreatedAt string `json:"created_at" bson:"created_at" required:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCommentReplyCountService 更新评论回复数
|
// CommentContent 评论内容
|
||||||
func (CommentReplyServiceImpl) UpdateCommentReplyCountService(replyId int64) error {
|
type CommentContent struct {
|
||||||
return commentReplyDao.UpdateCommentReplyCount(replyId)
|
NickName string `json:"nickname"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
Level int `json:"level,omitempty"`
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
TopicId string `json:"topic_id"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
ReplyTo int64 `json:"reply_to,omitempty"`
|
||||||
|
ReplyId int64 `json:"reply_id,omitempty"`
|
||||||
|
ReplyUser string `json:"reply_user,omitempty"`
|
||||||
|
ReplyUsername string `json:"reply_username,omitempty"`
|
||||||
|
Author int `json:"author"`
|
||||||
|
Likes int64 `json:"likes"`
|
||||||
|
ReplyCount int64 `json:"reply_count"`
|
||||||
|
CreatedTime time.Time `json:"created_time"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Browser string `json:"browser"`
|
||||||
|
OperatingSystem string `json:"operating_system"`
|
||||||
|
IsLiked bool `json:"is_liked" default:"false"`
|
||||||
|
Images []string `json:"images,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCommentLikesCountService 更新评论点赞数
|
// CommentResponse 评论返回值
|
||||||
func (CommentReplyServiceImpl) UpdateCommentLikesCountService(commentId int64, topicId string) error {
|
type CommentResponse struct {
|
||||||
return commentReplyDao.UpdateCommentLikesCount(commentId, topicId)
|
Size int `json:"size"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Current int `json:"current"`
|
||||||
|
Comments []CommentContent `json:"comments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecrementCommentLikesCountService 减少评论点赞数
|
// SubmitCommentService 提交评论
|
||||||
func (CommentReplyServiceImpl) DecrementCommentLikesCountService(commentId int64, topicId string) error {
|
func (CommentReplyServiceImpl) SubmitCommentService(comment *model.ScaCommentReply, topicId string, uid string, images []string) bool {
|
||||||
return commentReplyDao.DecrementCommentLikesCount(commentId, topicId)
|
tx := global.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
errCh := make(chan error, 2)
|
||||||
|
go func() {
|
||||||
|
errCh <- commentReplyDao.CreateCommentReply(comment)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
errCh <- commentReplyDao.UpdateCommentReplyCount(comment.ReplyId)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := <-errCh; err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
tx.Rollback()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(images) > 0 {
|
||||||
|
imagesDataCh := make(chan [][]byte)
|
||||||
|
go func() {
|
||||||
|
imagesData, err := utils.ProcessImages(images)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
imagesDataCh <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imagesDataCh <- imagesData
|
||||||
|
}()
|
||||||
|
|
||||||
|
imagesData := <-imagesDataCh
|
||||||
|
if imagesData == nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
commentImages := CommentImages{
|
||||||
|
TopicId: topicId,
|
||||||
|
CommentId: comment.Id,
|
||||||
|
UserId: uid,
|
||||||
|
Images: imagesData,
|
||||||
|
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理图片保存
|
||||||
|
go func() {
|
||||||
|
if _, err := global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").InsertOne(context.Background(), commentImages); err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommentReplyService 获取评论回复
|
||||||
|
func (CommentReplyServiceImpl) GetCommentReplyService(uid string, topicId string, commentId int64, pageNum int, size int) *CommentResponse {
|
||||||
|
query, u := gplus.NewQuery[model.ScaCommentReply]()
|
||||||
|
page := gplus.NewPage[model.ScaCommentReply](pageNum, size)
|
||||||
|
query.Eq(&u.TopicId, topicId).Eq(&u.ReplyId, commentId).Eq(&u.CommentType, enum.REPLY).OrderByDesc(&u.Likes).OrderByAsc(&u.CreatedTime)
|
||||||
|
page, pageDB := gplus.SelectPage(page, query)
|
||||||
|
if pageDB.Error != nil {
|
||||||
|
global.LOG.Errorln(pageDB.Error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(page.Records) == 0 {
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
userIdsSet := make(map[string]struct{}) // 使用集合去重用户 ID
|
||||||
|
commentIds := make([]int64, 0, len(page.Records))
|
||||||
|
// 收集用户 ID 和评论 ID
|
||||||
|
for _, comment := range page.Records {
|
||||||
|
userIdsSet[comment.UserId] = struct{}{} // 去重
|
||||||
|
commentIds = append(commentIds, comment.Id)
|
||||||
|
if comment.ReplyUser != "" {
|
||||||
|
userIdsSet[comment.ReplyUser] = struct{}{} // 去重
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将用户 ID 转换为切片
|
||||||
|
userIds := make([]string, 0, len(userIdsSet))
|
||||||
|
for userId := range userIdsSet {
|
||||||
|
userIds = append(userIds, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
likeMap := make(map[int64]bool, len(page.Records))
|
||||||
|
commentImagesMap := make(map[int64]CommentImages)
|
||||||
|
userInfoMap := make(map[string]model.ScaAuthUser, len(userIds))
|
||||||
|
|
||||||
|
wg.Add(3)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// 查询评论用户信息
|
||||||
|
queryUser, n := gplus.NewQuery[model.ScaAuthUser]()
|
||||||
|
queryUser.Select(&n.UID, &n.Avatar, &n.Nickname).In(&n.UID, userIds)
|
||||||
|
userInfos, userInfosDB := gplus.SelectList(queryUser)
|
||||||
|
if userInfosDB.Error != nil {
|
||||||
|
global.LOG.Errorln(userInfosDB.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, userInfo := range userInfos {
|
||||||
|
userInfoMap[*userInfo.UID] = *userInfo
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// 查询评论点赞状态
|
||||||
|
|
||||||
|
if len(page.Records) > 0 {
|
||||||
|
queryLike, l := gplus.NewQuery[model.ScaCommentLikes]()
|
||||||
|
queryLike.Eq(&l.TopicId, topicId).Eq(&l.UserId, uid).In(&l.CommentId, commentIds)
|
||||||
|
likes, likesDB := gplus.SelectList(queryLike)
|
||||||
|
if likesDB.Error != nil {
|
||||||
|
global.LOG.Errorln(likesDB.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, like := range likes {
|
||||||
|
likeMap[like.CommentId] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) // 设置超时,2秒
|
||||||
|
defer cancel()
|
||||||
|
cursor, err := global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").Find(ctx, bson.M{"comment_id": bson.M{"$in": commentIds}})
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("Failed to get images for comments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func(cursor *mongo.Cursor, ctx context.Context) {
|
||||||
|
warn := cursor.Close(ctx)
|
||||||
|
if warn != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(cursor, ctx)
|
||||||
|
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var commentImages CommentImages
|
||||||
|
if e := cursor.Decode(&commentImages); e != nil {
|
||||||
|
global.LOG.Errorf("Failed to decode comment images: %v", e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
commentImagesMap[commentImages.CommentId] = commentImages
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
replyChannel := make(chan CommentContent, len(page.Records)) // 使用通道传递回复内容
|
||||||
|
|
||||||
|
for _, reply := range page.Records {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(reply model.ScaCommentReply) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var imagesBase64 []string
|
||||||
|
if imgData, ok := commentImagesMap[reply.Id]; ok {
|
||||||
|
// 将图片转换为base64
|
||||||
|
for _, img := range imgData.Images {
|
||||||
|
mimeType := utils.GetMimeType(img)
|
||||||
|
base64Img := base64.StdEncoding.EncodeToString(img)
|
||||||
|
base64WithPrefix := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Img)
|
||||||
|
imagesBase64 = append(imagesBase64, base64WithPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userInfo, exist := userInfoMap[reply.UserId]
|
||||||
|
if !exist {
|
||||||
|
global.LOG.Errorf("Failed to get user info for comment: %s", reply.UserId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replyUserInfo, e := userInfoMap[reply.ReplyUser]
|
||||||
|
if !e {
|
||||||
|
global.LOG.Errorf("Failed to get reply user info for comment: %s", reply.ReplyUser)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commentContent := CommentContent{
|
||||||
|
Avatar: *userInfo.Avatar,
|
||||||
|
NickName: *userInfo.Nickname,
|
||||||
|
Id: reply.Id,
|
||||||
|
UserId: reply.UserId,
|
||||||
|
TopicId: reply.TopicId,
|
||||||
|
Content: reply.Content,
|
||||||
|
ReplyUsername: *replyUserInfo.Nickname,
|
||||||
|
ReplyCount: reply.ReplyCount,
|
||||||
|
Likes: reply.Likes,
|
||||||
|
CreatedTime: reply.CreatedTime,
|
||||||
|
ReplyUser: reply.ReplyUser,
|
||||||
|
ReplyId: reply.ReplyId,
|
||||||
|
ReplyTo: reply.ReplyTo,
|
||||||
|
Author: reply.Author,
|
||||||
|
Location: reply.Location,
|
||||||
|
Browser: reply.Browser,
|
||||||
|
OperatingSystem: reply.OperatingSystem,
|
||||||
|
Images: imagesBase64,
|
||||||
|
IsLiked: likeMap[reply.Id],
|
||||||
|
}
|
||||||
|
replyChannel <- commentContent // 发送到通道
|
||||||
|
}(*reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(replyChannel) // 关闭通道
|
||||||
|
}()
|
||||||
|
|
||||||
|
var repliesWithImages []CommentContent
|
||||||
|
for replyContent := range replyChannel {
|
||||||
|
repliesWithImages = append(repliesWithImages, replyContent) // 从通道获取回复内容
|
||||||
|
}
|
||||||
|
|
||||||
|
response := CommentResponse{
|
||||||
|
Comments: repliesWithImages,
|
||||||
|
Size: page.Size,
|
||||||
|
Current: page.Current,
|
||||||
|
Total: page.Total,
|
||||||
|
}
|
||||||
|
return &response
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommentListService 评论列表
|
||||||
|
func (CommentReplyServiceImpl) GetCommentListService(uid string, topicId string, pageNum int, size int, isHot bool) *CommentResponse {
|
||||||
|
// 查询评论列表
|
||||||
|
query, u := gplus.NewQuery[model.ScaCommentReply]()
|
||||||
|
page := gplus.NewPage[model.ScaCommentReply](pageNum, size)
|
||||||
|
if isHot {
|
||||||
|
query.OrderByDesc(&u.CommentOrder).OrderByDesc(&u.Likes).OrderByDesc(&u.ReplyCount)
|
||||||
|
} else {
|
||||||
|
query.OrderByDesc(&u.CommentOrder).OrderByDesc(&u.CreatedTime)
|
||||||
|
}
|
||||||
|
query.Eq(&u.TopicId, topicId).Eq(&u.CommentType, enum.COMMENT)
|
||||||
|
page, pageDB := gplus.SelectPage(page, query)
|
||||||
|
if pageDB.Error != nil {
|
||||||
|
global.LOG.Errorln(pageDB.Error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(page.Records) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
userIds := make([]string, 0, len(page.Records))
|
||||||
|
commentIds := make([]int64, 0, len(page.Records))
|
||||||
|
for _, comment := range page.Records {
|
||||||
|
userIds = append(userIds, comment.UserId)
|
||||||
|
commentIds = append(commentIds, comment.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结果存储
|
||||||
|
userInfoMap := make(map[string]model.ScaAuthUser)
|
||||||
|
likeMap := make(map[int64]bool)
|
||||||
|
commentImagesMap := make(map[int64]CommentImages)
|
||||||
|
|
||||||
|
// 使用 WaitGroup 等待协程完成
|
||||||
|
wg.Add(3)
|
||||||
|
|
||||||
|
// 查询评论用户信息
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
queryUser, n := gplus.NewQuery[model.ScaAuthUser]()
|
||||||
|
queryUser.Select(&n.UID, &n.Avatar, &n.Nickname).In(&n.UID, userIds)
|
||||||
|
userInfos, userInfosDB := gplus.SelectList(queryUser)
|
||||||
|
if userInfosDB.Error != nil {
|
||||||
|
global.LOG.Errorln(userInfosDB.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, userInfo := range userInfos {
|
||||||
|
userInfoMap[*userInfo.UID] = *userInfo
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 查询评论点赞状态
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if len(page.Records) > 0 {
|
||||||
|
queryLike, l := gplus.NewQuery[model.ScaCommentLikes]()
|
||||||
|
queryLike.Eq(&l.TopicId, topicId).Eq(&l.UserId, uid).In(&l.CommentId, commentIds)
|
||||||
|
likes, likesDB := gplus.SelectList(queryLike)
|
||||||
|
if likesDB.Error != nil {
|
||||||
|
global.LOG.Errorln(likesDB.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, like := range likes {
|
||||||
|
likeMap[like.CommentId] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 查询评论图片信息
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // 设置超时,2秒
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cursor, err := global.MongoDB.Database(global.CONFIG.MongoDB.DB).Collection("comment_images").Find(ctx, bson.M{"comment_id": bson.M{"$in": commentIds}})
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("Failed to get images for comments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func(cursor *mongo.Cursor, ctx context.Context) {
|
||||||
|
err := cursor.Close(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(cursor, ctx)
|
||||||
|
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var commentImages CommentImages
|
||||||
|
if err = cursor.Decode(&commentImages); err != nil {
|
||||||
|
global.LOG.Errorf("Failed to decode comment images: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
commentImagesMap[commentImages.CommentId] = commentImages
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待所有查询完成
|
||||||
|
wg.Wait()
|
||||||
|
commentChannel := make(chan CommentContent, len(page.Records))
|
||||||
|
|
||||||
|
for _, comment := range page.Records {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(comment model.ScaCommentReply) {
|
||||||
|
defer wg.Done()
|
||||||
|
// 将图片转换为base64
|
||||||
|
var imagesBase64 []string
|
||||||
|
if imgData, ok := commentImagesMap[comment.Id]; ok {
|
||||||
|
// 将图片转换为base64
|
||||||
|
for _, img := range imgData.Images {
|
||||||
|
mimeType := utils.GetMimeType(img)
|
||||||
|
base64Img := base64.StdEncoding.EncodeToString(img)
|
||||||
|
base64WithPrefix := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Img)
|
||||||
|
imagesBase64 = append(imagesBase64, base64WithPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo, exist := userInfoMap[comment.UserId]
|
||||||
|
if !exist {
|
||||||
|
global.LOG.Errorf("Failed to get user info for comment: %s", comment.UserId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commentContent := CommentContent{
|
||||||
|
Avatar: *userInfo.Avatar,
|
||||||
|
NickName: *userInfo.Nickname,
|
||||||
|
Id: comment.Id,
|
||||||
|
UserId: comment.UserId,
|
||||||
|
TopicId: comment.TopicId,
|
||||||
|
Content: comment.Content,
|
||||||
|
ReplyCount: comment.ReplyCount,
|
||||||
|
Likes: comment.Likes,
|
||||||
|
CreatedTime: comment.CreatedTime,
|
||||||
|
Author: comment.Author,
|
||||||
|
Location: comment.Location,
|
||||||
|
Browser: comment.Browser,
|
||||||
|
OperatingSystem: comment.OperatingSystem,
|
||||||
|
Images: imagesBase64,
|
||||||
|
IsLiked: likeMap[comment.Id],
|
||||||
|
}
|
||||||
|
commentChannel <- commentContent
|
||||||
|
}(*comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(commentChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var commentsWithImages []CommentContent
|
||||||
|
for commentContent := range commentChannel {
|
||||||
|
commentsWithImages = append(commentsWithImages, commentContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := CommentResponse{
|
||||||
|
Comments: commentsWithImages,
|
||||||
|
Size: page.Size,
|
||||||
|
Current: page.Current,
|
||||||
|
Total: page.Total,
|
||||||
|
}
|
||||||
|
return &response
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentLikeService 评论点赞
|
||||||
|
func (CommentReplyServiceImpl) CommentLikeService(uid string, commentId int64, topicId string) bool {
|
||||||
|
mx.Lock()
|
||||||
|
defer mx.Unlock()
|
||||||
|
likes := model.ScaCommentLikes{
|
||||||
|
CommentId: commentId,
|
||||||
|
UserId: uid,
|
||||||
|
TopicId: topicId,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := global.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
res := global.DB.Create(&likes) // 假设这是插入数据库的方法
|
||||||
|
if res.Error != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
global.LOG.Errorln(res.Error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步更新点赞计数
|
||||||
|
go func() {
|
||||||
|
if err := commentReplyDao.UpdateCommentLikesCount(commentId, topicId); err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
tx.Commit()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentDislikeService 取消评论点赞
|
||||||
|
func (CommentReplyServiceImpl) CommentDislikeService(uid string, commentId int64, topicId string) bool {
|
||||||
|
mx.Lock()
|
||||||
|
defer mx.Unlock()
|
||||||
|
|
||||||
|
tx := global.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
query, u := gplus.NewQuery[model.ScaCommentLikes]()
|
||||||
|
query.Eq(&u.CommentId, commentId).
|
||||||
|
Eq(&u.UserId, uid).
|
||||||
|
Eq(&u.TopicId, topicId)
|
||||||
|
|
||||||
|
res := gplus.Delete[model.ScaCommentLikes](query)
|
||||||
|
if res.Error != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步更新点赞计数
|
||||||
|
go func() {
|
||||||
|
if err := commentReplyDao.DecrementCommentLikesCount(commentId, topicId); err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
tx.Commit()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,36 @@
|
|||||||
package impl
|
package impl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"schisandra-cloud-album/common/constant"
|
||||||
|
"schisandra-cloud-album/common/redis"
|
||||||
"schisandra-cloud-album/dao/impl"
|
"schisandra-cloud-album/dao/impl"
|
||||||
|
"schisandra-cloud-album/global"
|
||||||
"schisandra-cloud-album/model"
|
"schisandra-cloud-album/model"
|
||||||
|
"schisandra-cloud-album/utils"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var userDao = impl.UserDaoImpl{}
|
var userDao = impl.UserDaoImpl{}
|
||||||
|
|
||||||
type UserServiceImpl struct{}
|
type UserServiceImpl struct{}
|
||||||
|
|
||||||
|
// ResponseData 返回数据
|
||||||
|
type ResponseData struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
UID *string `json:"uid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ResponseData) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ResponseData) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, &res)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserListService 返回用户列表
|
// GetUserListService 返回用户列表
|
||||||
func (UserServiceImpl) GetUserListService() []*model.ScaAuthUser {
|
func (UserServiceImpl) GetUserListService() []*model.ScaAuthUser {
|
||||||
return userDao.GetUserList()
|
return userDao.GetUserList()
|
||||||
@@ -52,3 +74,32 @@ func (UserServiceImpl) AddUserService(user model.ScaAuthUser) (*model.ScaAuthUse
|
|||||||
func (UserServiceImpl) UpdateUserService(phone, encrypt string) error {
|
func (UserServiceImpl) UpdateUserService(phone, encrypt string) error {
|
||||||
return userDao.UpdateUser(phone, encrypt)
|
return userDao.UpdateUser(phone, encrypt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshTokenService 刷新用户token
|
||||||
|
func (UserServiceImpl) RefreshTokenService(refreshToken string) (*ResponseData, bool) {
|
||||||
|
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
|
||||||
|
if err != nil || !isUpd {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID
|
||||||
|
token, err := redis.Get(tokenKey).Result()
|
||||||
|
if err != nil || token == "" {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
data := ResponseData{
|
||||||
|
AccessToken: accessTokenString,
|
||||||
|
RefreshToken: refreshToken,
|
||||||
|
UID: parseRefreshToken.UserID,
|
||||||
|
}
|
||||||
|
if err = redis.Set(tokenKey, data, time.Hour*24*7).Err(); err != nil {
|
||||||
|
global.LOG.Errorln(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &data, true
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import "schisandra-cloud-album/model"
|
import (
|
||||||
|
"schisandra-cloud-album/model"
|
||||||
|
"schisandra-cloud-album/service/impl"
|
||||||
|
)
|
||||||
|
|
||||||
type UserService interface {
|
type UserService interface {
|
||||||
// GetUserListService 返回用户列表
|
// GetUserListService 返回用户列表
|
||||||
@@ -19,4 +22,6 @@ type UserService interface {
|
|||||||
AddUserService(user model.ScaAuthUser) (*model.ScaAuthUser, error)
|
AddUserService(user model.ScaAuthUser) (*model.ScaAuthUser, error)
|
||||||
// UpdateUserService 更新用户信息
|
// UpdateUserService 更新用户信息
|
||||||
UpdateUserService(phone, encrypt string) error
|
UpdateUserService(phone, encrypt string) error
|
||||||
|
// RefreshTokenService 刷新token
|
||||||
|
RefreshTokenService(refreshToken string) (*impl.ResponseData, bool)
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package comment_controller
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@@ -6,50 +6,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// base64ToBytes 将base64字符串转换为字节数组
|
var wg sync.WaitGroup
|
||||||
func base64ToBytes(base64Str string) ([]byte, error) {
|
|
||||||
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64Str))
|
|
||||||
data, err := io.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to decode base64 string")
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// processImages 处理图片,将 base64 字符串转换为字节数组
|
// GetMimeType 获取 MIME 类型
|
||||||
func processImages(images []string) ([][]byte, error) {
|
func GetMimeType(data []byte) string {
|
||||||
var imagesData [][]byte
|
|
||||||
dataChan := make(chan []byte, len(images)) // 创建一个带缓冲的 channel
|
|
||||||
re := regexp.MustCompile(`^data:image/\w+;base64,`)
|
|
||||||
|
|
||||||
for _, img := range images {
|
|
||||||
wg.Add(1) // 增加 WaitGroup 的计数
|
|
||||||
go func(img string) {
|
|
||||||
defer wg.Done() // 函数结束时减少计数
|
|
||||||
|
|
||||||
imgWithoutPrefix := re.ReplaceAllString(img, "")
|
|
||||||
data, err := base64ToBytes(imgWithoutPrefix)
|
|
||||||
if err != nil {
|
|
||||||
return // 出错时直接返回
|
|
||||||
}
|
|
||||||
dataChan <- data // 将结果发送到 channel
|
|
||||||
}(img)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait() // 等待所有 goroutine 完成
|
|
||||||
close(dataChan) // 关闭 channel
|
|
||||||
|
|
||||||
for data := range dataChan { // 收集所有结果
|
|
||||||
imagesData = append(imagesData, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return imagesData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMimeType 获取 MIME 类型
|
|
||||||
func getMimeType(data []byte) string {
|
|
||||||
if len(data) < 4 {
|
if len(data) < 4 {
|
||||||
return "application/octet-stream" // 默认类型
|
return "application/octet-stream" // 默认类型
|
||||||
}
|
}
|
||||||
@@ -85,3 +48,43 @@ func getMimeType(data []byte) string {
|
|||||||
|
|
||||||
return "application/octet-stream" // 默认类型
|
return "application/octet-stream" // 默认类型
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessImages 处理图片,将 base64 字符串转换为字节数组
|
||||||
|
func ProcessImages(images []string) ([][]byte, error) {
|
||||||
|
var imagesData [][]byte
|
||||||
|
dataChan := make(chan []byte, len(images)) // 创建一个带缓冲的 channel
|
||||||
|
re := regexp.MustCompile(`^data:image/\w+;base64,`)
|
||||||
|
|
||||||
|
for _, img := range images {
|
||||||
|
wg.Add(1) // 增加 WaitGroup 的计数
|
||||||
|
go func(img string) {
|
||||||
|
defer wg.Done() // 函数结束时减少计数
|
||||||
|
|
||||||
|
imgWithoutPrefix := re.ReplaceAllString(img, "")
|
||||||
|
data, err := Base64ToBytes(imgWithoutPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return // 出错时直接返回
|
||||||
|
}
|
||||||
|
dataChan <- data // 将结果发送到 channel
|
||||||
|
}(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait() // 等待所有 goroutine 完成
|
||||||
|
close(dataChan) // 关闭 channel
|
||||||
|
|
||||||
|
for data := range dataChan { // 收集所有结果
|
||||||
|
imagesData = append(imagesData, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagesData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64ToBytes 将base64字符串转换为字节数组
|
||||||
|
func Base64ToBytes(base64Str string) ([]byte, error) {
|
||||||
|
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64Str))
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to decode base64 string")
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
Reference in New Issue
Block a user