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 = null) { try { return (await budgetService.GetListAsync(referenceDate)) .OrderBy(b => b.IsStopped) .OrderBy(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> GetStatisticsAsync([FromQuery] long id, [FromQuery] DateTime referenceDate) { try { // 参数验证 if (id == 0) { return "预算 Id 无效".Fail(); } var result = await budgetService.GetStatisticsAsync(id, referenceDate); if (result == null) { return "预算不存在".Fail(); } return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取预算统计失败, Id: {Id}", id); return $"获取预算统计失败: {ex.Message}".Fail(); } } /// /// 获取分类统计信息(月度和年度) /// [HttpGet] public async Task> GetCategoryStatsAsync([FromQuery] BudgetCategory category, [FromQuery] DateTime? referenceDate = null) { try { var result = await budgetService.GetCategoryStatsAsync(category, referenceDate); return result.Ok(); } catch (Exception ex) { logger.LogError(ex, "获取分类统计失败, Category: {Category}", category); 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 { var budget = new BudgetRecord { Name = dto.Name, Type = dto.Type, Limit = dto.Limit, Category = dto.Category, SelectedCategories = dto.SelectedCategories != null ? string.Join(",", dto.SelectedCategories) : string.Empty, StartDate = dto.StartDate ?? DateTime.Now }; 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(); budget.Name = dto.Name; budget.Type = dto.Type; budget.Limit = dto.Limit; budget.Category = dto.Category; budget.SelectedCategories = dto.SelectedCategories != null ? string.Join(",", dto.SelectedCategories) : string.Empty; budget.IsStopped = dto.IsStopped; 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(); } } /// /// 切换预算暂停状态 /// [HttpPost] public async Task ToggleStopAsync([FromQuery] long id) { try { var budget = await budgetRepository.GetByIdAsync(id); if (budget == null) { return "预算不存在".Fail(); } budget.IsStopped = !budget.IsStopped; var success = await budgetRepository.UpdateAsync(budget); return success ? BaseResponse.Done() : "操作失败".Fail(); } catch (Exception ex) { logger.LogError(ex, "切换预算状态失败, Id: {Id}", id); return $"操作失败: {ex.Message}".Fail(); } } /// /// 归档预算 /// [HttpPost("{year}/{month}")] public async Task ArchiveBudgetsAsync(int year, int month) { try { var msg = await budgetService.ArchiveBudgetsAsync(year, month); if (!string.IsNullOrEmpty(msg)) { return msg.Fail(); } return BaseResponse.Done(); } catch (Exception ex) { logger.LogError(ex, "归档预算失败, 归档日期: {Year}-{Month}", year, month); return $"归档预算失败: {ex.Message}".Fail(); } } private async Task ValidateBudgetSelectedCategoriesAsync(BudgetRecord record) { 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; } }