From 6af6aaa9d06b2c79ed6bed2f5f9910edc4e46581 Mon Sep 17 00:00:00 2001 From: lafay <2021211506@stu.hit.edu.cn> Date: Fri, 10 Apr 2026 01:13:58 +0800 Subject: [PATCH] feat(posts): add preview URL fields for post images Add PreviewURL and PreviewURLLarge fields to PostImage model and responses. Image data now supports format "url|preview_url|preview_url_large" for storing multiple image variants. Also change comment cursor pagination to sort by created_at ASC (oldest first) to display earliest comments as first floor, and make AdminReviewVerificationRequest.Approve field optional. --- internal/dto/dto.go | 2 +- internal/dto/post_converter.go | 12 ++++---- internal/repository/comment_repo.go | 8 +++--- internal/repository/post_repo.go | 39 ++++++++++++++++++++------ internal/service/admin_post_service.go | 24 +++++++++------- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/internal/dto/dto.go b/internal/dto/dto.go index 476fda5..bb91481 100644 --- a/internal/dto/dto.go +++ b/internal/dto/dto.go @@ -1404,7 +1404,7 @@ type AdminVerificationDetailResponse struct { // AdminReviewVerificationRequest 管理端审核认证请求 type AdminReviewVerificationRequest struct { - Approve bool `json:"approve" binding:"required"` + Approve bool `json:"approve"` RejectReason string `json:"reject_reason" binding:"omitempty,max=500"` } diff --git a/internal/dto/post_converter.go b/internal/dto/post_converter.go index 5c717f5..6c15486 100644 --- a/internal/dto/post_converter.go +++ b/internal/dto/post_converter.go @@ -14,11 +14,13 @@ func ConvertPostImageToResponse(img *model.PostImage) PostImageResponse { return PostImageResponse{} } return PostImageResponse{ - ID: img.ID, - URL: img.URL, - ThumbnailURL: img.ThumbnailURL, - Width: img.Width, - Height: img.Height, + ID: img.ID, + URL: img.URL, + ThumbnailURL: img.ThumbnailURL, + PreviewURL: img.PreviewURL, + PreviewURLLarge: img.PreviewURLLarge, + Width: img.Width, + Height: img.Height, } } diff --git a/internal/repository/comment_repo.go b/internal/repository/comment_repo.go index 7835ac5..4548e84 100644 --- a/internal/repository/comment_repo.go +++ b/internal/repository/comment_repo.go @@ -495,14 +495,14 @@ func (r *commentRepository) GetCommentsByPostIDForAdmin(postID string, page, pag // ========== Cursor Pagination Methods ========== // GetCommentsByCursor 游标分页获取帖子评论(包含回复) -// 排序方式:created_at DESC +// 排序方式:created_at ASC(最早发布的为一楼) func (r *commentRepository) GetCommentsByCursor(ctx context.Context, postID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Comment], error) { // 构建基础查询 - 只查询顶级评论 query := r.db.WithContext(ctx).Model(&model.Comment{}). Where("post_id = ? AND parent_id IS NULL AND status = ?", postID, model.CommentStatusPublished) // 使用游标构建器 - builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc). + builder := cursor.NewBuilder(query, cursor.SortByCreatedAtAsc). WithCursor(req.Cursor, req.Direction). WithPageSize(req.PageSize) @@ -539,7 +539,7 @@ func (r *commentRepository) GetCommentsByCursor(ctx context.Context, postID stri nextCursor = cursor.NewCursor( cursor.FormatTime(lastComment.CreatedAt), lastComment.ID, - cursor.SortByCreatedAtDesc, + cursor.SortByCreatedAtAsc, ).Encode() } // 上一页游标 @@ -547,7 +547,7 @@ func (r *commentRepository) GetCommentsByCursor(ctx context.Context, postID stri prevCursor = cursor.NewCursor( cursor.FormatTime(firstComment.CreatedAt), firstComment.ID, - cursor.SortByCreatedAtDesc, + cursor.SortByCreatedAtAsc, ).Encode() } diff --git a/internal/repository/post_repo.go b/internal/repository/post_repo.go index 0ef7e4f..e072c0b 100644 --- a/internal/repository/post_repo.go +++ b/internal/repository/post_repo.go @@ -148,11 +148,22 @@ func (r *postRepository) UpdateWithImages(post *model.Post, images *[]string) er return err } - for i, url := range *images { + for i, imageData := range *images { + parts := strings.Split(imageData, "|") + url := parts[0] + var previewURL, previewURLLarge string + if len(parts) >= 2 && parts[1] != "" { + previewURL = parts[1] + } + if len(parts) >= 3 && parts[2] != "" { + previewURLLarge = parts[2] + } image := &model.PostImage{ - PostID: post.ID, - URL: url, - SortOrder: i, + PostID: post.ID, + URL: url, + PreviewURL: previewURL, + PreviewURLLarge: previewURLLarge, + SortOrder: i, } if err := tx.Create(image).Error; err != nil { return err @@ -695,11 +706,23 @@ func (r *postRepository) createWithTx(tx *gorm.DB, post *model.Post, images []st } // 创建图片记录 - for i, url := range images { + // 支持格式:["url", "url|preview_url|preview_url_large", ...] + for i, imageData := range images { + parts := strings.Split(imageData, "|") + url := parts[0] + var previewURL, previewURLLarge string + if len(parts) >= 2 && parts[1] != "" { + previewURL = parts[1] + } + if len(parts) >= 3 && parts[2] != "" { + previewURLLarge = parts[2] + } image := &model.PostImage{ - PostID: post.ID, - URL: url, - SortOrder: i, + PostID: post.ID, + URL: url, + PreviewURL: previewURL, + PreviewURLLarge: previewURLLarge, + SortOrder: i, } if err := tx.Create(image).Error; err != nil { return err diff --git a/internal/service/admin_post_service.go b/internal/service/admin_post_service.go index 7a00482..1aab3ec 100644 --- a/internal/service/admin_post_service.go +++ b/internal/service/admin_post_service.go @@ -256,11 +256,13 @@ 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, + ID: img.ID, + URL: img.URL, + ThumbnailURL: img.ThumbnailURL, + PreviewURL: img.PreviewURL, + PreviewURLLarge: img.PreviewURLLarge, + Width: img.Width, + Height: img.Height, } } @@ -298,11 +300,13 @@ func convertPostToAdminDetailResponse(post *model.Post) *dto.AdminPostDetailResp 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, + ID: img.ID, + URL: img.URL, + ThumbnailURL: img.ThumbnailURL, + PreviewURL: img.PreviewURL, + PreviewURLLarge: img.PreviewURLLarge, + Width: img.Width, + Height: img.Height, } }