2026-03-13 09:38:18 +08:00
|
|
|
|
package dto
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"carrot_bbs/internal/model"
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== Post 转换 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertPostImageToResponse 将PostImage转换为PostImageResponse
|
|
|
|
|
|
func ConvertPostImageToResponse(img *model.PostImage) PostImageResponse {
|
|
|
|
|
|
if img == nil {
|
|
|
|
|
|
return PostImageResponse{}
|
|
|
|
|
|
}
|
|
|
|
|
|
return PostImageResponse{
|
|
|
|
|
|
ID: img.ID,
|
|
|
|
|
|
URL: img.URL,
|
|
|
|
|
|
ThumbnailURL: img.ThumbnailURL,
|
|
|
|
|
|
Width: img.Width,
|
|
|
|
|
|
Height: img.Height,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertPostImagesToResponse 将PostImage列表转换为响应列表
|
|
|
|
|
|
func ConvertPostImagesToResponse(images []model.PostImage) []PostImageResponse {
|
|
|
|
|
|
result := make([]PostImageResponse, 0, len(images))
|
|
|
|
|
|
for i := range images {
|
|
|
|
|
|
result = append(result, ConvertPostImageToResponse(&images[i]))
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertPostToResponse 将Post转换为PostResponse(列表用)
|
|
|
|
|
|
func ConvertPostToResponse(post *model.Post, isLiked, isFavorited bool) *PostResponse {
|
|
|
|
|
|
if post == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
images := make([]PostImageResponse, 0)
|
|
|
|
|
|
for _, img := range post.Images {
|
|
|
|
|
|
images = append(images, ConvertPostImageToResponse(&img))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var author *UserResponse
|
|
|
|
|
|
if post.User != nil {
|
|
|
|
|
|
author = ConvertUserToResponse(post.User)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &PostResponse{
|
2026-03-23 14:08:36 +08:00
|
|
|
|
ID: post.ID,
|
|
|
|
|
|
UserID: post.UserID,
|
|
|
|
|
|
Title: post.Title,
|
|
|
|
|
|
Content: post.Content,
|
|
|
|
|
|
Images: images,
|
|
|
|
|
|
Status: string(post.Status),
|
|
|
|
|
|
LikesCount: post.LikesCount,
|
|
|
|
|
|
CommentsCount: post.CommentsCount,
|
|
|
|
|
|
FavoritesCount: post.FavoritesCount,
|
|
|
|
|
|
SharesCount: post.SharesCount,
|
|
|
|
|
|
ViewsCount: post.ViewsCount,
|
|
|
|
|
|
IsPinned: post.IsPinned,
|
|
|
|
|
|
IsLocked: post.IsLocked,
|
|
|
|
|
|
IsVote: post.IsVote,
|
|
|
|
|
|
CreatedAt: FormatTime(post.CreatedAt),
|
|
|
|
|
|
UpdatedAt: FormatTime(post.UpdatedAt),
|
|
|
|
|
|
ContentEditedAt: FormatTimePointer(post.ContentEditedAt),
|
|
|
|
|
|
Author: author,
|
|
|
|
|
|
IsLiked: isLiked,
|
|
|
|
|
|
IsFavorited: isFavorited,
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertPostToDetailResponse 将Post转换为PostDetailResponse
|
|
|
|
|
|
func ConvertPostToDetailResponse(post *model.Post, isLiked, isFavorited bool) *PostDetailResponse {
|
|
|
|
|
|
if post == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
images := make([]PostImageResponse, 0)
|
|
|
|
|
|
for _, img := range post.Images {
|
|
|
|
|
|
images = append(images, ConvertPostImageToResponse(&img))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var author *UserResponse
|
|
|
|
|
|
if post.User != nil {
|
|
|
|
|
|
author = ConvertUserToResponse(post.User)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &PostDetailResponse{
|
2026-03-23 14:08:36 +08:00
|
|
|
|
ID: post.ID,
|
|
|
|
|
|
UserID: post.UserID,
|
|
|
|
|
|
Title: post.Title,
|
|
|
|
|
|
Content: post.Content,
|
|
|
|
|
|
Images: images,
|
|
|
|
|
|
Status: string(post.Status),
|
|
|
|
|
|
LikesCount: post.LikesCount,
|
|
|
|
|
|
CommentsCount: post.CommentsCount,
|
|
|
|
|
|
FavoritesCount: post.FavoritesCount,
|
|
|
|
|
|
SharesCount: post.SharesCount,
|
|
|
|
|
|
ViewsCount: post.ViewsCount,
|
|
|
|
|
|
IsPinned: post.IsPinned,
|
|
|
|
|
|
IsLocked: post.IsLocked,
|
|
|
|
|
|
IsVote: post.IsVote,
|
|
|
|
|
|
CreatedAt: FormatTime(post.CreatedAt),
|
|
|
|
|
|
UpdatedAt: FormatTime(post.UpdatedAt),
|
|
|
|
|
|
ContentEditedAt: FormatTimePointer(post.ContentEditedAt),
|
|
|
|
|
|
Author: author,
|
|
|
|
|
|
IsLiked: isLiked,
|
|
|
|
|
|
IsFavorited: isFavorited,
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertPostsToResponse 将Post列表转换为响应列表(每个帖子独立检查点赞/收藏状态)
|
|
|
|
|
|
func ConvertPostsToResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
|
|
|
|
|
result := make([]*PostResponse, 0, len(posts))
|
|
|
|
|
|
for _, post := range posts {
|
|
|
|
|
|
isLiked := false
|
|
|
|
|
|
isFavorited := false
|
|
|
|
|
|
if isLikedMap != nil {
|
|
|
|
|
|
isLiked = isLikedMap[post.ID]
|
|
|
|
|
|
}
|
|
|
|
|
|
if isFavoritedMap != nil {
|
|
|
|
|
|
isFavorited = isFavoritedMap[post.ID]
|
|
|
|
|
|
}
|
|
|
|
|
|
result = append(result, ConvertPostToResponse(post, isLiked, isFavorited))
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BuildPostResponse 构建单个帖子响应(包含交互状态)
|
|
|
|
|
|
// 这是一个语义化的辅助函数,便于 Handler 层调用
|
|
|
|
|
|
func BuildPostResponse(post *model.Post, isLiked, isFavorited bool) *PostResponse {
|
|
|
|
|
|
return ConvertPostToResponse(post, isLiked, isFavorited)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BuildPostsWithInteractionResponse 批量构建帖子响应(包含交互状态)
|
|
|
|
|
|
// 这是一个语义化的辅助函数,便于 Handler 层调用
|
|
|
|
|
|
func BuildPostsWithInteractionResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
|
|
|
|
|
return ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== Comment 转换 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertCommentToResponse 将Comment转换为CommentResponse
|
|
|
|
|
|
func ConvertCommentToResponse(comment *model.Comment, isLiked bool) *CommentResponse {
|
|
|
|
|
|
if comment == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var author *UserResponse
|
|
|
|
|
|
if comment.User != nil {
|
|
|
|
|
|
author = ConvertUserToResponse(comment.User)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换子回复(扁平化结构)
|
|
|
|
|
|
var replies []*CommentResponse
|
|
|
|
|
|
if len(comment.Replies) > 0 {
|
|
|
|
|
|
replies = make([]*CommentResponse, 0, len(comment.Replies))
|
|
|
|
|
|
for _, reply := range comment.Replies {
|
|
|
|
|
|
replies = append(replies, ConvertCommentToResponse(reply, false))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TargetID 就是 ParentID,前端根据这个 ID 找到被回复用户的昵称
|
|
|
|
|
|
var targetID *string
|
|
|
|
|
|
if comment.ParentID != nil && *comment.ParentID != "" {
|
|
|
|
|
|
targetID = comment.ParentID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析图片JSON
|
|
|
|
|
|
var images []CommentImageResponse
|
|
|
|
|
|
if comment.Images != "" {
|
|
|
|
|
|
var urlList []string
|
|
|
|
|
|
if err := json.Unmarshal([]byte(comment.Images), &urlList); err == nil {
|
|
|
|
|
|
images = make([]CommentImageResponse, 0, len(urlList))
|
|
|
|
|
|
for _, url := range urlList {
|
|
|
|
|
|
images = append(images, CommentImageResponse{URL: url})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &CommentResponse{
|
|
|
|
|
|
ID: comment.ID,
|
|
|
|
|
|
PostID: comment.PostID,
|
|
|
|
|
|
UserID: comment.UserID,
|
|
|
|
|
|
ParentID: comment.ParentID,
|
|
|
|
|
|
RootID: comment.RootID,
|
|
|
|
|
|
Content: comment.Content,
|
|
|
|
|
|
Images: images,
|
|
|
|
|
|
LikesCount: comment.LikesCount,
|
|
|
|
|
|
RepliesCount: comment.RepliesCount,
|
|
|
|
|
|
CreatedAt: FormatTime(comment.CreatedAt),
|
|
|
|
|
|
Author: author,
|
|
|
|
|
|
IsLiked: isLiked,
|
|
|
|
|
|
TargetID: targetID,
|
|
|
|
|
|
Replies: replies,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertCommentsToResponse 将Comment列表转换为响应列表
|
|
|
|
|
|
func ConvertCommentsToResponse(comments []*model.Comment, isLiked bool) []*CommentResponse {
|
|
|
|
|
|
result := make([]*CommentResponse, 0, len(comments))
|
|
|
|
|
|
for _, comment := range comments {
|
|
|
|
|
|
result = append(result, ConvertCommentToResponse(comment, isLiked))
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// IsLikedChecker 点赞状态检查器接口
|
|
|
|
|
|
type IsLikedChecker interface {
|
|
|
|
|
|
IsLiked(ctx context.Context, commentID, userID string) bool
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertCommentToResponseWithUser 将Comment转换为CommentResponse(根据用户ID检查点赞状态)
|
|
|
|
|
|
func ConvertCommentToResponseWithUser(comment *model.Comment, userID string, checker IsLikedChecker) *CommentResponse {
|
|
|
|
|
|
if comment == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查当前用户是否点赞了该评论
|
|
|
|
|
|
isLiked := false
|
|
|
|
|
|
if userID != "" && checker != nil {
|
|
|
|
|
|
isLiked = checker.IsLiked(context.Background(), comment.ID, userID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var author *UserResponse
|
|
|
|
|
|
if comment.User != nil {
|
|
|
|
|
|
author = ConvertUserToResponse(comment.User)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换子回复(扁平化结构),递归检查点赞状态
|
|
|
|
|
|
var replies []*CommentResponse
|
|
|
|
|
|
if len(comment.Replies) > 0 {
|
|
|
|
|
|
replies = make([]*CommentResponse, 0, len(comment.Replies))
|
|
|
|
|
|
for _, reply := range comment.Replies {
|
|
|
|
|
|
replies = append(replies, ConvertCommentToResponseWithUser(reply, userID, checker))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TargetID 就是 ParentID,前端根据这个 ID 找到被回复用户的昵称
|
|
|
|
|
|
var targetID *string
|
|
|
|
|
|
if comment.ParentID != nil && *comment.ParentID != "" {
|
|
|
|
|
|
targetID = comment.ParentID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析图片JSON
|
|
|
|
|
|
var images []CommentImageResponse
|
|
|
|
|
|
if comment.Images != "" {
|
|
|
|
|
|
var urlList []string
|
|
|
|
|
|
if err := json.Unmarshal([]byte(comment.Images), &urlList); err == nil {
|
|
|
|
|
|
images = make([]CommentImageResponse, 0, len(urlList))
|
|
|
|
|
|
for _, url := range urlList {
|
|
|
|
|
|
images = append(images, CommentImageResponse{URL: url})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &CommentResponse{
|
|
|
|
|
|
ID: comment.ID,
|
|
|
|
|
|
PostID: comment.PostID,
|
|
|
|
|
|
UserID: comment.UserID,
|
|
|
|
|
|
ParentID: comment.ParentID,
|
|
|
|
|
|
RootID: comment.RootID,
|
|
|
|
|
|
Content: comment.Content,
|
|
|
|
|
|
Images: images,
|
|
|
|
|
|
LikesCount: comment.LikesCount,
|
|
|
|
|
|
RepliesCount: comment.RepliesCount,
|
|
|
|
|
|
CreatedAt: FormatTime(comment.CreatedAt),
|
|
|
|
|
|
Author: author,
|
|
|
|
|
|
IsLiked: isLiked,
|
|
|
|
|
|
TargetID: targetID,
|
|
|
|
|
|
Replies: replies,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertCommentsToResponseWithUser 将Comment列表转换为响应列表(根据用户ID检查点赞状态)
|
|
|
|
|
|
func ConvertCommentsToResponseWithUser(comments []*model.Comment, userID string, checker IsLikedChecker) []*CommentResponse {
|
|
|
|
|
|
result := make([]*CommentResponse, 0, len(comments))
|
|
|
|
|
|
for _, comment := range comments {
|
|
|
|
|
|
result = append(result, ConvertCommentToResponseWithUser(comment, userID, checker))
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|