Files
EmailBill/openspec/changes/archive/2026-02-17-remove-v1-calendar-stats-budget/design.md

336 lines
12 KiB
Markdown
Raw Normal View History

2026-02-18 21:16:45 +08:00
## 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 中实现对应功能
---
**设计文档完成**。本文档为实施团队提供了清晰的技术决策依据和操作步骤,确保删除操作安全可控。