feat(post): enhance post handling with channel integration
- Updated PostHandler to include channel information in post responses. - Introduced PostChannelBrief DTO to represent channel details associated with posts. - Modified post conversion functions to support channel data, ensuring proper mapping of channel IDs to channel names. - Enhanced ChannelRepository and ChannelService to facilitate batch retrieval of channels by IDs. - Updated wire generation to inject channel service into post handler for improved functionality.
This commit is contained in:
@@ -61,7 +61,9 @@ func InitializeApp() (*App, error) {
|
||||
dataChangeLogService := wire.ProvideDataChangeLogService(asyncLogManager, dataChangeLogRepository)
|
||||
logService := wire.ProvideLogService(operationLogService, loginLogService, dataChangeLogService)
|
||||
userHandler := wire.ProvideUserHandler(userService, userActivityService, postService, commentService, logService)
|
||||
postHandler := wire.ProvidePostHandler(postService, userService, logService)
|
||||
channelRepository := repository.NewChannelRepository(db)
|
||||
channelService := wire.ProvideChannelService(channelRepository)
|
||||
postHandler := wire.ProvidePostHandler(postService, userService, channelService, logService)
|
||||
commentHandler := handler.NewCommentHandler(commentService)
|
||||
chatService := wire.ProvideChatService(db, messageRepository, userRepository, hub, cache)
|
||||
messageService := wire.ProvideMessageService(db, messageRepository, cache)
|
||||
@@ -87,8 +89,6 @@ func InitializeApp() (*App, error) {
|
||||
voteRepository := repository.NewVoteRepository(db)
|
||||
voteService := wire.ProvideVoteService(voteRepository, postRepository, cache, postAIService, systemMessageService)
|
||||
voteHandler := handler.NewVoteHandler(voteService, postService)
|
||||
channelRepository := repository.NewChannelRepository(db)
|
||||
channelService := wire.ProvideChannelService(channelRepository)
|
||||
channelHandler := handler.NewChannelHandler(channelService)
|
||||
scheduleRepository := repository.NewScheduleRepository(db)
|
||||
scheduleService := wire.ProvideScheduleService(scheduleRepository)
|
||||
|
||||
@@ -120,6 +120,12 @@ type AdminUpdateUserStatusRequest struct {
|
||||
|
||||
// ==================== Post DTOs ====================
|
||||
|
||||
// PostChannelBrief 帖子所属频道(列表/详情卡片展示)
|
||||
type PostChannelBrief struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// PostImageResponse 帖子图片响应
|
||||
type PostImageResponse struct {
|
||||
ID string `json:"id"`
|
||||
@@ -154,6 +160,7 @@ type PostResponse struct {
|
||||
Author *UserResponse `json:"author"`
|
||||
IsLiked bool `json:"is_liked"`
|
||||
IsFavorited bool `json:"is_favorited"`
|
||||
Channel *PostChannelBrief `json:"channel,omitempty"`
|
||||
}
|
||||
|
||||
// PostDetailResponse 帖子详情响应
|
||||
@@ -179,6 +186,7 @@ type PostDetailResponse struct {
|
||||
Author *UserResponse `json:"author"`
|
||||
IsLiked bool `json:"is_liked"`
|
||||
IsFavorited bool `json:"is_favorited"`
|
||||
Channel *PostChannelBrief `json:"channel,omitempty"`
|
||||
}
|
||||
|
||||
// ==================== Comment DTOs ====================
|
||||
|
||||
@@ -31,8 +31,42 @@ func ConvertPostImagesToResponse(images []model.PostImage) []PostImageResponse {
|
||||
return result
|
||||
}
|
||||
|
||||
// ConvertPostToResponse 将Post转换为PostResponse(列表用)
|
||||
func ConvertPostToResponse(post *model.Post, isLiked, isFavorited bool) *PostResponse {
|
||||
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
|
||||
}
|
||||
@@ -69,11 +103,12 @@ func ConvertPostToResponse(post *model.Post, isLiked, isFavorited bool) *PostRes
|
||||
Author: author,
|
||||
IsLiked: isLiked,
|
||||
IsFavorited: isFavorited,
|
||||
Channel: postChannelBrief(post, channelByID),
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertPostToDetailResponse 将Post转换为PostDetailResponse
|
||||
func ConvertPostToDetailResponse(post *model.Post, isLiked, isFavorited bool) *PostDetailResponse {
|
||||
// ConvertPostToDetailResponse 将Post转换为PostDetailResponse;channelByID 可为 nil
|
||||
func ConvertPostToDetailResponse(post *model.Post, channelByID map[string]*model.Channel, isLiked, isFavorited bool) *PostDetailResponse {
|
||||
if post == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -110,11 +145,12 @@ func ConvertPostToDetailResponse(post *model.Post, isLiked, isFavorited bool) *P
|
||||
Author: author,
|
||||
IsLiked: isLiked,
|
||||
IsFavorited: isFavorited,
|
||||
Channel: postChannelBrief(post, channelByID),
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertPostsToResponse 将Post列表转换为响应列表(每个帖子独立检查点赞/收藏状态)
|
||||
func ConvertPostsToResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
||||
// 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
|
||||
@@ -125,21 +161,21 @@ func ConvertPostsToResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[
|
||||
if isFavoritedMap != nil {
|
||||
isFavorited = isFavoritedMap[post.ID]
|
||||
}
|
||||
result = append(result, ConvertPostToResponse(post, isLiked, isFavorited))
|
||||
result = append(result, ConvertPostToResponse(post, channelByID, isLiked, isFavorited))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// BuildPostResponse 构建单个帖子响应(包含交互状态)
|
||||
// 这是一个语义化的辅助函数,便于 Handler 层调用
|
||||
func BuildPostResponse(post *model.Post, isLiked, isFavorited bool) *PostResponse {
|
||||
return ConvertPostToResponse(post, isLiked, isFavorited)
|
||||
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, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
||||
return ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap)
|
||||
func BuildPostsWithInteractionResponse(posts []*model.Post, channelByID map[string]*model.Channel, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse {
|
||||
return ConvertPostsToResponse(posts, channelByID, isLikedMap, isFavoritedMap)
|
||||
}
|
||||
|
||||
// ==================== Comment 转换 ====================
|
||||
|
||||
@@ -19,16 +19,31 @@ import (
|
||||
type PostHandler struct {
|
||||
postService service.PostService
|
||||
userService service.UserService
|
||||
channelService service.ChannelService
|
||||
}
|
||||
|
||||
// NewPostHandler 创建帖子处理器
|
||||
func NewPostHandler(postService service.PostService, userService service.UserService) *PostHandler {
|
||||
func NewPostHandler(postService service.PostService, userService service.UserService, channelService service.ChannelService) *PostHandler {
|
||||
return &PostHandler{
|
||||
postService: postService,
|
||||
userService: userService,
|
||||
channelService: channelService,
|
||||
}
|
||||
}
|
||||
|
||||
// channelMapForPosts 批量解析帖子中的频道 ID,用于填充响应里的 channel 名称
|
||||
func (h *PostHandler) channelMapForPosts(posts []*model.Post) map[string]*model.Channel {
|
||||
ids := dto.CollectPostChannelIDs(posts)
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
m, err := h.channelService.MapByIDs(ids)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Create 创建帖子
|
||||
func (h *PostHandler) Create(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
@@ -61,7 +76,8 @@ func (h *PostHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, dto.ConvertPostToResponse(post, false, false))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.ConvertPostToResponse(post, chMap, false, false))
|
||||
}
|
||||
|
||||
// GetByID 获取帖子(不增加浏览量)
|
||||
@@ -123,6 +139,9 @@ func (h *PostHandler) GetByID(c *gin.Context) {
|
||||
IsFavorited: isFavorited,
|
||||
}
|
||||
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
responseData.Channel = dto.PostChannelBriefForPost(post, chMap)
|
||||
|
||||
response.Success(c, responseData)
|
||||
}
|
||||
|
||||
@@ -251,8 +270,8 @@ func (h *PostHandler) List(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(posts)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
response.Paginated(c, postResponses, total, page, pageSize)
|
||||
}
|
||||
@@ -308,8 +327,8 @@ func (h *PostHandler) ListByCursor(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(result.Items)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
// 构建游标分页响应
|
||||
cursorResp := &dto.PostCursorPageResponse{
|
||||
@@ -378,7 +397,8 @@ func (h *PostHandler) Update(c *gin.Context) {
|
||||
currentUserID := c.GetString("user_id")
|
||||
isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), post.ID, currentUserID)
|
||||
|
||||
response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.BuildPostResponse(post, chMap, isLiked, isFavorited))
|
||||
}
|
||||
|
||||
// Delete 删除帖子
|
||||
@@ -437,7 +457,8 @@ func (h *PostHandler) Like(c *gin.Context) {
|
||||
// 获取交互状态
|
||||
isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID)
|
||||
|
||||
response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.BuildPostResponse(post, chMap, isLiked, isFavorited))
|
||||
}
|
||||
|
||||
// Unlike 取消点赞
|
||||
@@ -466,7 +487,8 @@ func (h *PostHandler) Unlike(c *gin.Context) {
|
||||
// 获取交互状态
|
||||
isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID)
|
||||
|
||||
response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.BuildPostResponse(post, chMap, isLiked, isFavorited))
|
||||
}
|
||||
|
||||
// Favorite 收藏帖子
|
||||
@@ -495,7 +517,8 @@ func (h *PostHandler) Favorite(c *gin.Context) {
|
||||
// 获取交互状态
|
||||
isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID)
|
||||
|
||||
response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.BuildPostResponse(post, chMap, isLiked, isFavorited))
|
||||
}
|
||||
|
||||
// Unfavorite 取消收藏
|
||||
@@ -524,7 +547,8 @@ func (h *PostHandler) Unfavorite(c *gin.Context) {
|
||||
// 获取交互状态
|
||||
isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID)
|
||||
|
||||
response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited))
|
||||
chMap := h.channelMapForPosts([]*model.Post{post})
|
||||
response.Success(c, dto.BuildPostResponse(post, chMap, isLiked, isFavorited))
|
||||
}
|
||||
|
||||
// GetUserPosts 获取用户帖子列表(支持游标分页和偏移分页)
|
||||
@@ -557,8 +581,8 @@ func (h *PostHandler) GetUserPosts(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(posts)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
response.Paginated(c, postResponses, total, page, pageSize)
|
||||
}
|
||||
@@ -588,8 +612,8 @@ func (h *PostHandler) GetUserPostsByCursor(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(result.Items)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
// 构建游标分页响应
|
||||
cursorResp := &dto.PostCursorPageResponse{
|
||||
@@ -623,8 +647,8 @@ func (h *PostHandler) GetFavorites(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(posts)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
response.Paginated(c, postResponses, total, page, pageSize)
|
||||
}
|
||||
@@ -659,8 +683,8 @@ func (h *PostHandler) Search(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(posts)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(posts, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
response.Paginated(c, postResponses, total, page, pageSize)
|
||||
}
|
||||
@@ -687,8 +711,8 @@ func (h *PostHandler) SearchByCursor(c *gin.Context) {
|
||||
}
|
||||
isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID)
|
||||
|
||||
// 转换为响应结构
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, isLikedMap, isFavoritedMap)
|
||||
chMap := h.channelMapForPosts(result.Items)
|
||||
postResponses := dto.BuildPostsWithInteractionResponse(result.Items, chMap, isLikedMap, isFavoritedMap)
|
||||
|
||||
// 构建游标分页响应
|
||||
cursorResp := &dto.PostCursorPageResponse{
|
||||
|
||||
@@ -48,6 +48,19 @@ func (r *ChannelRepository) GetByID(id string) (*model.Channel, error) {
|
||||
return &channel, nil
|
||||
}
|
||||
|
||||
// ListByIDs 按 ID 列表查询频道(用于帖子列表批量填充 channel 名称)
|
||||
func (r *ChannelRepository) ListByIDs(ids []string) ([]*model.Channel, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var list []*model.Channel
|
||||
err := r.db.Where("id IN ?", ids).Find(&list).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (r *ChannelRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.Channel{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
type ChannelService interface {
|
||||
ListActive() ([]*model.Channel, error)
|
||||
ListAll() ([]*model.Channel, error)
|
||||
MapByIDs(ids []string) (map[string]*model.Channel, error)
|
||||
Create(name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error)
|
||||
Update(id, name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error)
|
||||
Delete(id string) error
|
||||
@@ -29,6 +30,20 @@ func (s *channelServiceImpl) ListAll() ([]*model.Channel, error) {
|
||||
return s.channelRepo.ListAll()
|
||||
}
|
||||
|
||||
func (s *channelServiceImpl) MapByIDs(ids []string) (map[string]*model.Channel, error) {
|
||||
list, err := s.channelRepo.ListByIDs(ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]*model.Channel, len(list))
|
||||
for _, ch := range list {
|
||||
if ch != nil {
|
||||
m[ch.ID] = ch
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (s *channelServiceImpl) Create(name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error) {
|
||||
channel := &model.Channel{
|
||||
Name: name,
|
||||
|
||||
@@ -64,9 +64,10 @@ func ProvideUserHandler(
|
||||
func ProvidePostHandler(
|
||||
postService service.PostService,
|
||||
userService service.UserService,
|
||||
channelService service.ChannelService,
|
||||
logService *service.LogService,
|
||||
) *handler.PostHandler {
|
||||
h := handler.NewPostHandler(postService, userService)
|
||||
h := handler.NewPostHandler(postService, userService, channelService)
|
||||
if ps, ok := postService.(interface{ SetLogService(*service.LogService) }); ok {
|
||||
ps.SetLogService(logService)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user