init
This commit is contained in:
266
internal/handler/category_handler.go
Normal file
266
internal/handler/category_handler.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"accounting-app/pkg/api"
|
||||
"accounting-app/internal/models"
|
||||
"accounting-app/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CategoryHandler handles HTTP requests for category operations
|
||||
type CategoryHandler struct {
|
||||
categoryService *service.CategoryService
|
||||
}
|
||||
|
||||
// NewCategoryHandler creates a new CategoryHandler instance
|
||||
func NewCategoryHandler(categoryService *service.CategoryService) *CategoryHandler {
|
||||
return &CategoryHandler{
|
||||
categoryService: categoryService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCategory handles POST /api/v1/categories
|
||||
// Creates a new category with the provided data
|
||||
func (h *CategoryHandler) CreateCategory(c *gin.Context) {
|
||||
var input service.CategoryInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
api.ValidationError(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
input.UserID = userId.(uint)
|
||||
|
||||
category, err := h.categoryService.CreateCategory(input)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrInvalidParentCategory) {
|
||||
api.BadRequest(c, "Invalid parent category ID")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrParentTypeMismatch) {
|
||||
api.BadRequest(c, "Parent category type must match child category type")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrParentIsChild) {
|
||||
api.BadRequest(c, "Cannot set a child category as parent (only 2 levels allowed)")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to create category: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Created(c, category)
|
||||
}
|
||||
|
||||
// GetCategories handles GET /api/v1/categories
|
||||
// Returns a list of all categories, optionally filtered by type
|
||||
func (h *CategoryHandler) GetCategories(c *gin.Context) {
|
||||
// Check for type filter
|
||||
categoryType := c.Query("type")
|
||||
tree := c.Query("tree") == "true"
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var categories []models.Category
|
||||
var err error
|
||||
|
||||
if tree {
|
||||
// Return hierarchical tree structure
|
||||
if categoryType != "" {
|
||||
categories, err = h.categoryService.GetCategoryTreeByType(userId.(uint), models.CategoryType(categoryType))
|
||||
} else {
|
||||
categories, err = h.categoryService.GetCategoryTree(userId.(uint))
|
||||
}
|
||||
} else {
|
||||
// Return flat list
|
||||
if categoryType != "" {
|
||||
categories, err = h.categoryService.GetCategoriesByType(userId.(uint), models.CategoryType(categoryType))
|
||||
} else {
|
||||
categories, err = h.categoryService.GetAllCategories(userId.(uint))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get categories: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, categories)
|
||||
}
|
||||
|
||||
// GetCategory handles GET /api/v1/categories/:id
|
||||
// Returns a single category by ID
|
||||
func (h *CategoryHandler) GetCategory(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid category ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if children should be included
|
||||
withChildren := c.Query("children") == "true"
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var category *models.Category
|
||||
if withChildren {
|
||||
category, err = h.categoryService.GetCategoryWithChildren(userId.(uint), uint(id))
|
||||
} else {
|
||||
category, err = h.categoryService.GetCategory(userId.(uint), uint(id))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrCategoryNotFound) {
|
||||
api.NotFound(c, "Category not found")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to get category: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, category)
|
||||
}
|
||||
|
||||
// GetCategoryChildren handles GET /api/v1/categories/:id/children
|
||||
// Returns all child categories of a given parent
|
||||
func (h *CategoryHandler) GetCategoryChildren(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid category ID")
|
||||
return
|
||||
}
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
children, err := h.categoryService.GetChildCategories(userId.(uint), uint(id))
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrCategoryNotFound) {
|
||||
api.NotFound(c, "Category not found")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to get child categories: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, children)
|
||||
}
|
||||
|
||||
// UpdateCategory handles PUT /api/v1/categories/:id
|
||||
// Updates an existing category with the provided data
|
||||
func (h *CategoryHandler) UpdateCategory(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid category ID")
|
||||
return
|
||||
}
|
||||
|
||||
var input service.CategoryInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
api.ValidationError(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
input.UserID = userId.(uint)
|
||||
|
||||
category, err := h.categoryService.UpdateCategory(userId.(uint), uint(id), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrCategoryNotFound) {
|
||||
api.NotFound(c, "Category not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrInvalidParentCategory) {
|
||||
api.BadRequest(c, "Invalid parent category ID")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrParentTypeMismatch) {
|
||||
api.BadRequest(c, "Parent category type must match child category type")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrCircularReference) {
|
||||
api.BadRequest(c, "Cannot create circular reference in category hierarchy")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrParentIsChild) {
|
||||
api.BadRequest(c, "Cannot set a child category as parent (only 2 levels allowed)")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to update category: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, category)
|
||||
}
|
||||
|
||||
// DeleteCategory handles DELETE /api/v1/categories/:id
|
||||
// Deletes a category by ID
|
||||
func (h *CategoryHandler) DeleteCategory(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid category ID")
|
||||
return
|
||||
}
|
||||
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.categoryService.DeleteCategory(userId.(uint), uint(id))
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrCategoryNotFound) {
|
||||
api.NotFound(c, "Category not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrCategoryInUse) {
|
||||
api.Conflict(c, "Category is in use and cannot be deleted. Please remove associated transactions first.")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrCategoryHasChildren) {
|
||||
api.Conflict(c, "Category has child categories and cannot be deleted. Please remove child categories first.")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to delete category: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.NoContent(c)
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all category routes to the given router group
|
||||
func (h *CategoryHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||
categories := rg.Group("/categories")
|
||||
{
|
||||
categories.POST("", h.CreateCategory)
|
||||
categories.GET("", h.GetCategories)
|
||||
categories.GET("/:id", h.GetCategory)
|
||||
categories.GET("/:id/children", h.GetCategoryChildren)
|
||||
categories.PUT("/:id", h.UpdateCategory)
|
||||
categories.DELETE("/:id", h.DeleteCategory)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user