package service import ( "context" "carrot_bbs/internal/cache" "carrot_bbs/internal/dto" "carrot_bbs/internal/model" "carrot_bbs/internal/repository" ) // AdminPostService 管理端帖子服务接口 type AdminPostService interface { // GetPostList 获取帖子列表(分页、搜索、状态筛选) GetPostList(ctx context.Context, page, pageSize int, keyword, status, authorID, startDate, endDate string) ([]dto.AdminPostListResponse, int64, error) // GetPostDetail 获取帖子详情 GetPostDetail(ctx context.Context, postID string) (*dto.AdminPostDetailResponse, error) // DeletePost 删除帖子 DeletePost(ctx context.Context, postID string) error // ModeratePost 审核帖子 ModeratePost(ctx context.Context, postID string, status model.PostStatus, reason, reviewedBy string) (*dto.AdminPostDetailResponse, error) // BatchDeletePosts 批量删除帖子 BatchDeletePosts(ctx context.Context, ids []string) (*dto.AdminBatchOperationResponse, error) // BatchModeratePosts 批量审核帖子 BatchModeratePosts(ctx context.Context, ids []string, status model.PostStatus, reviewedBy string) (*dto.AdminBatchOperationResponse, error) // SetPostPin 置顶/取消置顶帖子 SetPostPin(ctx context.Context, postID string, isPinned bool) (*dto.AdminPostDetailResponse, error) // SetPostFeature 加精/取消加精帖子 SetPostFeature(ctx context.Context, postID string, isFeatured bool) (*dto.AdminPostDetailResponse, error) // 日志服务设置 SetLogService(logService *LogService) } // adminPostServiceImpl 管理端帖子服务实现 type adminPostServiceImpl struct { postRepo *repository.PostRepository cache cache.Cache logService *LogService } // NewAdminPostService 创建管理端帖子服务 func NewAdminPostService(postRepo *repository.PostRepository, cacheBackend cache.Cache) AdminPostService { return &adminPostServiceImpl{ postRepo: postRepo, cache: cacheBackend, logService: nil, } } // SetLogService 设置日志服务 func (s *adminPostServiceImpl) SetLogService(logService *LogService) { s.logService = logService } // GetPostList 获取帖子列表 func (s *adminPostServiceImpl) GetPostList(ctx context.Context, page, pageSize int, keyword, status, authorID, startDate, endDate string) ([]dto.AdminPostListResponse, int64, error) { query := repository.AdminPostListQuery{ Page: page, PageSize: pageSize, Keyword: keyword, Status: status, AuthorID: authorID, StartDate: startDate, EndDate: endDate, } posts, total, err := s.postRepo.AdminList(query) if err != nil { return nil, 0, err } responses := make([]dto.AdminPostListResponse, len(posts)) for i, post := range posts { responses[i] = convertPostToAdminListResponse(post) } return responses, total, nil } // GetPostDetail 获取帖子详情 func (s *adminPostServiceImpl) GetPostDetail(ctx context.Context, postID string) (*dto.AdminPostDetailResponse, error) { post, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } return convertPostToAdminDetailResponse(post), nil } // DeletePost 删除帖子 func (s *adminPostServiceImpl) DeletePost(ctx context.Context, postID string) error { post, err := s.postRepo.GetByID(postID) if err != nil { return err } err = s.postRepo.Delete(postID) if err != nil { return err } // 失效缓存 cache.InvalidatePostDetail(s.cache, postID) cache.InvalidatePostList(s.cache) // 记录操作日志 if s.logService != nil { s.logService.OperationLog.RecordOperation(&model.OperationLog{ UserID: post.UserID, Operation: string(model.OpAdminDeletePost), Module: "admin", TargetType: "post", TargetID: postID, Status: "success", }) } return nil } // ModeratePost 审核帖子 func (s *adminPostServiceImpl) ModeratePost(ctx context.Context, postID string, status model.PostStatus, reason, reviewedBy string) (*dto.AdminPostDetailResponse, error) { // 先检查帖子是否存在 _, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } // 更新审核状态 if err := s.postRepo.UpdateModerationStatus(postID, status, reason, reviewedBy); err != nil { return nil, err } // 失效缓存 cache.InvalidatePostDetail(s.cache, postID) cache.InvalidatePostList(s.cache) // 重新获取帖子信息 post, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } return convertPostToAdminDetailResponse(post), nil } // BatchDeletePosts 批量删除帖子 func (s *adminPostServiceImpl) BatchDeletePosts(ctx context.Context, ids []string) (*dto.AdminBatchOperationResponse, error) { failedIDs, err := s.postRepo.BatchDelete(ids) if err != nil { return &dto.AdminBatchOperationResponse{ Success: false, Message: "批量删除失败", Failed: ids, }, err } // 失效缓存 for _, id := range ids { cache.InvalidatePostDetail(s.cache, id) } cache.InvalidatePostList(s.cache) if len(failedIDs) == 0 { return &dto.AdminBatchOperationResponse{ Success: true, Message: "批量删除成功", }, nil } return &dto.AdminBatchOperationResponse{ Success: true, Message: "部分帖子删除成功", Failed: failedIDs, }, nil } // BatchModeratePosts 批量审核帖子 func (s *adminPostServiceImpl) BatchModeratePosts(ctx context.Context, ids []string, status model.PostStatus, reviewedBy string) (*dto.AdminBatchOperationResponse, error) { failedIDs, err := s.postRepo.BatchUpdateStatus(ids, status, reviewedBy) if err != nil { return &dto.AdminBatchOperationResponse{ Success: false, Message: "批量审核失败", Failed: ids, }, err } // 失效缓存 for _, id := range ids { cache.InvalidatePostDetail(s.cache, id) } cache.InvalidatePostList(s.cache) if len(failedIDs) == 0 { return &dto.AdminBatchOperationResponse{ Success: true, Message: "批量审核成功", }, nil } return &dto.AdminBatchOperationResponse{ Success: true, Message: "部分帖子审核成功", Failed: failedIDs, }, nil } // SetPostPin 置顶/取消置顶帖子 func (s *adminPostServiceImpl) SetPostPin(ctx context.Context, postID string, isPinned bool) (*dto.AdminPostDetailResponse, error) { // 先检查帖子是否存在 _, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } // 更新置顶状态 if err := s.postRepo.UpdatePinStatus(postID, isPinned); err != nil { return nil, err } // 失效缓存 cache.InvalidatePostDetail(s.cache, postID) cache.InvalidatePostList(s.cache) // 重新获取帖子信息 post, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } return convertPostToAdminDetailResponse(post), nil } // SetPostFeature 加精/取消加精帖子 func (s *adminPostServiceImpl) SetPostFeature(ctx context.Context, postID string, isFeatured bool) (*dto.AdminPostDetailResponse, error) { // 先检查帖子是否存在 _, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } // 更新加精状态 if err := s.postRepo.UpdateFeatureStatus(postID, isFeatured); err != nil { return nil, err } // 失效缓存 cache.InvalidatePostDetail(s.cache, postID) cache.InvalidatePostList(s.cache) // 重新获取帖子信息 post, err := s.postRepo.GetByIDForAdmin(postID) if err != nil { return nil, err } return convertPostToAdminDetailResponse(post), nil } // convertPostToAdminListResponse 转换帖子为管理端列表响应 func convertPostToAdminListResponse(post *model.Post) dto.AdminPostListResponse { images := make([]dto.PostImageResponse, len(post.Images)) for i, img := range post.Images { images[i] = dto.PostImageResponse{ ID: img.ID, URL: img.URL, ThumbnailURL: img.ThumbnailURL, Width: img.Width, Height: img.Height, } } var author *dto.UserResponse if post.User != nil { author = dto.ConvertUserToResponse(post.User) } return dto.AdminPostListResponse{ 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, ViewsCount: post.ViewsCount, IsPinned: post.IsPinned, IsFeatured: post.IsFeatured, IsLocked: post.IsLocked, RejectReason: post.RejectReason, ReviewedAt: dto.FormatTimePointer(post.ReviewedAt), ReviewedBy: post.ReviewedBy, CreatedAt: post.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), UpdatedAt: post.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), ContentEditedAt: dto.FormatTimePointer(post.ContentEditedAt), Author: author, } } // convertPostToAdminDetailResponse 转换帖子为管理端详情响应 func convertPostToAdminDetailResponse(post *model.Post) *dto.AdminPostDetailResponse { images := make([]dto.PostImageResponse, len(post.Images)) for i, img := range post.Images { images[i] = dto.PostImageResponse{ ID: img.ID, URL: img.URL, ThumbnailURL: img.ThumbnailURL, Width: img.Width, Height: img.Height, } } var author *dto.UserResponse if post.User != nil { author = dto.ConvertUserToResponse(post.User) } return &dto.AdminPostDetailResponse{ 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, IsFeatured: post.IsFeatured, IsLocked: post.IsLocked, IsVote: post.IsVote, RejectReason: post.RejectReason, ReviewedAt: dto.FormatTimePointer(post.ReviewedAt), ReviewedBy: post.ReviewedBy, CreatedAt: post.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), UpdatedAt: post.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), ContentEditedAt: dto.FormatTimePointer(post.ContentEditedAt), Author: author, } }