Files
Novault-backend/internal/handler/allocation_rule_handler.go
2026-01-25 21:59:00 +08:00

309 lines
8.1 KiB
Go

package handler
import (
"strconv"
"accounting-app/pkg/api"
"accounting-app/internal/models"
"accounting-app/internal/service"
"github.com/gin-gonic/gin"
)
// AllocationRuleHandler handles HTTP requests for allocation rule operations
type AllocationRuleHandler struct {
service *service.AllocationRuleService
}
// NewAllocationRuleHandler creates a new AllocationRuleHandler instance
func NewAllocationRuleHandler(service *service.AllocationRuleService) *AllocationRuleHandler {
return &AllocationRuleHandler{
service: service,
}
}
// RegisterRoutes registers allocation rule-related routes
func (h *AllocationRuleHandler) RegisterRoutes(rg *gin.RouterGroup) {
allocationRules := rg.Group("/allocation-rules")
{
allocationRules.POST("", h.CreateAllocationRule)
allocationRules.GET("", h.GetAllAllocationRules)
allocationRules.GET("/suggest", h.SuggestAllocationForIncome)
allocationRules.GET("/:id", h.GetAllocationRule)
allocationRules.PUT("/:id", h.UpdateAllocationRule)
allocationRules.DELETE("/:id", h.DeleteAllocationRule)
allocationRules.POST("/:id/apply", h.ApplyAllocationRule)
}
}
// CreateAllocationRule handles POST /api/v1/allocation-rules
func (h *AllocationRuleHandler) CreateAllocationRule(c *gin.Context) {
var input service.AllocationRuleInput
if err := c.ShouldBindJSON(&input); err != nil {
api.ValidationError(c, err.Error())
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
input.UserID = userID.(uint)
rule, err := h.service.CreateAllocationRule(input)
if err != nil {
switch err {
case service.ErrInvalidTriggerType:
api.BadRequest(c, err.Error())
case service.ErrInvalidTargetType:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationPercentage:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationAmount:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationTarget:
api.BadRequest(c, err.Error())
case service.ErrTotalPercentageExceeds100:
api.BadRequest(c, err.Error())
case service.ErrTargetNotFound:
api.BadRequest(c, err.Error())
default:
api.InternalError(c, "Failed to create allocation rule")
}
return
}
api.Created(c, rule)
}
// GetAllocationRule handles GET /api/v1/allocation-rules/:id
func (h *AllocationRuleHandler) GetAllocationRule(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid allocation rule ID")
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
rule, err := h.service.GetAllocationRule(userID.(uint), uint(id))
if err != nil {
if err == service.ErrAllocationRuleNotFound {
api.NotFound(c, "Allocation rule not found")
return
}
api.InternalError(c, "Failed to get allocation rule")
return
}
api.Success(c, rule)
}
// GetAllAllocationRules handles GET /api/v1/allocation-rules
func (h *AllocationRuleHandler) GetAllAllocationRules(c *gin.Context) {
// Check if we should filter by active status
activeOnly := c.Query("active") == "true"
var rules []models.AllocationRule
var err error
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
if activeOnly {
rules, err = h.service.GetActiveAllocationRules(userID.(uint))
} else {
rules, err = h.service.GetAllAllocationRules(userID.(uint))
}
if err != nil {
api.InternalError(c, "Failed to get allocation rules")
return
}
api.Success(c, rules)
}
// UpdateAllocationRule handles PUT /api/v1/allocation-rules/:id
func (h *AllocationRuleHandler) UpdateAllocationRule(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid allocation rule ID")
return
}
var input service.AllocationRuleInput
if err := c.ShouldBindJSON(&input); err != nil {
api.ValidationError(c, err.Error())
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
input.UserID = userID.(uint)
rule, err := h.service.UpdateAllocationRule(userID.(uint), uint(id), input)
if err != nil {
switch err {
case service.ErrAllocationRuleNotFound:
api.NotFound(c, "Allocation rule not found")
case service.ErrInvalidTriggerType:
api.BadRequest(c, err.Error())
case service.ErrInvalidTargetType:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationPercentage:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationAmount:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationTarget:
api.BadRequest(c, err.Error())
case service.ErrTotalPercentageExceeds100:
api.BadRequest(c, err.Error())
case service.ErrTargetNotFound:
api.BadRequest(c, err.Error())
default:
api.InternalError(c, "Failed to update allocation rule")
}
return
}
api.Success(c, rule)
}
// DeleteAllocationRule handles DELETE /api/v1/allocation-rules/:id
func (h *AllocationRuleHandler) DeleteAllocationRule(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid allocation rule ID")
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
err = h.service.DeleteAllocationRule(userID.(uint), uint(id))
if err != nil {
switch err {
case service.ErrAllocationRuleNotFound:
api.NotFound(c, "Allocation rule not found")
case service.ErrAllocationRuleInUse:
api.Conflict(c, err.Error())
default:
api.InternalError(c, "Failed to delete allocation rule")
}
return
}
api.NoContent(c)
}
// ApplyAllocationRule handles POST /api/v1/allocation-rules/:id/apply
func (h *AllocationRuleHandler) ApplyAllocationRule(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid allocation rule ID")
return
}
var input service.ApplyAllocationInput
if err := c.ShouldBindJSON(&input); err != nil {
api.ValidationError(c, err.Error())
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
result, err := h.service.ApplyAllocationRule(userID.(uint), uint(id), input)
if err != nil {
switch err {
case service.ErrAllocationRuleNotFound:
api.NotFound(c, "Allocation rule not found")
case service.ErrInsufficientAmount:
api.BadRequest(c, err.Error())
case service.ErrAccountNotFound:
api.BadRequest(c, err.Error())
case service.ErrInsufficientBalance:
api.BadRequest(c, err.Error())
case service.ErrTargetNotFound:
api.BadRequest(c, err.Error())
case service.ErrInvalidTargetType:
api.BadRequest(c, err.Error())
case service.ErrInvalidAllocationTarget:
api.BadRequest(c, err.Error())
default:
api.InternalError(c, "Failed to apply allocation rule")
}
return
}
api.Success(c, result)
}
// SuggestAllocationForIncome handles GET /api/v1/allocation-rules/suggest
func (h *AllocationRuleHandler) SuggestAllocationForIncome(c *gin.Context) {
// Get amount from query parameter
amountStr := c.Query("amount")
if amountStr == "" {
api.BadRequest(c, "Amount parameter is required")
return
}
amount, err := strconv.ParseFloat(amountStr, 64)
if err != nil || amount <= 0 {
api.BadRequest(c, "Invalid amount")
return
}
// Get account_id from query parameter
accountIDStr := c.Query("account_id")
if accountIDStr == "" {
api.BadRequest(c, "account_id parameter is required")
return
}
accountID, err := strconv.ParseUint(accountIDStr, 10, 32)
if err != nil {
api.BadRequest(c, "Invalid account_id")
return
}
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
rules, err := h.service.SuggestAllocationForIncome(userID.(uint), amount, uint(accountID))
if err != nil {
api.InternalError(c, "Failed to get allocation suggestions")
return
}
api.Success(c, rules)
}