diff --git a/app/internal/logic/createpostlogic.go b/app/internal/logic/createpostlogic.go index 9befe4d..787fc27 100644 --- a/app/internal/logic/createpostlogic.go +++ b/app/internal/logic/createpostlogic.go @@ -30,12 +30,12 @@ func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq) (resp *types.Crea var coverID sql.NullInt64 // If cover image key is provided, get the file ID from files table - if req.Cover_image_key != "" { + if req.CoverImageKey != "" { query := `SELECT id FROM files WHERE file_key = $1` - err := l.svcCtx.DB.QueryRowContext(l.ctx, query, req.Cover_image_key).Scan(&coverID.Int64) + err := l.svcCtx.DB.QueryRowContext(l.ctx, query, req.CoverImageKey).Scan(&coverID.Int64) if err != nil { if err == sql.ErrNoRows { - l.Errorf("Cover image file not found with key: %s", req.Cover_image_key) + l.Errorf("Cover image file not found with key: %s", req.CoverImageKey) return nil, fmt.Errorf("cover image not found") } l.Errorf("Failed to get cover image file: %v", err) @@ -54,6 +54,6 @@ func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq) (resp *types.Crea } return &types.CreatePostResp{ - Post_id: fmt.Sprintf("%d", postID), + PostId: fmt.Sprintf("%d", postID), }, nil } diff --git a/app/internal/logic/deletepostlogic.go b/app/internal/logic/deletepostlogic.go index 949aee7..2492900 100644 --- a/app/internal/logic/deletepostlogic.go +++ b/app/internal/logic/deletepostlogic.go @@ -28,7 +28,7 @@ func NewDeletePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete func (l *DeletePostLogic) DeletePost(req *types.DeletePostReq) (resp *types.DeletePostResp, err error) { // Delete post from database query := `DELETE FROM posts WHERE id = $1` - result, err := l.svcCtx.DB.ExecContext(l.ctx, query, req.Post_id) + result, err := l.svcCtx.DB.ExecContext(l.ctx, query, req.PostId) if err != nil { l.Errorf("Failed to delete post: %v", err) return nil, err @@ -41,7 +41,7 @@ func (l *DeletePostLogic) DeletePost(req *types.DeletePostReq) (resp *types.Dele return nil, err } if rowsAffected == 0 { - l.Errorf("Post not found with id: %s", req.Post_id) + l.Errorf("Post not found with id: %s", req.PostId) return nil, fmt.Errorf("post not found") } diff --git a/app/internal/logic/downloadpresignedurllogic.go b/app/internal/logic/downloadpresignedurllogic.go index 0bb17a9..634f8d7 100644 --- a/app/internal/logic/downloadpresignedurllogic.go +++ b/app/internal/logic/downloadpresignedurllogic.go @@ -37,7 +37,7 @@ func (l *DownloadPresignedURLLogic) DownloadPresignedURL(req *types.DownloadPres // Generate presigned GET URL getObjectInput := &s3.GetObjectInput{ Bucket: &l.svcCtx.Config.S3.Bucket, - Key: &req.File_key, + Key: &req.FileKey, } presignedReq, err := presignClient.PresignGetObject(l.ctx, getObjectInput, func(opts *s3.PresignOptions) { @@ -49,7 +49,7 @@ func (l *DownloadPresignedURLLogic) DownloadPresignedURL(req *types.DownloadPres } return &types.DownloadPresignedURLResp{ - Url: presignedReq.URL, - Expire_at: expireAt, + Url: presignedReq.URL, + ExpireAt: expireAt, }, nil } diff --git a/app/internal/logic/editpostlogic.go b/app/internal/logic/editpostlogic.go index 70b11b2..641dd08 100644 --- a/app/internal/logic/editpostlogic.go +++ b/app/internal/logic/editpostlogic.go @@ -30,12 +30,12 @@ func (l *EditPostLogic) EditPost(req *types.EditPostReq) (resp *types.EditPostRe var coverID sql.NullInt64 // If cover image key is provided, get the file ID from files table - if req.Cover_image_key != "" { + if req.CoverImageKey != "" { query := `SELECT id FROM files WHERE file_key = $1` - err := l.svcCtx.DB.QueryRowContext(l.ctx, query, req.Cover_image_key).Scan(&coverID.Int64) + err := l.svcCtx.DB.QueryRowContext(l.ctx, query, req.CoverImageKey).Scan(&coverID.Int64) if err != nil { if err == sql.ErrNoRows { - l.Errorf("Cover image file not found with key: %s", req.Cover_image_key) + l.Errorf("Cover image file not found with key: %s", req.CoverImageKey) return nil, fmt.Errorf("cover image not found") } l.Errorf("Failed to get cover image file: %v", err) @@ -46,7 +46,7 @@ func (l *EditPostLogic) EditPost(req *types.EditPostReq) (resp *types.EditPostRe // Update post with updated_at timestamp query := `UPDATE posts SET title = $1, content = $2, cover_id = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4` - result, err := l.svcCtx.DB.ExecContext(l.ctx, query, req.Title, req.Content, coverID, req.Post_id) + result, err := l.svcCtx.DB.ExecContext(l.ctx, query, req.Title, req.Content, coverID, req.PostId) if err != nil { l.Errorf("Failed to update post: %v", err) return nil, err @@ -59,7 +59,7 @@ func (l *EditPostLogic) EditPost(req *types.EditPostReq) (resp *types.EditPostRe return nil, err } if rowsAffected == 0 { - l.Errorf("Post not found with id: %s", req.Post_id) + l.Errorf("Post not found with id: %s", req.PostId) return nil, fmt.Errorf("post not found") } diff --git a/app/internal/logic/getpostlogic.go b/app/internal/logic/getpostlogic.go index 353b036..e0feadf 100644 --- a/app/internal/logic/getpostlogic.go +++ b/app/internal/logic/getpostlogic.go @@ -2,10 +2,14 @@ package logic import ( "context" + "database/sql" + "fmt" + "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" ) @@ -25,7 +29,52 @@ func NewGetPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostLo } func (l *GetPostLogic) GetPost(req *types.GetPostReq) (resp *types.GetPostResp, err error) { - // todo: add your logic here and delete this line + var post types.GetPostResp + var coverID sql.NullInt64 - return + // Query post with cover image + query := ` + SELECT p.id, p.title, p.content, p.created_at, p.updated_at, p.cover_id + FROM posts p + WHERE p.id = $1 + ` + err = l.svcCtx.DB.QueryRowContext(l.ctx, query, req.PostId).Scan( + &post.PostId, &post.Title, &post.Content, &post.CreatedAt, &post.UpdatedAt, &coverID, + ) + if err != nil { + if err == sql.ErrNoRows { + l.Errorf("Post not found with id: %s", req.PostId) + return nil, fmt.Errorf("post not found") + } + l.Errorf("Failed to get post: %v", err) + return nil, err + } + + // 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 + post.CreatedAt = post.CreatedAt * 1000 // Convert to milliseconds if needed + post.UpdatedAt = post.UpdatedAt * 1000 + + return &post, nil } diff --git a/app/internal/logic/listpostslogic.go b/app/internal/logic/listpostslogic.go index 1c2ca68..7df17ad 100644 --- a/app/internal/logic/listpostslogic.go +++ b/app/internal/logic/listpostslogic.go @@ -2,10 +2,13 @@ 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" ) @@ -25,7 +28,82 @@ func NewListPostsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPos } func (l *ListPostsLogic) ListPosts(req *types.ListPostsReq) (resp *types.ListPostsResp, err error) { - // todo: add your logic here and delete this line + // 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 - return + // 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 } diff --git a/app/internal/logic/listtagslogic.go b/app/internal/logic/listtagslogic.go index ec52ac6..b62d601 100644 --- a/app/internal/logic/listtagslogic.go +++ b/app/internal/logic/listtagslogic.go @@ -25,7 +25,27 @@ func NewListTagsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListTags } func (l *ListTagsLogic) ListTags(req *types.ListTagsReq) (resp *types.ListTagsResp, err error) { - // todo: add your logic here and delete this line + // Query all hashtags + query := `SELECT id, name FROM hashtags ORDER BY name` + rows, err := l.svcCtx.DB.QueryContext(l.ctx, query) + if err != nil { + l.Errorf("Failed to get tags: %v", err) + return nil, err + } + defer rows.Close() - return + var tags []types.ListTagsRespTags + for rows.Next() { + var tag types.ListTagsRespTags + err := rows.Scan(&tag.TagId, &tag.TagName) + if err != nil { + l.Errorf("Failed to scan tag: %v", err) + continue + } + tags = append(tags, tag) + } + + return &types.ListTagsResp{ + Tags: tags, + }, nil } diff --git a/app/internal/logic/pinglogic.go b/app/internal/logic/pinglogic.go index 1b640cb..bf2acb9 100644 --- a/app/internal/logic/pinglogic.go +++ b/app/internal/logic/pinglogic.go @@ -26,6 +26,6 @@ func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic { func (l *PingLogic) Ping(req *types.PingReq) (resp *types.PingResp, err error) { return &types.PingResp{ - Ok: true, + OK: true, }, nil } diff --git a/app/internal/logic/uploadpresignedurllogic.go b/app/internal/logic/uploadpresignedurllogic.go index 6a40ff0..d4a1dae 100644 --- a/app/internal/logic/uploadpresignedurllogic.go +++ b/app/internal/logic/uploadpresignedurllogic.go @@ -31,8 +31,8 @@ func NewUploadPresignedURLLogic(ctx context.Context, svcCtx *svc.ServiceContext) func (l *UploadPresignedURLLogic) UploadPresignedURL(req *types.UploadPresignedURLReq) (resp *types.UploadPresignedURLResp, err error) { // Generate unique file key - ext := filepath.Ext(req.File_name) - fileKey := fmt.Sprintf("%s%s", uuid.New().String(), ext) + ext := filepath.Ext(req.FileName) + fileKey := fmt.Sprintf("uploads/%s%s", uuid.New().String(), ext) // Calculate expiration time expiration := time.Duration(l.svcCtx.Config.S3.PresignedURLExpiration) * time.Second @@ -57,15 +57,15 @@ func (l *UploadPresignedURLLogic) UploadPresignedURL(req *types.UploadPresignedU // Insert record into tmpfiles table query := `INSERT INTO tmpfiles (file_name, key, presigned_url, expiration) VALUES ($1, $2, $3, $4)` - _, err = l.svcCtx.DB.ExecContext(l.ctx, query, req.File_name, fileKey, presignedReq.URL, expireAt) + _, err = l.svcCtx.DB.ExecContext(l.ctx, query, req.FileName, fileKey, presignedReq.URL, expireAt) if err != nil { l.Errorf("Failed to insert tmpfile record: %v", err) return nil, err } return &types.UploadPresignedURLResp{ - Url: presignedReq.URL, - File_key: fileKey, - Expire_at: expireAt.Unix(), + Url: presignedReq.URL, + FileKey: fileKey, + ExpireAt: expireAt.Unix(), }, nil }