336 lines
12 KiB
Markdown
336 lines
12 KiB
Markdown
|
|
## 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 中实现对应功能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**设计文档完成**。本文档为实施团队提供了清晰的技术决策依据和操作步骤,确保删除操作安全可控。
|