init
This commit is contained in:
199
internal/handler/image_handler.go
Normal file
199
internal/handler/image_handler.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"accounting-app/pkg/api"
|
||||
"accounting-app/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ImageHandler handles HTTP requests for transaction image operations
|
||||
// Feature: accounting-feature-upgrade
|
||||
// Validates: Requirements 4.1-4.13
|
||||
type ImageHandler struct {
|
||||
imageService *service.ImageService
|
||||
}
|
||||
|
||||
// NewImageHandler creates a new ImageHandler instance
|
||||
func NewImageHandler(imageService *service.ImageService) *ImageHandler {
|
||||
return &ImageHandler{
|
||||
imageService: imageService,
|
||||
}
|
||||
}
|
||||
|
||||
// UploadImage handles POST /api/v1/transactions/:id/images
|
||||
// Uploads an image attachment for a transaction
|
||||
// Validates: Requirements 4.3, 4.4, 4.9-4.13
|
||||
func (h *ImageHandler) UploadImage(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse transaction ID
|
||||
transactionID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid transaction ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Get compression level from query parameter (default: medium)
|
||||
compressionStr := c.DefaultQuery("compression", "medium")
|
||||
compression := service.CompressionLevel(compressionStr)
|
||||
|
||||
// Validate compression level
|
||||
if compression != service.CompressionLow &&
|
||||
compression != service.CompressionMedium &&
|
||||
compression != service.CompressionHigh {
|
||||
api.BadRequest(c, "Invalid compression level. Must be 'low', 'medium', or 'high'")
|
||||
return
|
||||
}
|
||||
|
||||
// Get uploaded file
|
||||
file, err := c.FormFile("image")
|
||||
if err != nil {
|
||||
api.BadRequest(c, "No image file provided")
|
||||
return
|
||||
}
|
||||
|
||||
// Upload image
|
||||
input := service.UploadImageInput{
|
||||
UserID: userId.(uint),
|
||||
TransactionID: uint(transactionID),
|
||||
File: file,
|
||||
Compression: compression,
|
||||
}
|
||||
|
||||
image, err := h.imageService.UploadImage(input)
|
||||
if err != nil {
|
||||
handleImageError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Created(c, image)
|
||||
}
|
||||
|
||||
// GetImage handles GET /api/v1/images/:id
|
||||
// Retrieves an image file by ID
|
||||
// Validates: Requirements 4.8
|
||||
func (h *ImageHandler) GetImage(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse image ID
|
||||
imageID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid image ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Get image record
|
||||
image, err := h.imageService.GetImage(userId.(uint), uint(imageID))
|
||||
if err != nil {
|
||||
handleImageError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Serve the file
|
||||
c.File(image.FilePath)
|
||||
}
|
||||
|
||||
// GetTransactionImages handles GET /api/v1/transactions/:id/images
|
||||
// Retrieves all images for a transaction
|
||||
// Validates: Requirements 4.8
|
||||
func (h *ImageHandler) GetTransactionImages(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse transaction ID
|
||||
transactionID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid transaction ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Get images
|
||||
images, err := h.imageService.GetImagesByTransaction(userId.(uint), uint(transactionID))
|
||||
if err != nil {
|
||||
handleImageError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, images)
|
||||
}
|
||||
|
||||
// DeleteImage handles DELETE /api/v1/transactions/:id/images/:imageId
|
||||
// Deletes an image attachment
|
||||
// Validates: Requirements 4.7
|
||||
func (h *ImageHandler) DeleteImage(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse transaction ID
|
||||
transactionID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid transaction ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse image ID
|
||||
imageID, err := strconv.ParseUint(c.Param("imageId"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid image ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Delete image
|
||||
err = h.imageService.DeleteImage(userId.(uint), uint(imageID), uint(transactionID))
|
||||
if err != nil {
|
||||
handleImageError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.NoContent(c)
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all image routes to the given router group
|
||||
func (h *ImageHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||
// Transaction image routes
|
||||
transactions := rg.Group("/transactions")
|
||||
{
|
||||
transactions.POST("/:id/images", h.UploadImage)
|
||||
transactions.GET("/:id/images", h.GetTransactionImages)
|
||||
transactions.DELETE("/:id/images/:imageId", h.DeleteImage)
|
||||
}
|
||||
|
||||
// Direct image access route
|
||||
rg.GET("/images/:id", h.GetImage)
|
||||
}
|
||||
|
||||
// handleImageError handles common image service errors
|
||||
func handleImageError(c *gin.Context, err error) {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrInvalidImageFormat):
|
||||
api.BadRequest(c, "Invalid image format. Only JPEG, PNG, and HEIC are supported")
|
||||
case errors.Is(err, service.ErrImageTooLarge):
|
||||
api.RequestEntityTooLarge(c, "Image size exceeds 10MB limit")
|
||||
case errors.Is(err, service.ErrMaxImagesExceeded):
|
||||
api.BadRequest(c, "Maximum 9 images per transaction")
|
||||
case errors.Is(err, service.ErrImageTransactionNotFound):
|
||||
api.NotFound(c, "Transaction not found")
|
||||
case errors.Is(err, service.ErrImageNotFound):
|
||||
api.NotFound(c, "Image not found")
|
||||
default:
|
||||
api.InternalError(c, "Failed to process image: "+err.Error())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user