Files
EmailBill/.doc/api-refactoring-transaction-statistics.md
SunCheng d052ae5197 fix
2026-02-10 17:49:19 +08:00

11 KiB
Raw Permalink Blame History

title, author, date, status, category
title author date status category
TransactionStatistics 接口重构文档 AI Assistant 2026-02-10 final API重构

TransactionStatistics 接口重构文档

概述

本次重构针对 TransactionStatisticsController 中的接口进行了统一优化,采用方案一(激进重构),将原有 9 个接口精简为 4 个核心接口,同时保留旧接口用于向后兼容(标记为 [Obsolete])。

重构时间: 2026-02-10
影响范围: Backend (Service/Application/Controller) + Frontend (API/Views)


一、重构目标

问题分析

  1. 接口冗余: 存在多组功能重复的接口对

    • GetMonthlyStatistics vs GetRangeStatistics
    • GetDailyStatistics vs GetWeeklyStatistics
    • GetCategoryStatistics vs GetCategoryStatisticsByDateRange
  2. 参数不统一: 部分接口使用 (year, month),部分使用 (startDate, endDate)

  3. 未使用接口: GetReasonGroups 在前端完全未被调用

重构原则

  • 统一使用日期范围查询 (startDate, endDate)
  • 保留旧接口用于向后兼容,标记为 [Obsolete]
  • 删除未使用的接口
  • 前端优先迁移到新接口

二、接口变更对照表

新接口(推荐使用)

新接口名称 路径 参数 说明 替代的旧接口
GetDailyStatisticsByRange /TransactionStatistics/GetDailyStatisticsByRange startDate, endDate, savingClassify? 按日期范围获取每日统计 GetDailyStatistics
GetWeeklyStatistics
GetSummaryByRange /TransactionStatistics/GetSummaryByRange startDate, endDate 按日期范围获取汇总统计 GetMonthlyStatistics
GetRangeStatistics
GetCategoryStatisticsByRange /TransactionStatistics/GetCategoryStatisticsByRange startDate, endDate, type 按日期范围获取分类统计 GetCategoryStatistics
GetCategoryStatisticsByDateRange
GetTrendStatistics /TransactionStatistics/GetTrendStatistics startYear, startMonth, monthCount 多月趋势统计 (保持不变)

标记为 Obsolete 的接口(兼容性保留)

接口名称 状态 建议迁移方案
GetBalanceStatistics [Obsolete] 使用 GetDailyStatisticsByRange 并在前端计算累积余额
GetDailyStatistics [Obsolete] 使用 GetDailyStatisticsByRange
GetWeeklyStatistics [Obsolete] 使用 GetDailyStatisticsByRange
GetMonthlyStatistics [Obsolete] 使用 GetSummaryByRange
GetRangeStatistics [Obsolete] 使用 GetSummaryByRange
GetCategoryStatistics [Obsolete] 使用 GetCategoryStatisticsByRange
GetCategoryStatisticsByDateRange [Obsolete] 使用 GetCategoryStatisticsByRange (DateTime 参数版本)

删除的接口

接口名称 删除原因
GetReasonGroups 前端完全未使用

三、技术实现细节

1. Service 层变更

文件: Service/Transaction/TransactionStatisticsService.cs

新增方法:

/// <summary>
/// 按日期范围获取汇总统计数据(新统一接口)
/// </summary>
Task<MonthlyStatistics> GetSummaryByRangeAsync(DateTime startDate, DateTime endDate);

实现: 直接查询指定日期范围的交易记录并汇总统计。


2. Application 层变更

文件: Application/TransactionStatisticsApplication.cs

新增方法:

// 新统一接口
Task<List<DailyStatisticsDto>> GetDailyStatisticsByRangeAsync(DateTime startDate, DateTime endDate, string? savingClassify = null);
Task<MonthlyStatistics> GetSummaryByRangeAsync(DateTime startDate, DateTime endDate);
Task<List<CategoryStatistics>> GetCategoryStatisticsByRangeAsync(DateTime startDate, DateTime endDate, TransactionType type);

标记为过时的方法: 所有旧方法均添加 [Obsolete] 特性,指引开发者迁移到新接口。


3. Controller 层变更

文件: WebApi/Controllers/TransactionStatisticsController.cs

新增接口:

[HttpGet]
public async Task<BaseResponse<List<DailyStatisticsDto>>> GetDailyStatisticsByRangeAsync(
    [FromQuery] DateTime startDate,
    [FromQuery] DateTime endDate,
    [FromQuery] string? savingClassify = null)

[HttpGet]
public async Task<BaseResponse<Service.Transaction.MonthlyStatistics>> GetSummaryByRangeAsync(
    [FromQuery] DateTime startDate,
    [FromQuery] DateTime endDate)

[HttpGet]
public async Task<BaseResponse<List<Service.Transaction.CategoryStatistics>>> GetCategoryStatisticsByRangeAsync(
    [FromQuery] DateTime startDate,
    [FromQuery] DateTime endDate,
    [FromQuery] TransactionType type)

标记为过时的接口: 所有旧接口均添加 [Obsolete] 特性。

删除的接口: GetReasonGroupsAsync


4. 前端变更

API 定义文件

文件: Web/src/api/statistics.js

新增方法:

// 新统一接口
export const getDailyStatisticsByRange = (params) => { /* ... */ }
export const getSummaryByRange = (params) => { /* ... */ }
export const getCategoryStatisticsByRange = (params) => { /* ... */ }

标记为过时的方法: 添加 @deprecated JSDoc 注释。

视图文件

文件: Web/src/views/statisticsV2/Index.vue

迁移内容:

  1. 周度数据加载: getRangeStatisticsgetSummaryByRange
  2. 周度每日统计: getWeeklyStatisticsgetDailyStatisticsByRange
  3. 周度分类统计: getCategoryStatisticsByDateRangegetCategoryStatisticsByRange

关键修改:

  • 修改 endDate 计算逻辑: +6天+7天(因为新接口的 endDate 是不包含的)
  • 移除 weekEnd.setHours(23, 59, 59, 999) 逻辑(不再需要)

四、迁移指南

后端开发者

场景 1: 获取月度统计

旧代码:

await statisticsService.GetMonthlyStatisticsAsync(2025, 2);

新代码:

var startDate = new DateTime(2025, 2, 1);
var endDate = new DateTime(2025, 3, 1); // 不包含3月1日
await statisticsService.GetSummaryByRangeAsync(startDate, endDate);

场景 2: 获取每日统计

旧代码:

await statisticsService.GetDailyStatisticsAsync(2025, 2, savingClassify);

新代码:

var startDate = new DateTime(2025, 2, 1);
var endDate = new DateTime(2025, 3, 1);
await statisticsService.GetDailyStatisticsByRangeAsync(startDate, endDate, savingClassify);

前端开发者

场景 1: 获取月度汇总

旧代码:

await getMonthlyStatistics({ year: 2025, month: 2 })

新代码:

await getSummaryByRange({ 
  startDate: '2025-02-01', 
  endDate: '2025-03-01' 
})

场景 2: 获取周度每日统计

旧代码:

await getWeeklyStatistics({ 
  startDate: '2025-02-01', 
  endDate: '2025-02-07' 
})

新代码:

await getDailyStatisticsByRange({ 
  startDate: '2025-02-01', 
  endDate: '2025-02-08'  // 注意endDate 不包含
})

场景 3: 获取分类统计

旧代码:

await getCategoryStatistics({ year: 2025, month: 2, type: 0 })
// 或
await getCategoryStatisticsByDateRange({ 
  startDate: '2025-02-01', 
  endDate: '2025-02-28', 
  type: 0 
})

新代码:

await getCategoryStatisticsByRange({ 
  startDate: '2025-02-01', 
  endDate: '2025-03-01',  // 注意endDate 不包含
  type: 0 
})

五、重要注意事项

⚠️ endDate 语义变更

新接口的 endDate 参数是不包含的exclusive

  • 错误: startDate: '2025-02-01', endDate: '2025-02-28' → 缺少2月28日数据
  • 正确: startDate: '2025-02-01', endDate: '2025-03-01' → 包含整个2月

🔒 向后兼容策略

  • 所有旧接口保持可用,仅标记为 [Obsolete]
  • 前端 V1 版本继续使用旧接口
  • 前端 V2 版本已迁移到新接口
  • 建议在后续版本中逐步废弃旧接口

📝 测试覆盖

  • 后端单元测试: TransactionStatisticsServiceTest 全部通过9个测试
  • ⚠️ 前端测试: 建议手动测试以下场景
    • 月度视图切换
    • 年度视图切换
    • 周度视图切换
    • 分类统计数据展示

六、优化效果

接口精简

指标 重构前 重构后 优化
接口总数 9 4 ⬇️ 55%
推荐使用接口 - 4 -
兼容性接口 - 7 -
未使用接口 1 0 ⬇️ 100%

参数统一度

  • 重构前: 3种参数模式year+month, startDate+endDate, startYear+startMonth+monthCount
  • 重构后: 2种参数模式startDate+endDate, startYear+startMonth+monthCount

API 可维护性

  • 接口语义更清晰
  • 参数命名更统一
  • 减少代码重复
  • 降低学习成本

七、后续计划

  1. V1 版本迁移 (优先级: 中)

    • statisticsV1/Index.vue 迁移到新接口
    • 移除对 getBalanceStatistics 的依赖
  2. 旧接口废弃 (优先级: 低)

    • 确认所有前端页面迁移完成
    • 在下个大版本中彻底删除旧接口
  3. 性能优化 (优先级: 高)

    • 考虑提供聚合接口,一次返回多种类型的分类统计
    • 前端添加数据缓存机制

八、相关文件清单

后端文件

  • Service/Transaction/TransactionStatisticsService.cs
  • Application/TransactionStatisticsApplication.cs
  • WebApi/Controllers/TransactionStatisticsController.cs

前端文件

  • Web/src/api/statistics.js
  • Web/src/views/statisticsV2/Index.vue

测试文件

  • WebApi.Test/Transaction/TransactionStatisticsServiceTest.cs
  • WebApi.Test/Application/TransactionStatisticsApplicationTest.cs

九、常见问题 (FAQ)

Q1: 旧接口什么时候会被删除?

A: 目前旧接口仅标记为 [Obsolete],不会被立即删除。计划在确认所有前端页面迁移完成后,在下个大版本中删除。

Q2: 为什么 GetTrendStatistics 没有改为日期范围?

A: GetTrendStatistics 的业务场景是获取"连续N个月"的趋势数据,使用 (startYear, startMonth, monthCount) 更符合业务语义。如果改为日期范围,反而需要前端自行计算月份跨度。

Q3: 新接口的 endDate 为什么是不包含的?

A: 采用"左闭右开区间 [startDate, endDate)"是业界常见做法,优点包括:

  • 方便表示连续时间段如2月: [2025-02-01, 2025-03-01)
  • 避免时区和时间精度问题(不需要 23:59:59.999
  • 与大多数编程语言的区间语义一致(如 Python 的 range()

Q4: 如何确认迁移是否成功?

A: 检查以下几点:

  1. 前端代码中没有引用旧的 API 方法
  2. 统计数据展示正常,与旧版本数据一致
  3. 控制台无 API 调用错误

文档版本: v1.0
最后更新: 2026-02-10