mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 02:56:22 +00:00
✨ feat: 添加评论动画样式和登录组件
This commit is contained in:
@ -1,177 +1,177 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/snowykami/neo-blog/pkg/constant"
|
"github.com/snowykami/neo-blog/pkg/constant"
|
||||||
|
|
||||||
"github.com/snowykami/neo-blog/internal/ctxutils"
|
"github.com/snowykami/neo-blog/internal/ctxutils"
|
||||||
"github.com/snowykami/neo-blog/internal/dto"
|
"github.com/snowykami/neo-blog/internal/dto"
|
||||||
"github.com/snowykami/neo-blog/internal/model"
|
"github.com/snowykami/neo-blog/internal/model"
|
||||||
"github.com/snowykami/neo-blog/internal/repo"
|
"github.com/snowykami/neo-blog/internal/repo"
|
||||||
"github.com/snowykami/neo-blog/pkg/errs"
|
"github.com/snowykami/neo-blog/pkg/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommentService struct{}
|
type CommentService struct{}
|
||||||
|
|
||||||
func NewCommentService() *CommentService {
|
func NewCommentService() *CommentService {
|
||||||
return &CommentService{}
|
return &CommentService{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateCommentReq) error {
|
func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateCommentReq) error {
|
||||||
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errs.ErrUnauthorized
|
return errs.ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, err := cs.checkTargetExists(req.TargetID, req.TargetType); !ok {
|
if ok, err := cs.checkTargetExists(req.TargetID, req.TargetType); !ok {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.New(errs.ErrBadRequest.Code, "target not found", err)
|
return errs.New(errs.ErrBadRequest.Code, "target not found", err)
|
||||||
}
|
}
|
||||||
return errs.ErrBadRequest
|
return errs.ErrBadRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
comment := &model.Comment{
|
comment := &model.Comment{
|
||||||
Content: req.Content,
|
Content: req.Content,
|
||||||
ReplyID: req.ReplyID,
|
ReplyID: req.ReplyID,
|
||||||
TargetID: req.TargetID,
|
TargetID: req.TargetID,
|
||||||
TargetType: req.TargetType,
|
TargetType: req.TargetType,
|
||||||
UserID: currentUser.ID,
|
UserID: currentUser.ID,
|
||||||
IsPrivate: req.IsPrivate,
|
IsPrivate: req.IsPrivate,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := repo.Comment.CreateComment(comment)
|
err := repo.Comment.CreateComment(comment)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) UpdateComment(ctx context.Context, req *dto.UpdateCommentReq) error {
|
func (cs *CommentService) UpdateComment(ctx context.Context, req *dto.UpdateCommentReq) error {
|
||||||
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errs.ErrUnauthorized
|
return errs.ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
comment, err := repo.Comment.GetComment(strconv.Itoa(int(req.CommentID)))
|
comment, err := repo.Comment.GetComment(strconv.Itoa(int(req.CommentID)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentUser.ID != comment.UserID {
|
if currentUser.ID != comment.UserID {
|
||||||
return errs.ErrForbidden
|
return errs.ErrForbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
comment.Content = req.Content
|
comment.Content = req.Content
|
||||||
comment.IsPrivate = req.IsPrivate
|
comment.IsPrivate = req.IsPrivate
|
||||||
|
|
||||||
err = repo.Comment.UpdateComment(comment)
|
err = repo.Comment.UpdateComment(comment)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) DeleteComment(ctx context.Context, commentID string) error {
|
func (cs *CommentService) DeleteComment(ctx context.Context, commentID string) error {
|
||||||
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
currentUser, ok := ctxutils.GetCurrentUser(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errs.ErrUnauthorized
|
return errs.ErrUnauthorized
|
||||||
}
|
}
|
||||||
if commentID == "" {
|
if commentID == "" {
|
||||||
return errs.ErrBadRequest
|
return errs.ErrBadRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
comment, err := repo.Comment.GetComment(commentID)
|
comment, err := repo.Comment.GetComment(commentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.New(errs.ErrNotFound.Code, "comment not found", err)
|
return errs.New(errs.ErrNotFound.Code, "comment not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if comment.UserID != currentUser.ID {
|
if comment.UserID != currentUser.ID {
|
||||||
return errs.ErrForbidden
|
return errs.ErrForbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.Comment.DeleteComment(commentID); err != nil {
|
if err := repo.Comment.DeleteComment(commentID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) GetComment(ctx context.Context, commentID string) (*dto.CommentDto, error) {
|
func (cs *CommentService) GetComment(ctx context.Context, commentID string) (*dto.CommentDto, error) {
|
||||||
comment, err := repo.Comment.GetComment(commentID)
|
comment, err := repo.Comment.GetComment(commentID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err)
|
return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commentDto := dto.CommentDto{
|
commentDto := dto.CommentDto{
|
||||||
ID: comment.ID,
|
ID: comment.ID,
|
||||||
TargetID: comment.TargetID,
|
TargetID: comment.TargetID,
|
||||||
TargetType: comment.TargetType,
|
TargetType: comment.TargetType,
|
||||||
Content: comment.Content,
|
Content: comment.Content,
|
||||||
ReplyID: comment.ReplyID,
|
ReplyID: comment.ReplyID,
|
||||||
Depth: comment.Depth,
|
Depth: comment.Depth,
|
||||||
CreatedAt: comment.CreatedAt.String(),
|
CreatedAt: comment.CreatedAt.String(),
|
||||||
UpdatedAt: comment.UpdatedAt.String(),
|
UpdatedAt: comment.UpdatedAt.String(),
|
||||||
User: comment.User.ToDto(),
|
User: comment.User.ToDto(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &commentDto, err
|
return &commentDto, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommentListReq) ([]dto.CommentDto, error) {
|
func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommentListReq) ([]dto.CommentDto, error) {
|
||||||
currentUserID := uint(0)
|
currentUserID := uint(0)
|
||||||
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
|
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
|
||||||
currentUserID = currentUser.ID
|
currentUserID = currentUser.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
comments, err := repo.Comment.ListComments(currentUserID, req.TargetID, req.CommentID, req.TargetType, req.Page, req.Size, req.OrderBy, req.Desc, req.Depth)
|
comments, err := repo.Comment.ListComments(currentUserID, req.TargetID, req.CommentID, req.TargetType, req.Page, req.Size, req.OrderBy, req.Desc, req.Depth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.New(errs.ErrInternalServer.Code, "failed to list comments", err)
|
return nil, errs.New(errs.ErrInternalServer.Code, "failed to list comments", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commentDtos := make([]dto.CommentDto, 0)
|
commentDtos := make([]dto.CommentDto, 0)
|
||||||
|
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
replyCount, _ := repo.Comment.CountReplyComments(currentUserID, comment.ID)
|
replyCount, _ := repo.Comment.CountReplyComments(currentUserID, comment.ID)
|
||||||
isLiked := false
|
isLiked := false
|
||||||
if currentUserID != 0 {
|
if currentUserID != 0 {
|
||||||
isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment)
|
isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
commentDto := dto.CommentDto{
|
commentDto := dto.CommentDto{
|
||||||
ID: comment.ID,
|
ID: comment.ID,
|
||||||
Content: comment.Content,
|
Content: comment.Content,
|
||||||
TargetID: comment.TargetID,
|
TargetID: comment.TargetID,
|
||||||
TargetType: comment.TargetType,
|
TargetType: comment.TargetType,
|
||||||
ReplyID: comment.ReplyID,
|
ReplyID: comment.ReplyID,
|
||||||
CreatedAt: comment.CreatedAt.String(),
|
CreatedAt: comment.CreatedAt.String(),
|
||||||
UpdatedAt: comment.UpdatedAt.String(),
|
UpdatedAt: comment.UpdatedAt.String(),
|
||||||
Depth: comment.Depth,
|
Depth: comment.Depth,
|
||||||
User: comment.User.ToDto(),
|
User: comment.User.ToDto(),
|
||||||
ReplyCount: replyCount,
|
ReplyCount: replyCount,
|
||||||
LikeCount: comment.LikeCount,
|
LikeCount: comment.LikeCount,
|
||||||
IsLiked: isLiked,
|
IsLiked: isLiked,
|
||||||
IsPrivate: comment.IsPrivate,
|
IsPrivate: comment.IsPrivate,
|
||||||
}
|
}
|
||||||
commentDtos = append(commentDtos, commentDto)
|
commentDtos = append(commentDtos, commentDto)
|
||||||
}
|
}
|
||||||
return commentDtos, nil
|
return commentDtos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *CommentService) checkTargetExists(targetID uint, targetType string) (bool, error) {
|
func (cs *CommentService) checkTargetExists(targetID uint, targetType string) (bool, error) {
|
||||||
switch targetType {
|
switch targetType {
|
||||||
case constant.TargetTypePost:
|
case constant.TargetTypePost:
|
||||||
if _, err := repo.Post.GetPostByID(strconv.Itoa(int(targetID))); err != nil {
|
if _, err := repo.Post.GetPostByID(strconv.Itoa(int(targetID))); err != nil {
|
||||||
return false, errs.New(errs.ErrNotFound.Code, "post not found", err)
|
return false, errs.New(errs.ErrNotFound.Code, "post not found", err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return false, errs.New(errs.ErrBadRequest.Code, "invalid target type", nil)
|
return false, errs.New(errs.ErrBadRequest.Code, "invalid target type", nil)
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ export function CommentItem(
|
|||||||
|
|
||||||
const [likeCount, setLikeCount] = useState(comment.likeCount);
|
const [likeCount, setLikeCount] = useState(comment.likeCount);
|
||||||
const [liked, setLiked] = useState(comment.isLiked);
|
const [liked, setLiked] = useState(comment.isLiked);
|
||||||
|
const [canClickLike, setCanClickLike] = useState(true);
|
||||||
const [isPrivate, setIsPrivate] = useState(comment.isPrivate);
|
const [isPrivate, setIsPrivate] = useState(comment.isPrivate);
|
||||||
const [replyCount, setReplyCount] = useState(comment.replyCount);
|
const [replyCount, setReplyCount] = useState(comment.replyCount);
|
||||||
const [showReplies, setShowReplies] = useState(false);
|
const [showReplies, setShowReplies] = useState(false);
|
||||||
@ -45,6 +46,10 @@ export function CommentItem(
|
|||||||
const [showEditInput, setShowEditInput] = useState(false);
|
const [showEditInput, setShowEditInput] = useState(false);
|
||||||
|
|
||||||
const handleToggleLike = () => {
|
const handleToggleLike = () => {
|
||||||
|
if (!canClickLike) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCanClickLike(false);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
toast.error(t("login_required"), {
|
toast.error(t("login_required"), {
|
||||||
action: <div className="flex justify-end">
|
action: <div className="flex justify-end">
|
||||||
@ -58,6 +63,7 @@ export function CommentItem(
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setLiked(!liked) // 提前转换状态,让用户觉得响应很快
|
||||||
toggleLike(
|
toggleLike(
|
||||||
{ targetType: TargetType.Comment, targetId: comment.id }
|
{ targetType: TargetType.Comment, targetId: comment.id }
|
||||||
).then(res => {
|
).then(res => {
|
||||||
@ -67,6 +73,7 @@ export function CommentItem(
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
toast.error(t("like_failed") + ": " + error.message);
|
toast.error(t("like_failed") + ": " + error.message);
|
||||||
});
|
});
|
||||||
|
setCanClickLike(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadReplies = () => {
|
const reloadReplies = () => {
|
||||||
|
Reference in New Issue
Block a user