This commit is contained in:
2026-01-25 21:59:00 +08:00
parent 7fd537bef3
commit 4cad3f0250
118 changed files with 30473 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
package handler
import (
"errors"
"strconv"
"accounting-app/pkg/api"
"accounting-app/internal/service"
"github.com/gin-gonic/gin"
)
// SavingsPotHandler handles HTTP requests for savings pot operations
// Feature: financial-core-upgrade
// Validates: Requirements 10.1-10.8
type SavingsPotHandler struct {
savingsPotService *service.SavingsPotService
}
// NewSavingsPotHandler creates a new SavingsPotHandler instance
func NewSavingsPotHandler(savingsPotService *service.SavingsPotService) *SavingsPotHandler {
return &SavingsPotHandler{
savingsPotService: savingsPotService,
}
}
// DepositRequest represents the request body for deposit operation
type DepositRequest struct {
Amount float64 `json:"amount" binding:"required,gt=0"`
}
// WithdrawRequest represents the request body for withdraw operation
type WithdrawRequest struct {
Amount float64 `json:"amount" binding:"required,gt=0"`
}
// Deposit handles POST /api/savings-pot/:id/deposit
// Deposits money into a savings pot from the parent account
// Validates: Requirements 10.1, 10.3, 10.5
func (h *SavingsPotHandler) Deposit(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid savings pot ID")
return
}
var req DepositRequest
if err := c.ShouldBindJSON(&req); err != nil {
api.ValidationError(c, "Invalid request body: "+err.Error())
return
}
result, err := h.savingsPotService.Deposit(userID.(uint), uint(savingsPotID), req.Amount)
if err != nil {
if errors.Is(err, service.ErrSavingsPotNotFound) {
api.NotFound(c, "Savings pot not found")
return
}
if errors.Is(err, service.ErrNotASavingsPot) {
api.BadRequest(c, "Account is not a savings pot")
return
}
if errors.Is(err, service.ErrInsufficientAvailableBalance) {
api.BadRequest(c, "Insufficient available balance in parent account")
return
}
if errors.Is(err, service.ErrInvalidDepositAmount) {
api.BadRequest(c, "Deposit amount must be positive")
return
}
api.InternalError(c, "Failed to deposit: "+err.Error())
return
}
api.Success(c, result)
}
// Withdraw handles POST /api/savings-pot/:id/withdraw
// Withdraws money from a savings pot back to the parent account
// Validates: Requirements 10.2, 10.4, 10.6
func (h *SavingsPotHandler) Withdraw(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid savings pot ID")
return
}
var req WithdrawRequest
if err := c.ShouldBindJSON(&req); err != nil {
api.ValidationError(c, "Invalid request body: "+err.Error())
return
}
result, err := h.savingsPotService.Withdraw(userID.(uint), uint(savingsPotID), req.Amount)
if err != nil {
if errors.Is(err, service.ErrSavingsPotNotFound) {
api.NotFound(c, "Savings pot not found")
return
}
if errors.Is(err, service.ErrNotASavingsPot) {
api.BadRequest(c, "Account is not a savings pot")
return
}
if errors.Is(err, service.ErrInsufficientSavingsPotBalance) {
api.BadRequest(c, "Insufficient balance in savings pot")
return
}
if errors.Is(err, service.ErrInvalidWithdrawAmount) {
api.BadRequest(c, "Withdraw amount must be positive")
return
}
api.InternalError(c, "Failed to withdraw: "+err.Error())
return
}
api.Success(c, result)
}
// GetSavingsPot handles GET /api/savings-pot/:id
// Returns savings pot details including progress
// Validates: Requirements 10.7, 10.8
func (h *SavingsPotHandler) GetSavingsPot(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid savings pot ID")
return
}
result, err := h.savingsPotService.GetSavingsPot(userID.(uint), uint(savingsPotID))
if err != nil {
if errors.Is(err, service.ErrSavingsPotNotFound) {
api.NotFound(c, "Savings pot not found")
return
}
if errors.Is(err, service.ErrNotASavingsPot) {
api.BadRequest(c, "Account is not a savings pot")
return
}
api.InternalError(c, "Failed to get savings pot: "+err.Error())
return
}
api.Success(c, result)
}
// RegisterRoutes registers all savings pot routes to the given router group
func (h *SavingsPotHandler) RegisterRoutes(rg *gin.RouterGroup) {
savingsPot := rg.Group("/savings-pot")
{
savingsPot.POST("/:id/deposit", h.Deposit)
savingsPot.POST("/:id/withdraw", h.Withdraw)
savingsPot.GET("/:id", h.GetSavingsPot)
}
}