--- title: TransactionStatistics 接口重构文档 author: AI Assistant date: 2026-02-10 status: final category: 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` **新增方法**: ```csharp /// /// 按日期范围获取汇总统计数据(新统一接口) /// Task GetSummaryByRangeAsync(DateTime startDate, DateTime endDate); ``` **实现**: 直接查询指定日期范围的交易记录并汇总统计。 --- ### 2. Application 层变更 **文件**: `Application/TransactionStatisticsApplication.cs` **新增方法**: ```csharp // 新统一接口 Task> GetDailyStatisticsByRangeAsync(DateTime startDate, DateTime endDate, string? savingClassify = null); Task GetSummaryByRangeAsync(DateTime startDate, DateTime endDate); Task> GetCategoryStatisticsByRangeAsync(DateTime startDate, DateTime endDate, TransactionType type); ``` **标记为过时的方法**: 所有旧方法均添加 `[Obsolete]` 特性,指引开发者迁移到新接口。 --- ### 3. Controller 层变更 **文件**: `WebApi/Controllers/TransactionStatisticsController.cs` **新增接口**: ```csharp [HttpGet] public async Task>> GetDailyStatisticsByRangeAsync( [FromQuery] DateTime startDate, [FromQuery] DateTime endDate, [FromQuery] string? savingClassify = null) [HttpGet] public async Task> GetSummaryByRangeAsync( [FromQuery] DateTime startDate, [FromQuery] DateTime endDate) [HttpGet] public async Task>> GetCategoryStatisticsByRangeAsync( [FromQuery] DateTime startDate, [FromQuery] DateTime endDate, [FromQuery] TransactionType type) ``` **标记为过时的接口**: 所有旧接口均添加 `[Obsolete]` 特性。 **删除的接口**: `GetReasonGroupsAsync` --- ### 4. 前端变更 #### API 定义文件 **文件**: `Web/src/api/statistics.js` **新增方法**: ```javascript // 新统一接口 export const getDailyStatisticsByRange = (params) => { /* ... */ } export const getSummaryByRange = (params) => { /* ... */ } export const getCategoryStatisticsByRange = (params) => { /* ... */ } ``` **标记为过时的方法**: 添加 `@deprecated` JSDoc 注释。 #### 视图文件 **文件**: `Web/src/views/statisticsV2/Index.vue` **迁移内容**: 1. 周度数据加载: `getRangeStatistics` → `getSummaryByRange` 2. 周度每日统计: `getWeeklyStatistics` → `getDailyStatisticsByRange` 3. 周度分类统计: `getCategoryStatisticsByDateRange` → `getCategoryStatisticsByRange` **关键修改**: - 修改 `endDate` 计算逻辑: `+6天` → `+7天`(因为新接口的 endDate 是不包含的) - 移除 `weekEnd.setHours(23, 59, 59, 999)` 逻辑(不再需要) --- ## 四、迁移指南 ### 后端开发者 #### 场景 1: 获取月度统计 **旧代码**: ```csharp await statisticsService.GetMonthlyStatisticsAsync(2025, 2); ``` **新代码**: ```csharp var startDate = new DateTime(2025, 2, 1); var endDate = new DateTime(2025, 3, 1); // 不包含3月1日 await statisticsService.GetSummaryByRangeAsync(startDate, endDate); ``` #### 场景 2: 获取每日统计 **旧代码**: ```csharp await statisticsService.GetDailyStatisticsAsync(2025, 2, savingClassify); ``` **新代码**: ```csharp var startDate = new DateTime(2025, 2, 1); var endDate = new DateTime(2025, 3, 1); await statisticsService.GetDailyStatisticsByRangeAsync(startDate, endDate, savingClassify); ``` --- ### 前端开发者 #### 场景 1: 获取月度汇总 **旧代码**: ```javascript await getMonthlyStatistics({ year: 2025, month: 2 }) ``` **新代码**: ```javascript await getSummaryByRange({ startDate: '2025-02-01', endDate: '2025-03-01' }) ``` #### 场景 2: 获取周度每日统计 **旧代码**: ```javascript await getWeeklyStatistics({ startDate: '2025-02-01', endDate: '2025-02-07' }) ``` **新代码**: ```javascript await getDailyStatisticsByRange({ startDate: '2025-02-01', endDate: '2025-02-08' // 注意:endDate 不包含 }) ``` #### 场景 3: 获取分类统计 **旧代码**: ```javascript await getCategoryStatistics({ year: 2025, month: 2, type: 0 }) // 或 await getCategoryStatisticsByDateRange({ startDate: '2025-02-01', endDate: '2025-02-28', type: 0 }) ``` **新代码**: ```javascript 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