Files
backend/internal/dto/post_converter.go
lafay 081e44c4ff
Some checks failed
Build Backend / build (push) Failing after 13m9s
Build Backend / build-docker (push) Has been skipped
chore(ci, dto): update build workflow and enhance post DTOs
- Added NODE_NO_WARNINGS environment variable to build and deploy workflows.
- Upgraded actions/upload-artifact and actions/download-artifact to version 4 in the build workflow.
- Introduced ContentEditedAt field in PostResponse and PostDetailResponse DTOs to track content modification timestamps.
- Updated post conversion functions to include ContentEditedAt in responses.
- Ensured ContentEditedAt is set during post updates in the repository.
2026-03-23 14:08:36 +08:00

285 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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{
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,
}
}
// 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{
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,
}
}
// 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
}