From 4b2355deee498fba31d495cdbca78ff96de8b22e Mon Sep 17 00:00:00 2001 From: admin <1297598740@qq.com> Date: Mon, 2 Feb 2026 13:55:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=A2=84=E7=AE=97?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E5=85=B6handler=E3=80=81service=E5=92=8Crepository=E5=B1=82?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/budget_handler.go | 4 +- internal/repository/budget_repository.go | 15 +++++- internal/service/budget_service.go | 59 ++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/internal/handler/budget_handler.go b/internal/handler/budget_handler.go index 82669f1..fd136d4 100644 --- a/internal/handler/budget_handler.go +++ b/internal/handler/budget_handler.go @@ -98,6 +98,7 @@ func (h *BudgetHandler) GetBudget(c *gin.Context) { } // GetAllBudgets handles GET /api/v1/budgets +// Returns budgets with calculated spent and progress fields func (h *BudgetHandler) GetAllBudgets(c *gin.Context) { // Get user ID from context userID, exists := c.Get("user_id") @@ -106,7 +107,8 @@ func (h *BudgetHandler) GetAllBudgets(c *gin.Context) { return } - budgets, err := h.service.GetAllBudgets(userID.(uint)) + // Use GetAllBudgetsWithProgress to return budgets with spent and progress calculated + budgets, err := h.service.GetAllBudgetsWithProgress(userID.(uint)) if err != nil { api.InternalError(c, "Failed to get budgets") return diff --git a/internal/repository/budget_repository.go b/internal/repository/budget_repository.go index 51e6477..c273535 100644 --- a/internal/repository/budget_repository.go +++ b/internal/repository/budget_repository.go @@ -66,8 +66,19 @@ func (r *BudgetRepository) Update(budget *models.Budget) error { return fmt.Errorf("failed to check budget existence: %w", err) } - // Update the budget - if err := r.db.Save(budget).Error; err != nil { + // Update the budget using Select to explicitly specify fields to update + // This ensures that nil/NULL values for CategoryID and AccountID are properly saved + if err := r.db.Model(budget).Select( + "name", + "amount", + "period_type", + "category_id", // Explicitly include to allow setting to NULL + "account_id", // Explicitly include to allow setting to NULL + "is_rolling", + "start_date", + "end_date", + "updated_at", + ).Updates(budget).Error; err != nil { return fmt.Errorf("failed to update budget: %w", err) } return nil diff --git a/internal/service/budget_service.go b/internal/service/budget_service.go index 350b9fb..14bfa16 100644 --- a/internal/service/budget_service.go +++ b/internal/service/budget_service.go @@ -126,6 +126,65 @@ func (s *BudgetService) GetAllBudgets(userID uint) ([]models.Budget, error) { return budgets, nil } +// BudgetWithProgress represents a budget with calculated progress fields +type BudgetWithProgress struct { + models.Budget + Spent float64 `json:"spent"` + Progress float64 `json:"progress"` +} + +// GetAllBudgetsWithProgress retrieves all budgets with calculated spent and progress for a user +func (s *BudgetService) GetAllBudgetsWithProgress(userID uint) ([]BudgetWithProgress, error) { + budgets, err := s.repo.GetAll(userID) + if err != nil { + return nil, fmt.Errorf("failed to get budgets: %w", err) + } + + result := make([]BudgetWithProgress, len(budgets)) + now := time.Now() + + for i, budget := range budgets { + // Calculate current period + startDate, endDate := s.calculateCurrentPeriod(&budget, now) + + // Get spent amount for current period + spent, err := s.repo.GetSpentAmount(&budget, startDate, endDate) + if err != nil { + // Log error but continue with 0 spent + spent = 0 + } + + // Calculate effective amount and progress + effectiveAmount := budget.Amount + + if budget.IsRolling { + // For rolling budgets, calculate effective amount + periodsElapsed := s.calculatePeriodsElapsed(&budget, startDate) + totalBudget := budget.Amount * float64(periodsElapsed+1) + + historyEnd := startDate.Add(-time.Second) + historySpent := 0.0 + if historyEnd.After(budget.StartDate) { + historySpent, _ = s.repo.GetSpentAmount(&budget, budget.StartDate, historyEnd) + } + effectiveAmount = totalBudget - historySpent + } + + progress := 0.0 + if effectiveAmount > 0 { + progress = (spent / effectiveAmount) * 100 + } + + result[i] = BudgetWithProgress{ + Budget: budget, + Spent: spent, + Progress: progress, + } + } + + return result, nil +} + // UpdateBudget updates an existing budget after verifying ownership func (s *BudgetService) UpdateBudget(userID, id uint, input BudgetInput) (*models.Budget, error) { // Get existing budget