feat: 添加评论功能的客户端信息显示选项,更新相关接口和组件

This commit is contained in:
2025-09-13 16:04:09 +08:00
parent 011dc298c2
commit 2d0e1a46e2
10 changed files with 279 additions and 234 deletions

View File

@ -27,6 +27,7 @@ type CreateCommentReq struct {
IsPrivate bool `json:"is_private"` // 是否私密评论默认false
RemoteAddr string `json:"remote_addr"` // 远程地址
UserAgent string `json:"user_agent"` // 用户代理
ShowClientInfo bool `json:"show_client_info"` // 是否显示客户端信息
}
type UpdateCommentReq struct {

View File

@ -20,6 +20,7 @@ type Comment struct {
Location string `gorm:"type:text"` // 用户位置基于IP
LikeCount uint64
CommentCount uint64
ShowClientInfo bool `gorm:"default:false"` // 是否显示客户端信息
}
func (c *Comment) AfterCreate(tx *gorm.DB) (err error) {

View File

@ -121,6 +121,7 @@ func (cr *CommentRepo) CreateComment(comment *model.Comment) (uint, error) {
})
return commentID, err
}
func (cr *CommentRepo) UpdateComment(comment *model.Comment) error {
if comment.ID == 0 {
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)

View File

@ -42,6 +42,7 @@ func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateComm
IsPrivate: req.IsPrivate,
RemoteAddr: req.RemoteAddr,
UserAgent: req.UserAgent,
ShowClientInfo: req.ShowClientInfo,
}
commentID, err := repo.Comment.CreateComment(comment)
@ -112,18 +113,44 @@ func (cs *CommentService) GetComment(ctx context.Context, commentID string) (*dt
return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err)
}
currentUserID := uint(0)
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
currentUserID = currentUser.ID
}
if comment.IsPrivate && currentUserID != comment.UserID {
return nil, errs.ErrForbidden
}
isLiked := false
if currentUserID != 0 {
isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment)
}
ua := utils.ParseUA(comment.UserAgent)
if !comment.ShowClientInfo {
comment.Location = ""
ua.OS = ""
ua.OSVersion = ""
ua.Browser = ""
ua.BrowserVer = ""
}
commentDto := dto.CommentDto{
ID: comment.ID,
Content: comment.Content,
TargetID: comment.TargetID,
TargetType: comment.TargetType,
Content: comment.Content,
ReplyID: comment.ReplyID,
Depth: comment.Depth,
CreatedAt: comment.CreatedAt.String(),
UpdatedAt: comment.UpdatedAt.String(),
Depth: comment.Depth,
User: comment.User.ToDto(),
ReplyCount: comment.CommentCount,
LikeCount: comment.LikeCount,
IsLiked: isLiked,
IsPrivate: comment.IsPrivate,
OS: ua.OS + " " + ua.OSVersion,
Browser: ua.Browser + " " + ua.BrowserVer,
Location: comment.Location,
}
// TODO: 返回更多字段
return &commentDto, err
}
@ -133,14 +160,11 @@ func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommen
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
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)
if err != nil {
return nil, errs.New(errs.ErrInternalServer.Code, "failed to list comments", err)
}
commentDtos := make([]dto.CommentDto, 0)
for _, comment := range comments {
//replyCount, _ := repo.Comment.CountReplyComments(currentUserID, comment.ID)
isLiked := false
@ -166,6 +190,11 @@ func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommen
Browser: ua.Browser + " " + ua.BrowserVer,
Location: comment.Location,
}
if !comment.ShowClientInfo {
commentDto.Location = ""
commentDto.OS = ""
commentDto.Browser = ""
}
commentDtos = append(commentDtos, commentDto)
}
return commentDtos, nil

View File

@ -12,13 +12,15 @@ export async function createComment(
targetId,
content,
replyId = null,
isPrivate = false
isPrivate = false,
showClientInfo = true,
}: {
targetType: TargetType
targetId: number
content: string
replyId: number | null
isPrivate: boolean
showClientInfo: boolean
}
): Promise<BaseResponse<{ id: number }>> {
const res = await axiosClient.post<BaseResponse<{ id: number }>>('/comment/c', {
@ -26,7 +28,8 @@ export async function createComment(
targetId,
content,
replyId,
isPrivate
isPrivate,
showClientInfo,
})
return res.data
}

View File

@ -3,7 +3,7 @@ import { User } from "@/models/user";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { toast } from "sonner";
import GravatarAvatar, { getGravatarByUser } from "@/components/common/gravatar";
import GravatarAvatar from "@/components/common/gravatar";
import { CircleUser } from "lucide-react";
import { Textarea } from "@/components/ui/textarea";
import { Checkbox } from "@/components/ui/checkbox"
@ -20,7 +20,7 @@ export function CommentInput(
isUpdate = false
}: {
user: User | null,
onCommentSubmitted: ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => void,
onCommentSubmitted: ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => void,
initContent?: string,
initIsPrivate?: boolean,
placeholder?: string,
@ -33,6 +33,7 @@ export function CommentInput(
const clickToUserProfile = useToUserProfile();
const [isPrivate, setIsPrivate] = useState(initIsPrivate);
const [showClientInfo, setShowClientInfo] = useState(true);
const [commentContent, setCommentContent] = useState(initContent);
const handleCommentSubmit = async () => {
@ -54,7 +55,7 @@ export function CommentInput(
toast.warning(t("comment_unchanged"));
return;
}
onCommentSubmitted({ commentContent, isPrivate });
onCommentSubmitted({ commentContent, isPrivate, showClientInfo });
setCommentContent("");
};
@ -75,6 +76,13 @@ export function CommentInput(
</div>
</div>
<div className="flex justify-end fade-in-up space-x-4 items-center">
<div className="flex items-center space-x-2">
<Checkbox
checked={showClientInfo}
onCheckedChange={checked => setShowClientInfo(checked === true)}
/>
<Label onClick={() => setShowClientInfo(prev => !prev)}>{t("show_client_info")}</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
checked={isPrivate}

View File

@ -123,13 +123,14 @@ export function CommentItem(
});
}
const onReply = ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => {
const onReply = ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => {
createComment({
targetType: comment.targetType,
targetId: comment.targetId,
content: commentContent,
replyId: comment.id,
isPrivate,
showClientInfo
}).then(() => {
toast.success(t("comment_success"));
reloadReplies();

View File

@ -61,18 +61,18 @@ export function CommentSection(
});
}, [])
const onCommentSubmitted = ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => {
const onCommentSubmitted = ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => {
createComment({
targetType,
targetId,
content: commentContent,
replyId: null,
isPrivate,
showClientInfo
}).then(res => {
toast.success(t("comment_success"));
setTotalCommentCount(c => c + 1);
getComment({ id: res.data.id }).then(response => {
console.log("New comment fetched:", response.data);
setComments(prevComments => [response.data, ...prevComments]);
});
setActiveInput(null);

View File

@ -47,6 +47,7 @@
"private": "私密评论",
"private_placeholder": "悄悄地说一句...",
"reply": "回复",
"show_client_info": "展示客户端信息",
"submit": "提交",
"unlike": "取消点赞",
"unlike_success": "已取消点赞",