The Go module name has been changed from `carrot_bbs` to `with_you`, which requires all import paths to be updated throughout the codebase and by external consumers. BREAKING CHANGE: Module name changed from carrot_bbs to with_you. All imports and dependencies must be updated from carrot_bbs/* to with_you/*.
325 lines
10 KiB
Go
325 lines
10 KiB
Go
package dto
|
||
|
||
import (
|
||
"with_you/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,
|
||
PreviewURL: img.PreviewURL,
|
||
PreviewURLLarge: img.PreviewURLLarge,
|
||
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
|
||
}
|
||
|
||
func postChannelBrief(post *model.Post, channelByID map[string]*model.Channel) *PostChannelBrief {
|
||
if post == nil || post.ChannelID == nil || *post.ChannelID == "" || channelByID == nil {
|
||
return nil
|
||
}
|
||
ch := channelByID[*post.ChannelID]
|
||
if ch == nil {
|
||
return nil
|
||
}
|
||
return &PostChannelBrief{ID: ch.ID, Name: ch.Name}
|
||
}
|
||
|
||
// CollectPostChannelIDs 从帖子列表收集去重后的频道 ID(用于批量查库填充 channel 名称)
|
||
func CollectPostChannelIDs(posts []*model.Post) []string {
|
||
seen := make(map[string]struct{})
|
||
out := make([]string, 0)
|
||
for _, p := range posts {
|
||
if p == nil || p.ChannelID == nil || *p.ChannelID == "" {
|
||
continue
|
||
}
|
||
id := *p.ChannelID
|
||
if _, ok := seen[id]; ok {
|
||
continue
|
||
}
|
||
seen[id] = struct{}{}
|
||
out = append(out, id)
|
||
}
|
||
return out
|
||
}
|
||
|
||
// PostChannelBriefForPost 根据帖子与频道映射生成频道摘要(供 Handler 手动组装响应)
|
||
func PostChannelBriefForPost(post *model.Post, channelByID map[string]*model.Channel) *PostChannelBrief {
|
||
return postChannelBrief(post, channelByID)
|
||
}
|
||
|
||
// ConvertPostToResponse 将Post转换为PostResponse(列表用);channelByID 可为 nil
|
||
func ConvertPostToResponse(post *model.Post, channelByID map[string]*model.Channel, 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,
|
||
ChannelID: post.ChannelID,
|
||
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,
|
||
Channel: postChannelBrief(post, channelByID),
|
||
}
|
||
}
|
||
|
||
// ConvertPostToDetailResponse 将Post转换为PostDetailResponse;channelByID 可为 nil
|
||
func ConvertPostToDetailResponse(post *model.Post, channelByID map[string]*model.Channel, 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,
|
||
ChannelID: post.ChannelID,
|
||
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,
|
||
Channel: postChannelBrief(post, channelByID),
|
||
}
|
||
}
|
||
|
||
// ConvertPostsToResponse 将Post列表转换为响应列表(每个帖子独立检查点赞/收藏状态);channelByID 可为 nil
|
||
func ConvertPostsToResponse(posts []*model.Post, channelByID map[string]*model.Channel, 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, channelByID, isLiked, isFavorited))
|
||
}
|
||
return result
|
||
}
|
||
|
||
// BuildPostResponse 构建单个帖子响应(包含交互状态)
|
||
// 这是一个语义化的辅助函数,便于 Handler 层调用
|
||
func BuildPostResponse(post *model.Post, channelByID map[string]*model.Channel, isLiked, isFavorited bool) *PostResponse {
|
||
return ConvertPostToResponse(post, channelByID, isLiked, isFavorited)
|
||
}
|
||
|
||
// BuildPostsWithInteractionResponse 批量构建帖子响应(包含交互状态)
|
||
// 这是一个语义化的辅助函数,便于 Handler 层调用
|
||
func BuildPostsWithInteractionResponse(posts []*model.Post, channelByID map[string]*model.Channel, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
||
return ConvertPostsToResponse(posts, channelByID, 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
|
||
}
|