feat: 添加预算管理功能,包括其handler、service和repository层。
This commit is contained in:
@@ -98,6 +98,7 @@ func (h *BudgetHandler) GetBudget(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllBudgets handles GET /api/v1/budgets
|
// GetAllBudgets handles GET /api/v1/budgets
|
||||||
|
// Returns budgets with calculated spent and progress fields
|
||||||
func (h *BudgetHandler) GetAllBudgets(c *gin.Context) {
|
func (h *BudgetHandler) GetAllBudgets(c *gin.Context) {
|
||||||
// Get user ID from context
|
// Get user ID from context
|
||||||
userID, exists := c.Get("user_id")
|
userID, exists := c.Get("user_id")
|
||||||
@@ -106,7 +107,8 @@ func (h *BudgetHandler) GetAllBudgets(c *gin.Context) {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
api.InternalError(c, "Failed to get budgets")
|
api.InternalError(c, "Failed to get budgets")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -66,8 +66,19 @@ func (r *BudgetRepository) Update(budget *models.Budget) error {
|
|||||||
return fmt.Errorf("failed to check budget existence: %w", err)
|
return fmt.Errorf("failed to check budget existence: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the budget
|
// Update the budget using Select to explicitly specify fields to update
|
||||||
if err := r.db.Save(budget).Error; err != nil {
|
// 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 fmt.Errorf("failed to update budget: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -126,6 +126,65 @@ func (s *BudgetService) GetAllBudgets(userID uint) ([]models.Budget, error) {
|
|||||||
return budgets, nil
|
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
|
// UpdateBudget updates an existing budget after verifying ownership
|
||||||
func (s *BudgetService) UpdateBudget(userID, id uint, input BudgetInput) (*models.Budget, error) {
|
func (s *BudgetService) UpdateBudget(userID, id uint, input BudgetInput) (*models.Budget, error) {
|
||||||
// Get existing budget
|
// Get existing budget
|
||||||
|
|||||||
Reference in New Issue
Block a user