using Service.Budget; namespace WebApi.Controllers; [ApiController] [Route("api/[controller]/[action]")] public class BudgetController( IBudgetService budgetService, IBudgetRepository budgetRepository, ILogger logger) : ControllerBase { /// /// 获取预算列表 /// [HttpGet] public async Task>> GetListAsync([FromQuery] DateTime referenceDate) { try { return (await budgetService.GetListAsync(referenceDate)) .OrderByDescending(b => b.IsMandatoryExpense) .ThenBy(b => b.Category) .ThenBy(b => b.Type) .ThenByDescending(b => b.Limit > 0 ? b.Current / b.Limit : 0) .ThenBy(b => b.Name) .ToList() .Ok(); } catch (Exception ex) { logger.LogError(ex, "获取预算列表失败"); return $"获取预算列表失败: {ex.Message}".Fail>(); } } /// /// 获取分类统计信息(月度和年度) /// [HttpGet] public async Task> GetCategoryStatsAsync([FromQuery] BudgetCategory category, [FromQuery] DateTime referenceDate) { try { var result = await budgetService.GetCategoryStatsAsync(category, referenceDate); return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取分类统计失败, Category: {Category}", category); return $"获取分类统计失败: {ex.Message}".Fail(); } } /// /// 获取未被预算覆盖的分类统计信息 /// [HttpGet] public async Task>> GetUncoveredCategoriesAsync([FromQuery] BudgetCategory category, [FromQuery] DateTime? referenceDate = null) { try { var result = await budgetService.GetUncoveredCategoriesAsync(category, referenceDate); return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取未覆盖分类统计失败, Category: {Category}", category); return $"获取未覆盖分类统计失败: {ex.Message}".Fail>(); } } /// /// 获取归档总结 /// [HttpGet] public async Task> GetArchiveSummaryAsync([FromQuery] DateTime referenceDate) { try { var result = await budgetService.GetArchiveSummaryAsync(referenceDate.Year, referenceDate.Month); return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取归档总结失败"); return $"获取归档总结失败: {ex.Message}".Fail(); } } /// /// 获取指定周期的存款预算信息 /// [HttpGet] public async Task> GetSavingsBudgetAsync(int year, int month, BudgetPeriodType type) { try { var result = await budgetService.GetSavingsBudgetAsync(year, month, type); return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取存款预算信息失败"); return $"获取存款预算信息失败: {ex.Message}".Fail(); } } /// /// 删除预算 /// [HttpDelete("{id}")] public async Task DeleteByIdAsync(long id) { try { var success = await budgetRepository.DeleteAsync(id); return success ? BaseResponse.Done() : "删除预算失败".Fail(); } catch (Exception ex) { logger.LogError(ex, "删除预算失败, Id: {Id}", id); return $"删除预算失败: {ex.Message}".Fail(); } } /// /// 创建预算 /// [HttpPost] public async Task> CreateAsync([FromBody] CreateBudgetDto dto) { try { // 不记额预算的金额强制设为0 var limit = dto.NoLimit ? 0 : dto.Limit; var budget = new BudgetRecord { Name = dto.Name, Type = dto.Type, Limit = limit, Category = dto.Category, SelectedCategories = string.Join(",", dto.SelectedCategories), StartDate = dto.StartDate ?? DateTime.Now, NoLimit = dto.NoLimit, IsMandatoryExpense = dto.IsMandatoryExpense }; var varidationError = await ValidateBudgetSelectedCategoriesAsync(budget); if (!string.IsNullOrEmpty(varidationError)) { return varidationError.Fail(); } var success = await budgetRepository.AddAsync(budget); if (success) { return budget.Id.Ok(); } return "创建预算失败".Fail(); } catch (Exception ex) { logger.LogError(ex, "创建预算失败"); return $"创建预算失败: {ex.Message}".Fail(); } } /// /// 更新预算 /// [HttpPost] public async Task UpdateAsync([FromBody] UpdateBudgetDto dto) { try { var budget = await budgetRepository.GetByIdAsync(dto.Id); if (budget == null) return "预算不存在".Fail(); // 不记额预算的金额强制设为0 var limit = dto.NoLimit ? 0 : dto.Limit; budget.Name = dto.Name; budget.Type = dto.Type; budget.Limit = limit; budget.Category = dto.Category; budget.SelectedCategories = string.Join(",", dto.SelectedCategories); budget.NoLimit = dto.NoLimit; budget.IsMandatoryExpense = dto.IsMandatoryExpense; if (dto.StartDate.HasValue) { budget.StartDate = dto.StartDate.Value; } var varidationError = await ValidateBudgetSelectedCategoriesAsync(budget); if (!string.IsNullOrEmpty(varidationError)) { return varidationError.Fail(); } var success = await budgetRepository.UpdateAsync(budget); return success ? BaseResponse.Done() : "更新预算失败".Fail(); } catch (Exception ex) { logger.LogError(ex, "更新预算失败, Id: {Id}", dto.Id); return $"更新预算失败: {ex.Message}".Fail(); } } private async Task ValidateBudgetSelectedCategoriesAsync(BudgetRecord record) { // 验证不记额预算必须是年度预算 if (record.NoLimit && record.Type != BudgetPeriodType.Year) { return "不记额预算只能设置为年度预算。"; } var allBudgets = await budgetRepository.GetAllAsync(); var recordSelectedCategories = record.SelectedCategories.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var budget in allBudgets) { var selectedCategories = budget.SelectedCategories.Split(',', StringSplitOptions.RemoveEmptyEntries); if (budget.Id != record.Id) { if (budget.Category == record.Category && recordSelectedCategories.Intersect(selectedCategories).Any()) { return $"和 {budget.Name} 存在分类冲突,请调整相关分类。"; } } } return string.Empty; } }