package logic import ( "context" "database/sql" "time" "git.cialloo.com/CiallooWeb/Blog/app/internal/svc" "git.cialloo.com/CiallooWeb/Blog/app/internal/types" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/zeromicro/go-zero/core/logx" ) type ListPostsLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // Get a list of blog posts func NewListPostsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPostsLogic { return &ListPostsLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *ListPostsLogic) ListPosts(req *types.ListPostsReq) (resp *types.ListPostsResp, err error) { // Set default values page := req.Page if page < 1 { page = 1 } pageSize := req.PageSize if pageSize < 1 || pageSize > 100 { pageSize = 10 // Default page size } offset := (page - 1) * pageSize // Get total count var totalCount int countQuery := `SELECT COUNT(*) FROM posts` err = l.svcCtx.DB.QueryRowContext(l.ctx, countQuery).Scan(&totalCount) if err != nil { l.Errorf("Failed to get total count: %v", err) return nil, err } // Get posts with pagination postsQuery := ` SELECT p.id, p.title, p.created_at, p.updated_at, p.cover_id FROM posts p ORDER BY p.created_at DESC LIMIT $1 OFFSET $2 ` rows, err := l.svcCtx.DB.QueryContext(l.ctx, postsQuery, pageSize, offset) if err != nil { l.Errorf("Failed to get posts: %v", err) return nil, err } defer rows.Close() var posts []types.ListPostsRespPosts for rows.Next() { var post types.ListPostsRespPosts var coverID sql.NullInt64 err := rows.Scan(&post.PostId, &post.Title, &post.CreatedAt, &post.UpdatedAt, &coverID) if err != nil { l.Errorf("Failed to scan post: %v", err) continue } // If cover image exists, get its URL if coverID.Valid { coverQuery := `SELECT file_key FROM files WHERE id = $1` var fileKey string err = l.svcCtx.DB.QueryRowContext(l.ctx, coverQuery, coverID.Int64).Scan(&fileKey) if err == nil { // Generate presigned URL for cover image expiration := time.Duration(l.svcCtx.Config.S3.PresignedURLExpiration) * time.Second presignClient := s3.NewPresignClient(l.svcCtx.S3Client) getObjectInput := &s3.GetObjectInput{ Bucket: &l.svcCtx.Config.S3.Bucket, Key: &fileKey, } presignedReq, err := presignClient.PresignGetObject(l.ctx, getObjectInput, func(opts *s3.PresignOptions) { opts.Expires = expiration }) if err == nil { post.CoverImageUrl = presignedReq.URL } } } // Convert timestamps to Unix milliseconds post.CreatedAt = post.CreatedAt * 1000 post.UpdatedAt = post.UpdatedAt * 1000 posts = append(posts, post) } return &types.ListPostsResp{ Posts: posts, TotalCount: totalCount, }, nil }