This commit is contained in:
SunCheng
2026-02-10 17:49:19 +08:00
parent 3e18283e52
commit d052ae5197
104 changed files with 10369 additions and 3000 deletions

View File

@@ -1,4 +1,5 @@
using Service.Transaction;
using Application.Dto.Statistics;
using Application;
namespace WebApi.Controllers;
@@ -8,308 +9,173 @@ namespace WebApi.Controllers;
[ApiController]
[Route("api/[controller]/[action]")]
public class TransactionStatisticsController(
ITransactionRecordRepository transactionRepository,
ITransactionStatisticsService transactionStatisticsService,
ILogger<TransactionStatisticsController> logger,
IConfigService configService
ITransactionStatisticsApplication statisticsApplication
) : ControllerBase
{
// ===== 新统一接口(推荐使用) =====
/// <summary>
/// 获取累积余额统计数据(用于余额卡片图表
/// 按日期范围获取每日统计(新统一接口
/// </summary>
/// <param name="startDate">开始日期(包含)</param>
/// <param name="endDate">结束日期(不包含)</param>
/// <param name="savingClassify">储蓄分类(可选,不传则使用系统配置)</param>
[HttpGet]
public async Task<BaseResponse<List<BalanceStatisticsDto>>> GetBalanceStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetDailyStatisticsByRangeAsync(
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate,
[FromQuery] string? savingClassify = null
)
{
try
{
// 获取存款分类
var savingClassify = await configService.GetConfigByKeyAsync<string>("SavingsCategories");
// 获取每日统计数据
var statistics = await transactionStatisticsService.GetDailyStatisticsAsync(year, month, savingClassify);
// 按日期排序并计算累积余额
var sortedStats = statistics.OrderBy(s => s.Key).ToList();
var result = new List<BalanceStatisticsDto>();
decimal cumulativeBalance = 0;
foreach (var item in sortedStats)
{
var dailyBalance = item.Value.income - item.Value.expense;
cumulativeBalance += dailyBalance;
result.Add(new BalanceStatisticsDto(
item.Key,
cumulativeBalance
));
}
return result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取累积余额统计失败,年份: {Year}, 月份: {Month}", year, month);
return $"获取累积余额统计失败: {ex.Message}".Fail<List<BalanceStatisticsDto>>();
}
var result = await statisticsApplication.GetDailyStatisticsByRangeAsync(startDate, endDate, savingClassify);
return result.Ok();
}
/// <summary>
/// 获取指定月份每天的消费统计
/// 按日期范围获取汇总统计(新统一接口)
/// </summary>
/// <param name="startDate">开始日期(包含)</param>
/// <param name="endDate">结束日期(不包含)</param>
[HttpGet]
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetDailyStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
)
{
try
{
// 获取存款分类
var savingClassify = await configService.GetConfigByKeyAsync<string>("SavingsCategories");
var statistics = await transactionStatisticsService.GetDailyStatisticsAsync(year, month, savingClassify);
var result = statistics.Select(s => new DailyStatisticsDto(
s.Key,
s.Value.count,
s.Value.expense,
s.Value.income,
s.Value.saving
)).ToList();
return result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取日历统计数据失败,年份: {Year}, 月份: {Month}", year, month);
return $"获取日历统计数据失败: {ex.Message}".Fail<List<DailyStatisticsDto>>();
}
}
/// <summary>
/// 获取周统计数据
/// </summary>
[HttpGet]
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetWeeklyStatisticsAsync(
public async Task<BaseResponse<Service.Transaction.MonthlyStatistics>> GetSummaryByRangeAsync(
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate
)
{
try
{
// 获取存款分类
var savingClassify = await configService.GetConfigByKeyAsync<string>("SavingsCategories");
var statistics = await transactionStatisticsService.GetDailyStatisticsByRangeAsync(startDate, endDate, savingClassify);
var result = statistics.Select(s => new DailyStatisticsDto(
s.Key,
s.Value.count,
s.Value.expense,
s.Value.income,
s.Value.saving
)).ToList();
return result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取周统计数据失败,开始日期: {StartDate}, 结束日期: {EndDate}", startDate, endDate);
return $"获取周统计数据失败: {ex.Message}".Fail<List<DailyStatisticsDto>>();
}
var result = await statisticsApplication.GetSummaryByRangeAsync(startDate, endDate);
return result.Ok();
}
/// <summary>
/// 获取指定日期范围的统计汇总数据
/// 按日期范围获取分类统计(新统一接口)
/// </summary>
/// <param name="startDate">开始日期(包含)</param>
/// <param name="endDate">结束日期(不包含)</param>
/// <param name="type">交易类型</param>
[HttpGet]
public async Task<BaseResponse<MonthlyStatistics>> GetRangeStatisticsAsync(
public async Task<BaseResponse<List<Service.Transaction.CategoryStatistics>>> GetCategoryStatisticsByRangeAsync(
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate
)
{
try
{
// 通过日期范围查询数据
var records = await transactionRepository.QueryAsync(
startDate: startDate,
endDate: endDate,
pageSize: int.MaxValue);
var statistics = new MonthlyStatistics
{
Year = startDate.Year,
Month = startDate.Month
};
foreach (var record in records)
{
var amount = Math.Abs(record.Amount);
if (record.Type == TransactionType.Expense)
{
statistics.TotalExpense += amount;
statistics.ExpenseCount++;
}
else if (record.Type == TransactionType.Income)
{
statistics.TotalIncome += amount;
statistics.IncomeCount++;
}
}
statistics.Balance = statistics.TotalIncome - statistics.TotalExpense;
statistics.TotalCount = records.Count;
return statistics.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取时间范围统计数据失败,开始日期: {StartDate}, 结束日期: {EndDate}", startDate, endDate);
return $"获取时间范围统计数据失败: {ex.Message}".Fail<MonthlyStatistics>();
}
}
/// <summary>
/// 获取月度统计数据
/// </summary>
[HttpGet]
public async Task<BaseResponse<MonthlyStatistics>> GetMonthlyStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
)
{
try
{
var statistics = await transactionStatisticsService.GetMonthlyStatisticsAsync(year, month);
return statistics.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取月度统计数据失败,年份: {Year}, 月份: {Month}", year, month);
return $"获取月度统计数据失败: {ex.Message}".Fail<MonthlyStatistics>();
}
}
/// <summary>
/// 获取分类统计数据
/// </summary>
[HttpGet]
public async Task<BaseResponse<List<CategoryStatistics>>> GetCategoryStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month,
[FromQuery] DateTime endDate,
[FromQuery] TransactionType type
)
{
try
{
var statistics = await transactionStatisticsService.GetCategoryStatisticsAsync(year, month, type);
return statistics.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取分类统计数据失败,年份: {Year}, 月份: {Month}, 类型: {Type}", year, month, type);
return $"获取分类统计数据失败: {ex.Message}".Fail<List<CategoryStatistics>>();
}
}
/// <summary>
/// 按日期范围获取分类统计数据
/// </summary>
[HttpGet]
public async Task<BaseResponse<List<CategoryStatistics>>> GetCategoryStatisticsByDateRangeAsync(
[FromQuery] string startDate,
[FromQuery] string endDate,
[FromQuery] TransactionType type
)
{
try
{
if (!DateTime.TryParse(startDate, out var start))
{
return "开始日期格式错误".Fail<List<CategoryStatistics>>();
}
if (!DateTime.TryParse(endDate, out var end))
{
return "结束日期格式错误".Fail<List<CategoryStatistics>>();
}
var statistics = await transactionStatisticsService.GetCategoryStatisticsByDateRangeAsync(start, end, type);
return statistics.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取分类统计数据失败,开始日期: {StartDate}, 结束日期: {EndDate}, 类型: {Type}", startDate, endDate, type);
return $"获取分类统计数据失败: {ex.Message}".Fail<List<CategoryStatistics>>();
}
var result = await statisticsApplication.GetCategoryStatisticsByRangeAsync(startDate, endDate, type);
return result.Ok();
}
/// <summary>
/// 获取趋势统计数据
/// </summary>
[HttpGet]
public async Task<BaseResponse<List<TrendStatistics>>> GetTrendStatisticsAsync(
public async Task<BaseResponse<List<Service.Transaction.TrendStatistics>>> GetTrendStatisticsAsync(
[FromQuery] int startYear,
[FromQuery] int startMonth,
[FromQuery] int monthCount = 6
)
{
try
{
var statistics = await transactionStatisticsService.GetTrendStatisticsAsync(startYear, startMonth, monthCount);
return statistics.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "获取趋势统计数据失败,开始年份: {Year}, 开始月份: {Month}, 月份数: {Count}", startYear, startMonth,
monthCount);
return $"获取趋势统计数据失败: {ex.Message}".Fail<List<TrendStatistics>>();
}
var result = await statisticsApplication.GetTrendStatisticsAsync(startYear, startMonth, monthCount);
return result.Ok();
}
// ===== 旧接口(保留用于向后兼容,已标记为过时) =====
/// <summary>
/// 获取累积余额统计数据(用于余额卡片图表)
/// </summary>
[Obsolete("请使用 GetDailyStatisticsByRangeAsync 并在前端计算累积余额")]
[HttpGet]
public async Task<BaseResponse<List<BalanceStatisticsDto>>> GetBalanceStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
)
{
var result = await statisticsApplication.GetBalanceStatisticsAsync(year, month);
return result.Ok();
}
/// <summary>
/// 获取按交易摘要分组的统计信息(支持分页)
/// 获取指定月份每天的消费统计
/// </summary>
[Obsolete("请使用 GetDailyStatisticsByRangeAsync")]
[HttpGet]
public async Task<PagedResponse<ReasonGroupDto>> GetReasonGroupsAsync(
[FromQuery] int pageIndex = 1,
[FromQuery] int pageSize = 20)
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetDailyStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
)
{
try
{
var (list, total) = await transactionStatisticsService.GetReasonGroupsAsync(pageIndex, pageSize);
return new PagedResponse<ReasonGroupDto>
{
Success = true,
Data = list.ToArray(),
Total = total
};
}
catch (Exception ex)
{
logger.LogError(ex, "获取交易摘要分组失败");
return PagedResponse<ReasonGroupDto>.Fail($"获取交易摘要分组失败: {ex.Message}");
}
var result = await statisticsApplication.GetDailyStatisticsAsync(year, month);
return result.Ok();
}
/// <summary>
/// 获取周统计数据
/// </summary>
[Obsolete("请使用 GetDailyStatisticsByRangeAsync")]
[HttpGet]
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetWeeklyStatisticsAsync(
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate
)
{
var result = await statisticsApplication.GetWeeklyStatisticsAsync(startDate, endDate);
return result.Ok();
}
/// <summary>
/// 获取指定日期范围的统计汇总数据
/// </summary>
[Obsolete("请使用 GetSummaryByRangeAsync")]
[HttpGet]
public async Task<BaseResponse<Service.Transaction.MonthlyStatistics>> GetRangeStatisticsAsync(
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate
)
{
var result = await statisticsApplication.GetRangeStatisticsAsync(startDate, endDate);
return result.Ok();
}
/// <summary>
/// 获取月度统计数据
/// </summary>
[Obsolete("请使用 GetSummaryByRangeAsync")]
[HttpGet]
public async Task<BaseResponse<Service.Transaction.MonthlyStatistics>> GetMonthlyStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month
)
{
var result = await statisticsApplication.GetMonthlyStatisticsAsync(year, month);
return result.Ok();
}
/// <summary>
/// 获取分类统计数据
/// </summary>
[Obsolete("请使用 GetCategoryStatisticsByRangeAsync")]
[HttpGet]
public async Task<BaseResponse<List<Service.Transaction.CategoryStatistics>>> GetCategoryStatisticsAsync(
[FromQuery] int year,
[FromQuery] int month,
[FromQuery] TransactionType type
)
{
var result = await statisticsApplication.GetCategoryStatisticsAsync(year, month, type);
return result.Ok();
}
/// <summary>
/// 按日期范围获取分类统计数据
/// </summary>
[Obsolete("请使用 GetCategoryStatisticsByRangeAsyncDateTime 参数版本)")]
[HttpGet]
public async Task<BaseResponse<List<Service.Transaction.CategoryStatistics>>> GetCategoryStatisticsByDateRangeAsync(
[FromQuery] string startDate,
[FromQuery] string endDate,
[FromQuery] TransactionType type
)
{
var result = await statisticsApplication.GetCategoryStatisticsByDateRangeAsync(startDate, endDate, type);
return result.Ok();
}
}
/// <summary>
/// 日历统计响应DTO
/// </summary>
public record DailyStatisticsDto(
string Date,
int Count,
decimal Expense,
decimal Income,
decimal Balance
);
/// <summary>
/// 累积余额统计DTO
/// </summary>
public record BalanceStatisticsDto(
string Date,
decimal CumulativeBalance
);