mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 11:06:23 +00:00
✨ 优化了评论分页加载的列表显示
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 15s
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 15s
This commit is contained in:
@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/cloudwego/hertz/pkg/common/utils"
|
||||
"github.com/snowykami/neo-blog/internal/ctxutils"
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"github.com/snowykami/neo-blog/internal/service"
|
||||
@ -30,13 +31,13 @@ func (cc *CommentController) CreateComment(ctx context.Context, c *app.RequestCo
|
||||
resps.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
err := cc.service.CreateComment(ctx, &req)
|
||||
commentID, err := cc.service.CreateComment(ctx, &req)
|
||||
if err != nil {
|
||||
serviceErr := errs.AsServiceError(err)
|
||||
resps.Custom(c, serviceErr.Code, serviceErr.Message, nil)
|
||||
return
|
||||
}
|
||||
resps.Ok(c, resps.Success, nil)
|
||||
resps.Ok(c, resps.Success, utils.H{"id": commentID})
|
||||
}
|
||||
|
||||
func (cc *CommentController) UpdateComment(ctx context.Context, c *app.RequestContext) {
|
||||
|
@ -2,6 +2,9 @@ package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/cloudwego/hertz/pkg/common/utils"
|
||||
"github.com/snowykami/neo-blog/internal/ctxutils"
|
||||
@ -10,8 +13,6 @@ import (
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"github.com/snowykami/neo-blog/pkg/errs"
|
||||
"github.com/snowykami/neo-blog/pkg/resps"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PostController struct {
|
||||
|
@ -69,7 +69,7 @@ func (u *UserController) Logout(ctx context.Context, c *app.RequestContext) {
|
||||
ctxutils.ClearTokenAndRefreshTokenCookie(c)
|
||||
resps.Ok(c, resps.Success, nil)
|
||||
// 尝试吊销服务端状态:若用户登录的情况下
|
||||
// TODO: 这里可以添加服务端状态的吊销逻辑
|
||||
// TODO: 添加服务端状态的吊销逻辑
|
||||
}
|
||||
|
||||
func (u *UserController) OidcList(ctx context.Context, c *app.RequestContext) {
|
||||
@ -176,3 +176,11 @@ func (u *UserController) VerifyEmail(ctx context.Context, c *app.RequestContext)
|
||||
}
|
||||
resps.Ok(c, resps.Success, resp)
|
||||
}
|
||||
|
||||
func (u *UserController) ChangePassword(ctx context.Context, c *app.RequestContext) {
|
||||
// TODO: 实现修改密码功能
|
||||
}
|
||||
|
||||
func (u *UserController) ChangeEmail(ctx context.Context, c *app.RequestContext) {
|
||||
// TODO: 实现修改邮箱功能
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Comment struct {
|
||||
gorm.Model
|
||||
|
@ -1,16 +1,16 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"errors"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/snowykami/neo-blog/internal/model"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"github.com/snowykami/neo-blog/pkg/errs"
|
||||
"github.com/snowykami/neo-blog/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
"github.com/snowykami/neo-blog/internal/model"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"github.com/snowykami/neo-blog/pkg/errs"
|
||||
"github.com/snowykami/neo-blog/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CommentRepo struct {
|
||||
@ -21,195 +21,237 @@ var Comment = &CommentRepo{}
|
||||
// 检查设置父评论是否会造成循环引用
|
||||
// 它通过向上遍历潜在父评论的所有祖先来实现
|
||||
func (cr *CommentRepo) isCircularReference(tx *gorm.DB, commentID, parentID uint) (bool, error) {
|
||||
// 如果没有父评论,则不可能有循环
|
||||
if parentID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
// 如果没有父评论,则不可能有循环
|
||||
if parentID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
currentID := parentID
|
||||
for currentID != 0 {
|
||||
// 如果在向上追溯的过程中找到了自己的ID,说明存在循环
|
||||
if currentID == commentID {
|
||||
return true, nil
|
||||
}
|
||||
currentID := parentID
|
||||
for currentID != 0 {
|
||||
// 如果在向上追溯的过程中找到了自己的ID,说明存在循环
|
||||
if currentID == commentID {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
var parent model.Comment
|
||||
if err := tx.Where("id = ?", currentID).First(&parent).Error; err != nil {
|
||||
// 如果祖先链中的某个评论不存在,说明链已经断开,不可能形成循环
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
// 继续向上追溯
|
||||
currentID = parent.ReplyID
|
||||
}
|
||||
var parent model.Comment
|
||||
if err := tx.Where("id = ?", currentID).First(&parent).Error; err != nil {
|
||||
// 如果祖先链中的某个评论不存在,说明链已经断开,不可能形成循环
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
// 继续向上追溯
|
||||
currentID = parent.ReplyID
|
||||
}
|
||||
|
||||
// 已经追溯到树的根节点,没有发现循环
|
||||
return false, nil
|
||||
// 已经追溯到树的根节点,没有发现循环
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 递归删除子评论的辅助函数
|
||||
func (cr *CommentRepo) deleteChildren(tx *gorm.DB, parentID uint) error {
|
||||
var children []*model.Comment
|
||||
// 1. 找到所有直接子评论
|
||||
if err := tx.Where("reply_id = ?", parentID).Find(&children).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var children []*model.Comment
|
||||
// 1. 找到所有直接子评论
|
||||
if err := tx.Where("reply_id = ?", parentID).Find(&children).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 对每一个子评论,递归删除它的子评论
|
||||
for _, child := range children {
|
||||
if err := cr.deleteChildren(tx, child.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 2. 对每一个子评论,递归删除它的子评论
|
||||
for _, child := range children {
|
||||
if err := cr.deleteChildren(tx, child.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 删除当前层级的子评论
|
||||
if err := tx.Where("reply_id = ?", parentID).Delete(&model.Comment{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 3. 删除当前层级的子评论
|
||||
if err := tx.Where("reply_id = ?", parentID).Delete(&model.Comment{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) CreateComment(comment *model.Comment) error {
|
||||
err := GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
depth := 0
|
||||
if comment.ReplyID != 0 {
|
||||
isCircular, err := cr.isCircularReference(tx, comment.ID, comment.ReplyID)
|
||||
if err != nil {
|
||||
return err // 检查过程中发生数据库错误
|
||||
}
|
||||
if isCircular {
|
||||
return errs.New(http.StatusBadRequest, "circular reference detected in comment tree", nil)
|
||||
}
|
||||
var parentComment model.Comment
|
||||
if err := tx.Where("id = ?", comment.ReplyID).First(&parentComment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
parentComment.CommentCount += 1
|
||||
if err := tx.Model(&parentComment).UpdateColumn("CommentCount", parentComment.CommentCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
depth = parentComment.Depth + 1
|
||||
}
|
||||
if depth > utils.Env.GetAsInt(constant.EnvKeyMaxReplyDepth, constant.MaxReplyDepthDefault) {
|
||||
return errs.New(http.StatusBadRequest, "exceeded maximum reply depth", nil)
|
||||
}
|
||||
comment.Depth = depth
|
||||
if err := tx.Create(comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
func (cr *CommentRepo) CreateComment(comment *model.Comment) (uint, error) {
|
||||
var commentID uint
|
||||
err := GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
depth := 0
|
||||
if comment.ReplyID != 0 {
|
||||
isCircular, err := cr.isCircularReference(tx, comment.ID, comment.ReplyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isCircular {
|
||||
return errs.New(http.StatusBadRequest, "circular reference detected in comment tree", nil)
|
||||
}
|
||||
var parentComment model.Comment
|
||||
if err := tx.Where("id = ?", comment.ReplyID).First(&parentComment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
parentComment.CommentCount += 1
|
||||
if err := tx.Model(&parentComment).UpdateColumn("CommentCount", parentComment.CommentCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
depth = parentComment.Depth + 1
|
||||
}
|
||||
if depth > utils.Env.GetAsInt(constant.EnvKeyMaxReplyDepth, constant.MaxReplyDepthDefault) {
|
||||
return errs.New(http.StatusBadRequest, "exceeded maximum reply depth", nil)
|
||||
}
|
||||
comment.Depth = depth
|
||||
if err := tx.Create(comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
commentID = comment.ID // 记录主键
|
||||
switch comment.TargetType {
|
||||
case constant.TargetTypePost:
|
||||
var count int64
|
||||
if err := tx.Model(&model.Comment{}).
|
||||
Where("target_id = ? AND target_type = ?", comment.TargetID, constant.TargetTypePost).
|
||||
Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Post{}).Where("id = ?", comment.TargetID).
|
||||
UpdateColumn("comment_count", count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errs.New(http.StatusBadRequest, "unsupported target type: "+comment.TargetType, nil)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return commentID, err
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) UpdateComment(comment *model.Comment) error {
|
||||
if comment.ID == 0 {
|
||||
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)
|
||||
}
|
||||
if comment.ID == 0 {
|
||||
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)
|
||||
}
|
||||
|
||||
if err := GetDB().Select("IsPrivate", "Content").Updates(comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := GetDB().Select("IsPrivate", "Content").Updates(comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) DeleteComment(commentID string) error {
|
||||
if commentID == "" {
|
||||
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)
|
||||
}
|
||||
if commentID == "" {
|
||||
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)
|
||||
}
|
||||
|
||||
err := GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
var comment model.Comment
|
||||
err := GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
var comment model.Comment
|
||||
|
||||
// 1. 查找主评论
|
||||
if err := tx.Where("id = ?", commentID).First(&comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 1. 查找主评论
|
||||
if err := tx.Where("id = ?", commentID).First(&comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 删除子评论
|
||||
if err := cr.deleteChildren(tx, comment.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 2. 删除子评论
|
||||
if err := cr.deleteChildren(tx, comment.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 删除主评论
|
||||
if err := tx.Delete(&comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 3. 删除主评论
|
||||
if err := tx.Delete(&comment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 更新父评论的回复计数
|
||||
if comment.ReplyID != 0 {
|
||||
var parent model.Comment
|
||||
if err := tx.Where("id = ?", comment.ReplyID).First(&parent).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 4. 更新父评论的回复计数
|
||||
if comment.ReplyID != 0 {
|
||||
var parent model.Comment
|
||||
if err := tx.Where("id = ?", comment.ReplyID).First(&parent).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parent.CommentCount -= 1
|
||||
parent.CommentCount -= 1
|
||||
|
||||
if err := tx.Save(&parent).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Save(&parent).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
// 5. 更新目标的评论数量
|
||||
switch comment.TargetType {
|
||||
case constant.TargetTypePost:
|
||||
var count int64
|
||||
if err := tx.Model(&model.Comment{}).
|
||||
Where("target_id = ? AND target_type = ?", comment.TargetID, constant.TargetTypePost).
|
||||
Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Post{}).Where("id = ?", comment.TargetID).
|
||||
UpdateColumn("comment_count", count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errs.New(http.StatusBadRequest, "unsupported target type: "+comment.TargetType, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) GetComment(commentID string) (*model.Comment, error) {
|
||||
var comment model.Comment
|
||||
if err := GetDB().Where("id = ?", commentID).Preload("User").First(&comment).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &comment, nil
|
||||
var comment model.Comment
|
||||
if err := GetDB().Where("id = ?", commentID).Preload("User").First(&comment).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &comment, nil
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) ListComments(currentUserID, targetID, commentID uint, targetType string, page, size uint64, orderBy string, desc bool, depth int) ([]model.Comment, error) {
|
||||
if !slices.Contains(constant.OrderByEnumComment, orderBy) {
|
||||
return nil, errs.New(http.StatusBadRequest, "invalid order_by parameter", nil)
|
||||
}
|
||||
if !slices.Contains(constant.OrderByEnumComment, orderBy) {
|
||||
return nil, errs.New(http.StatusBadRequest, "invalid order_by parameter", nil)
|
||||
}
|
||||
|
||||
var masterID uint
|
||||
var masterID uint
|
||||
|
||||
if targetType == constant.TargetTypePost {
|
||||
post, err := Post.GetPostByID(strconv.Itoa(int(targetID)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
masterID = post.UserID
|
||||
}
|
||||
if targetType == constant.TargetTypePost {
|
||||
post, err := Post.GetPostByID(strconv.Itoa(int(targetID)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
masterID = post.UserID
|
||||
}
|
||||
|
||||
query := GetDB().Model(&model.Comment{}).Preload("User")
|
||||
query := GetDB().Model(&model.Comment{}).Preload("User")
|
||||
|
||||
if commentID > 0 {
|
||||
query = query.Where("reply_id = ?", commentID)
|
||||
}
|
||||
if commentID > 0 {
|
||||
query = query.Where("reply_id = ?", commentID)
|
||||
}
|
||||
|
||||
if currentUserID > 0 {
|
||||
query = query.Where("(is_private = ? OR (is_private = ? AND (user_id = ? OR user_id = ?)))", false, true, currentUserID, masterID)
|
||||
} else {
|
||||
query = query.Where("is_private = ?", false)
|
||||
}
|
||||
if currentUserID > 0 {
|
||||
query = query.Where("(is_private = ? OR (is_private = ? AND (user_id = ? OR user_id = ?)))", false, true, currentUserID, masterID)
|
||||
} else {
|
||||
query = query.Where("is_private = ?", false)
|
||||
}
|
||||
|
||||
if depth >= 0 {
|
||||
query = query.Where("target_id = ? AND target_type = ? AND depth = ?", targetID, targetType, depth)
|
||||
} else {
|
||||
query = query.Where("target_id = ? AND target_type = ?", targetID, targetType)
|
||||
}
|
||||
if depth >= 0 {
|
||||
query = query.Where("target_id = ? AND target_type = ? AND depth = ?", targetID, targetType, depth)
|
||||
} else {
|
||||
query = query.Where("target_id = ? AND target_type = ?", targetID, targetType)
|
||||
}
|
||||
|
||||
items, _, err := PaginateQuery[model.Comment](query, page, size, orderBy, desc)
|
||||
items, _, err := PaginateQuery[model.Comment](query, page, size, orderBy, desc)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return items, nil
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) CountComments(targetType string, targetID uint) (int64, error) {
|
||||
var count int64
|
||||
err := GetDB().Model(&model.Comment{}).Where("target_id = ? AND target_type = ?", targetID, targetType).Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"errors"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/snowykami/neo-blog/internal/model"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"gorm.io/gorm"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/snowykami/neo-blog/internal/model"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type likeRepo struct{}
|
||||
@ -14,96 +14,96 @@ type likeRepo struct{}
|
||||
var Like = &likeRepo{}
|
||||
|
||||
func (l *likeRepo) ToggleLike(userID, targetID uint, targetType string) (bool, error) {
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var finalStatus bool
|
||||
err = GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
isLiked, err := l.IsLiked(userID, targetID, targetType)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
if isLiked {
|
||||
if err :=
|
||||
tx.Where("target_type = ? AND target_id = ? AND user_id = ?", targetType, targetID, userID).
|
||||
Delete(&model.Like{TargetType: targetType, TargetID: targetID, UserID: userID}).
|
||||
Error; err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
finalStatus = false
|
||||
} else {
|
||||
like := &model.Like{
|
||||
TargetType: targetType,
|
||||
TargetID: targetID,
|
||||
UserID: userID,
|
||||
}
|
||||
if err := tx.Create(like).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
finalStatus = true
|
||||
}
|
||||
// 重新计算点赞数量
|
||||
var count int64
|
||||
if err := tx.Model(&model.Like{}).Where("target_type = ? AND target_id = ?", targetType, targetID).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 更新目标的点赞数量
|
||||
//switch targetType {
|
||||
//case constant.TargetTypePost:
|
||||
// if err := tx.Model(&model.Post{}).Where("id = ?", targetID).UpdateColumn("like_count", count).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
//case constant.TargetTypeComment:
|
||||
// if err := tx.Model(&model.Comment{}).Where("id = ?", targetID).UpdateColumn("like_count", count).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
//default:
|
||||
// return errors.New("invalid target type")
|
||||
//}
|
||||
return nil
|
||||
})
|
||||
return finalStatus, err
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var finalStatus bool
|
||||
err = GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
isLiked, err := l.IsLiked(userID, targetID, targetType)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
if isLiked {
|
||||
if err :=
|
||||
tx.Where("target_type = ? AND target_id = ? AND user_id = ?", targetType, targetID, userID).
|
||||
Delete(&model.Like{TargetType: targetType, TargetID: targetID, UserID: userID}).
|
||||
Error; err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
finalStatus = false
|
||||
} else {
|
||||
like := &model.Like{
|
||||
TargetType: targetType,
|
||||
TargetID: targetID,
|
||||
UserID: userID,
|
||||
}
|
||||
if err := tx.Create(like).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
finalStatus = true
|
||||
}
|
||||
// 重新计算点赞数量
|
||||
var count int64
|
||||
if err := tx.Model(&model.Like{}).Where("target_type = ? AND target_id = ?", targetType, targetID).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 更新目标的点赞数量
|
||||
//switch targetType {
|
||||
//case constant.TargetTypePost:
|
||||
// if err := tx.Model(&model.Post{}).Where("id = ?", targetID).UpdateColumn("like_count", count).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
//case constant.TargetTypeComment:
|
||||
// if err := tx.Model(&model.Comment{}).Where("id = ?", targetID).UpdateColumn("like_count", count).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
//default:
|
||||
// return errors.New("invalid target type")
|
||||
//}
|
||||
return nil
|
||||
})
|
||||
return finalStatus, err
|
||||
}
|
||||
|
||||
// IsLiked 检查是否点赞
|
||||
func (l *likeRepo) IsLiked(userID, targetID uint, targetType string) (bool, error) {
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var like model.Like
|
||||
err = GetDB().Where("target_type = ? AND target_id = ? AND user_id = ?", targetType, targetID, userID).First(&like).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var like model.Like
|
||||
err = GetDB().Where("target_type = ? AND target_id = ? AND user_id = ?", targetType, targetID, userID).First(&like).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Count 点赞计数
|
||||
func (l *likeRepo) Count(targetID uint, targetType string) (int64, error) {
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var count int64
|
||||
err = GetDB().Model(&model.Like{}).Where("target_type = ? AND target_id = ?", targetType, targetID).Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
err := l.checkTargetType(targetType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var count int64
|
||||
err = GetDB().Model(&model.Like{}).Where("target_type = ? AND target_id = ?", targetType, targetID).Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (l *likeRepo) checkTargetType(targetType string) error {
|
||||
switch targetType {
|
||||
case constant.TargetTypePost, constant.TargetTypeComment:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("invalid target type")
|
||||
}
|
||||
switch targetType {
|
||||
case constant.TargetTypePost, constant.TargetTypeComment:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("invalid target type")
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,27 @@
|
||||
package apiv1
|
||||
|
||||
import (
|
||||
"github.com/cloudwego/hertz/pkg/route"
|
||||
"github.com/snowykami/neo-blog/internal/controller/v1"
|
||||
"github.com/snowykami/neo-blog/internal/middleware"
|
||||
"github.com/cloudwego/hertz/pkg/route"
|
||||
"github.com/snowykami/neo-blog/internal/controller/v1"
|
||||
"github.com/snowykami/neo-blog/internal/middleware"
|
||||
)
|
||||
|
||||
func registerUserRoutes(group *route.RouterGroup) {
|
||||
userController := v1.NewUserController()
|
||||
userGroup := group.Group("/user").Use(middleware.UseAuth(true))
|
||||
userGroupWithoutAuth := group.Group("/user").Use(middleware.UseAuth(false))
|
||||
userGroupWithoutAuthNeedsCaptcha := userGroupWithoutAuth.Use(middleware.UseCaptcha())
|
||||
{
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/login", userController.Login)
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/register", userController.Register)
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/email/verify", userController.VerifyEmail) // Send email verification code
|
||||
userGroupWithoutAuth.GET("/oidc/list", userController.OidcList)
|
||||
userGroupWithoutAuth.GET("/oidc/login/:name", userController.OidcLogin)
|
||||
userGroupWithoutAuth.GET("/u/:id", userController.GetUser)
|
||||
userGroup.GET("/me", userController.GetUser)
|
||||
userGroupWithoutAuth.POST("/logout", userController.Logout)
|
||||
userGroup.PUT("/u/:id", userController.UpdateUser)
|
||||
}
|
||||
userController := v1.NewUserController()
|
||||
userGroup := group.Group("/user").Use(middleware.UseAuth(true))
|
||||
userGroupWithoutAuth := group.Group("/user").Use(middleware.UseAuth(false))
|
||||
userGroupWithoutAuthNeedsCaptcha := userGroupWithoutAuth.Use(middleware.UseCaptcha())
|
||||
{
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/login", userController.Login)
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/register", userController.Register)
|
||||
userGroupWithoutAuthNeedsCaptcha.POST("/email/verify", userController.VerifyEmail) // Send email verification code
|
||||
userGroupWithoutAuth.GET("/oidc/list", userController.OidcList)
|
||||
userGroupWithoutAuth.GET("/oidc/login/:name", userController.OidcLogin)
|
||||
userGroupWithoutAuth.GET("/u/:id", userController.GetUser)
|
||||
userGroup.GET("/me", userController.GetUser)
|
||||
userGroupWithoutAuth.POST("/logout", userController.Logout)
|
||||
userGroup.PUT("/u/:id", userController.UpdateUser)
|
||||
userGroup.PUT("/password/edit", userController.ChangePassword)
|
||||
userGroup.PUT("/email/edit", userController.ChangeEmail)
|
||||
}
|
||||
}
|
||||
|
@ -19,17 +19,17 @@ func NewCommentService() *CommentService {
|
||||
return &CommentService{}
|
||||
}
|
||||
|
||||
func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateCommentReq) error {
|
||||
func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateCommentReq) (uint, error) {
|
||||
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
||||
if !ok {
|
||||
return errs.ErrUnauthorized
|
||||
return 0, errs.ErrUnauthorized
|
||||
}
|
||||
|
||||
if ok, err := cs.checkTargetExists(req.TargetID, req.TargetType); !ok {
|
||||
if err != nil {
|
||||
return errs.New(errs.ErrBadRequest.Code, "target not found", err)
|
||||
return 0, errs.New(errs.ErrBadRequest.Code, "target not found", err)
|
||||
}
|
||||
return errs.ErrBadRequest
|
||||
return 0, errs.ErrBadRequest
|
||||
}
|
||||
|
||||
comment := &model.Comment{
|
||||
@ -41,13 +41,13 @@ func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateComm
|
||||
IsPrivate: req.IsPrivate,
|
||||
}
|
||||
|
||||
err := repo.Comment.CreateComment(comment)
|
||||
commentID, err := repo.Comment.CreateComment(comment)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return commentID, nil
|
||||
}
|
||||
|
||||
func (cs *CommentService) UpdateComment(ctx context.Context, req *dto.UpdateCommentReq) error {
|
||||
|
Reference in New Issue
Block a user