Files
EmailBill/openspec/changes/archive/2026-02-21-remove-v1-calendar-stats-budget/design.md
SunCheng 9dce12c61b
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 18s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
归档
2026-02-21 21:58:55 +08:00

336 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
EmailBill 项目在早期开发了 V1 版本的日历、统计、预算三大核心功能模块。随着业务迭代V2 版本已经重构并稳定运行,提供了更优的用户体验和代码架构。当前代码库中同时维护 V1 和 V2 两套实现,导致:
1. **代码冗余**: 前端包含 `CalendarView.vue`, `BudgetView.vue`, `statisticsV1/Index.vue` 等页面,后端包含多个仅服务于 V1 的 API 端点
2. **维护成本**: 任何共享组件或数据模型的变更需要同时考虑 V1 和 V2 的兼容性
3. **认知负担**: 新开发者需要理解两套架构,增加了上手难度
4. **测试负担**: 需要维护两套测试用例,且部分测试已失效
### 当前状态
- V2 版本已完成功能对齐,用户已逐步迁移
- V1 相关 API 端点部分已标记 `[Obsolete]` (如 `TransactionRecordController.GetDailyStatisticsAsync`)
- 路由守卫中包含 V1/V2 版本切换逻辑
### 约束条件
- **不能影响 V2 功能**: 必须保证 V2 页面完全正常运行
- **共享组件不能删除**: `TransactionList`, `TransactionDetail`, `PopupContainer` 等组件被 V2 使用
- **Repository 层不能动**: V1 和 V2 共用相同的数据访问层
- **需要手动测试**: 删除后必须打开 V2 页面验证功能完整性
### 利益相关者
- **开发团队**: 减少维护负担,简化架构
- **测试团队**: 减少测试用例数量
- **最终用户**: 无感知(已迁移到 V2
---
## Goals / Non-Goals
### Goals
1. **完全移除 V1 代码**: 删除所有 V1 专用的页面、API、Service 方法
2. **保持 V2 功能完整**: 确保删除操作不影响 V2 版本的任何功能
3. **清理路由配置**: 移除 V1 路由和版本切换逻辑
4. **验证功能正常**: 通过手动测试验证 V2 页面能正常打开和使用
### Non-Goals
1. **不修改 Repository 层**: V2 继续使用现有的 Repository不进行任何修改
2. **不修改数据库**: 不涉及数据迁移或表结构变更
3. **不重构 V2 代码**: 仅删除 V1 代码,不对 V2 进行任何优化或重构
4. **不移除共享组件**: 即使组件原本为 V1 开发,只要 V2 在用就保留
5. **不更新文档**: API 文档和用户文档的更新不在本次变更范围内
---
## Decisions
### Decision 1: 采用自底向上的删除顺序 (Backend → Frontend)
**选择**: 先删除后端 Service/Application → 再删除 Controller → 最后删除前端页面和路由
**理由**:
- **依赖方向**: 前端依赖后端 API先删除底层可以避免悬挂引用
- **验证便利**: 删除后端接口后,前端调用会立即报 404易于发现遗漏
- **回滚简单**: 如果出问题,可以快速恢复后端接口,前端暂时保留也不影响
**备选方案 (未选择)**:
- **自顶向下 (Frontend → Backend)**: 先删除前端页面,可能导致后端接口成为"僵尸代码"难以发现
- **同步删除**: 一次性删除所有层,风险较高且难以定位问题
---
### Decision 2: 通过代码搜索确认 V1 专用性
**选择**: 对每个待删除的方法/接口进行全局搜索,确认仅被 V1 页面调用
**搜索关键字**:
- 前端页面: `CalendarView.vue`, `BudgetView.vue`, `statisticsV1/Index.vue`
- API 方法: `GetDailyStatistics`, `GetUncoveredCategories`, `GetArchiveSummary`, `GetBalanceStatistics`
- 路由: `/calendar`, `/budget`, `name: 'statistics'`
**验证标准**:
- 如果搜索结果仅出现在以上三个页面中 → 可以安全删除
- 如果出现在其他文件中 → 标记为"需保留"或"需进一步分析"
**理由**:
- **准确性**: 避免误删 V2 依赖的代码
- **可追溯**: 搜索结果可作为删除依据留存
---
### Decision 3: 保留共享组件,即使其仅在 V1 中被直接使用
**选择**: 保留 `TransactionList`, `TransactionDetail`, `PopupContainer`, `SmartClassifyButton` 等组件
**判断依据**:
- 通过搜索确认这些组件在 V2 页面中有引用
- 即使组件内部包含 V1 特定逻辑(如全局事件监听),也不在本次变更中修改
**理由**:
- **最小化风险**: 删除共享组件可能导致 V2 功能异常
- **职责分离**: 组件优化应作为独立的重构任务,不在本次删除范围内
---
### Decision 4: 移除全局事件监听 (条件性)
**选择**: 如果全局事件 (`transaction-deleted`, `transactions-changed`) **仅在 V1 页面中监听**,则移除事件触发代码
**验证流程**:
1. 搜索 `window.addEventListener('transaction-deleted')`
2. 确认监听器仅在 `CalendarView.vue``statisticsV1/Index.vue` 中注册
3. 搜索 `window.dispatchEvent(new Event('transaction-deleted'))`
4. 如果触发位置在共享组件中(如 `TransactionDetail.vue`),检查该组件是否被 V2 使用
- 如果被 V2 使用 → 保留事件触发代码(即使 V1 删除后无人监听)
- 如果仅 V1 使用 → 删除事件触发代码
**理由**:
- **保守策略**: 优先保证 V2 功能不受影响
- **技术债务**: 遗留的无用事件触发代码可作为后续清理任务
---
### Decision 5: 采用手动测试验证 V2 功能
**选择**: 删除完成后,手动打开以下 V2 页面并验证核心功能
**测试清单**:
1. **统计 V2** (`/statistics-v2`):
- 月度统计数据正常加载
- 分类统计饼图正常渲染
- 日度统计折线图正常渲染
- 交易列表正常展示和滚动加载
2. **预算 V2** (`/budget-v2`):
- 预算列表正常加载
- 分类统计(月度+年度)正常显示
- 预算创建/编辑/删除功能正常
- 存款预算导航和显示正常
3. **日历 V2** (`/calendar-v2`):
- 日历视图正常渲染
- 日期选择和统计数据加载正常
- 交易详情查看和编辑正常
**理由**:
- **V2 无单元测试覆盖**: 当前项目主要测试集中在后端,前端缺少自动化测试
- **快速反馈**: 手动测试可以在 10 分钟内完成所有关键路径验证
- **用户体验保证**: 确保最终用户不会遇到功能异常
**备选方案 (未选择)**:
- **仅依赖编译检查**: 无法发现运行时错误(如路由配置错误)
- **编写自动化测试**: 时间成本过高,且本次变更不涉及逻辑修改
---
## Risks / Trade-offs
### Risk 1: 误删 V2 依赖的代码
**表现**: 删除后端接口或前端方法,导致 V2 页面调用失败
**缓解措施**:
- 采用 Decision 2 的搜索验证流程,确保每个删除操作都经过确认
- 先删除后端 Service 层,观察前端是否有新的 404 错误
- 使用 Git 进行版本控制,支持快速回滚
**残留影响**:
- 如果 V2 通过动态路由或字符串拼接调用接口,静态搜索可能遗漏
---
### Risk 2: 共享组件包含 V1 特定逻辑但未被清理
**表现**: 保留的组件中包含 V1 特定的条件判断或事件监听,成为"死代码"
**缓解措施**:
- 标记为 Non-Goal不在本次清理范围内
- 在任务清单中添加"后续优化"任务,记录需要清理的组件列表
**残留影响**:
- 代码库中保留少量无用代码,但不影响功能正确性
---
### Risk 3: 路由守卫中遗留 V1 相关逻辑
**表现**: 删除 V1 路由后,`router/index.js``useVersionStore` 中仍有版本切换逻辑
**缓解措施**:
- 在任务清单中明确包含"清理路由守卫"任务
- 搜索 `isV2()`, `useVersionStore`, `router.push` 等关键字,逐一检查
**残留影响**:
- 可能导致用户访问不存在的路由时出现 404 错误
---
### Risk 4: V2 页面功能异常但手动测试未覆盖
**表现**: 删除操作影响了 V2 的边缘功能(如智能分类、批量操作),但未在测试清单中
**缓解措施**:
- 优先测试 V2 的核心流程(查看、创建、编辑、删除)
- 如果发现问题,立即回滚对应的删除操作
- 记录测试结果,作为归档的一部分
**残留影响**:
- 边缘功能可能在生产环境中被用户发现,需要后续修复
---
### Trade-off 1: 保留共享组件 vs 彻底清理
**权衡**: 为了保证 V2 功能稳定,选择保留所有共享组件,即使部分组件包含 V1 特定逻辑
**代价**:
- 代码库中保留部分冗余代码
- 后续维护时可能需要额外的上下文理解
**收益**:
- 大幅降低误删风险
- 缩短变更周期,快速上线
---
### Trade-off 2: 自动化测试 vs 手动测试
**权衡**: 采用手动测试而非编写自动化测试用例
**代价**:
- 无法在 CI/CD 中自动验证 V2 功能
- 未来的变更可能再次破坏 V2 功能
**收益**:
- 节省测试编写时间(预计 2-3 小时)
- 适用于一次性删除任务,性价比高
---
## Migration Plan
### 阶段 1: 准备工作 (Pre-deletion)
1. 创建 feature 分支 `feature/remove-v1-modules`
2. 运行所有现有测试,确认当前状态正常
3. 备份 V1 相关文件列表(用于回滚)
### 阶段 2: 后端删除 (Backend Removal)
1. **Service 层删除**:
- 删除 `BudgetService.GetUncoveredCategoriesAsync`
- 删除 `BudgetService.GetArchiveSummaryAsync`
- 删除 `TransactionStatisticsService.GetBalanceStatisticsAsync`
- 编译验证,确认无编译错误
2. **Application 层删除**:
- 删除 `BudgetApplication.GetUncoveredCategoriesAsync`
- 删除 `BudgetApplication.GetArchiveSummaryAsync`
- 删除 `TransactionStatisticsApplication.GetBalanceStatisticsAsync`
- 编译验证
3. **Controller 层删除**:
- 删除 `TransactionRecordController.GetDailyStatisticsAsync`
- 删除 `BudgetController.GetUncoveredCategoriesAsync`
- 删除 `BudgetController.GetArchiveSummaryAsync`
- 删除 `TransactionStatisticsController.GetBalanceStatisticsAsync`
- 编译验证
4. **运行后端测试**:
```bash
dotnet test WebApi.Test/WebApi.Test.csproj
```
- 移除失败的 V1 相关测试用例
- 确保其他测试通过
### 阶段 3: 前端删除 (Frontend Removal)
1. **API 客户端清理**:
- 清理 `Web/src/api/transactionRecord.js` 中的 `GetDailyStatistics` 调用
- 清理 `Web/src/api/budget.js` 中的 `GetUncoveredCategories`, `GetArchiveSummary`
- 清理 `Web/src/api/statistics.js` 中的 `GetBalanceStatistics`
2. **页面文件删除**:
- 删除 `Web/src/views/CalendarView.vue`
- 删除 `Web/src/views/BudgetView.vue`
- 删除 `Web/src/views/statisticsV1/Index.vue`
- 删除 `Web/src/views/statisticsV1/` 目录
3. **路由配置清理**:
- 删除 `Web/src/router/index.js` 中的 V1 路由定义:
- `/calendar`
- `/budget`
- `/` (statisticsV1)
- 简化 `useVersionStore` 中的版本切换逻辑
- 移除路由守卫中的 V1 相关判断
4. **编译验证**:
```bash
cd Web && pnpm build
```
### 阶段 4: 手动测试验证 (Manual Testing)
按照 Decision 5 的测试清单,逐项验证 V2 功能:
1. 启动开发服务器: `pnpm dev`
2. 打开浏览器,访问 V2 页面
3. 验证核心功能(数据加载、交互、导航)
4. 记录测试结果
### 阶段 5: 代码审查和合并 (Code Review & Merge)
1. 提交变更到远程分支
2. 创建 Pull Request
3. 代码审查关注点:
- 确认删除的代码不在 V2 中被引用
- 检查是否有遗漏的 V1 特定逻辑
4. 合并到主分支
### 回滚策略
如果发现 V2 功能异常:
1. **立即回滚**: `git revert <commit-hash>`
2. **定位问题**: 使用 `git diff` 找到引起问题的删除操作
3. **部分恢复**: 仅恢复必要的文件或方法
4. **重新测试**: 确认恢复后 V2 功能正常
---
## Open Questions
### Q1: 是否需要保留 `[Obsolete]` 标记的接口一段时间?
**当前决策**: 直接删除,因为 V2 已稳定运行
**待确认**: 是否有外部系统或移动端 APP 仍在调用这些接口?如果有,需要先通知相关方并提供迁移时间
---
### Q2: 全局事件监听 (`transaction-deleted`) 在 V2 中是否还需要?
**当前决策**: 保留事件触发代码,即使 V1 删除后无人监听
**待确认**: V2 是否使用其他机制(如 Pinia store来实现组件间通信如果是可以在后续任务中移除全局事件
---
### Q3: 是否需要更新 API 文档 (Swagger/Scalar)
**当前决策**: 标记为 Non-Goal不在本次范围内
**待确认**: API 文档是否自动生成?如果是手动维护,需要添加任务来移除已删除接口的文档
---
### Q4: V2 页面是否有完整的功能对齐?
**当前假设**: V2 已完全替代 V1 功能
**待确认**: 是否有用户或内部团队仍在使用 V1 特有的功能(如 `GetUncoveredCategories`)?如果有,需要先在 V2 中实现对应功能
---
**设计文档完成**。本文档为实施团队提供了清晰的技术决策依据和操作步骤,确保删除操作安全可控。