315 lines
10 KiB
C#
315 lines
10 KiB
C#
|
|
using Service.Transaction;
|
|||
|
|
|
|||
|
|
namespace WebApi.Controllers;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 交易统计控制器
|
|||
|
|
/// </summary>
|
|||
|
|
[ApiController]
|
|||
|
|
[Route("api/[controller]/[action]")]
|
|||
|
|
public class TransactionStatisticsController(
|
|||
|
|
ITransactionRecordRepository transactionRepository,
|
|||
|
|
ITransactionStatisticsService transactionStatisticsService,
|
|||
|
|
ILogger<TransactionStatisticsController> logger,
|
|||
|
|
IConfigService configService
|
|||
|
|
) : ControllerBase
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取累积余额统计数据(用于余额卡片图表)
|
|||
|
|
/// </summary>
|
|||
|
|
[HttpGet]
|
|||
|
|
public async Task<BaseResponse<List<BalanceStatisticsDto>>> GetBalanceStatisticsAsync(
|
|||
|
|
[FromQuery] int year,
|
|||
|
|
[FromQuery] int month
|
|||
|
|
)
|
|||
|
|
{
|
|||
|
|
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>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取指定月份每天的消费统计
|
|||
|
|
/// </summary>
|
|||
|
|
[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(
|
|||
|
|
[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>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取指定日期范围的统计汇总数据
|
|||
|
|
/// </summary>
|
|||
|
|
[HttpGet]
|
|||
|
|
public async Task<BaseResponse<MonthlyStatistics>> GetRangeStatisticsAsync(
|
|||
|
|
[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] 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>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取趋势统计数据
|
|||
|
|
/// </summary>
|
|||
|
|
[HttpGet]
|
|||
|
|
public async Task<BaseResponse<List<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>>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取按交易摘要分组的统计信息(支持分页)
|
|||
|
|
/// </summary>
|
|||
|
|
[HttpGet]
|
|||
|
|
public async Task<PagedResponse<ReasonGroupDto>> GetReasonGroupsAsync(
|
|||
|
|
[FromQuery] int pageIndex = 1,
|
|||
|
|
[FromQuery] int pageSize = 20)
|
|||
|
|
{
|
|||
|
|
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}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <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
|
|||
|
|
);
|