Files
EmailBill/openspec/changes/archive/2026-02-14-fix-budget-income-calculation/design.md
SunCheng a88556c784 fix
2026-02-15 10:10:28 +08:00

166 lines
6.3 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 是一个预算跟踪应用,包含 .NET 10 后端和 Vue 3 前端。预算模块负责计算和展示预算执行情况,包括收入、支出和存款计划。
**当前问题**
1. 预算收入的实际金额计算错误,`BudgetRepository.GetCurrentAmountAsync` 方法查询交易记录时可能存在过滤条件问题
2. 存款计划卡片缺少透明度,用户无法了解"计划存款"金额的计算依据
3. 预算页面的卡片样式与统计页面不一致,影响视觉统一性
**技术栈**
- 后端:.NET 10, FreeSql ORM, xUnit 测试
- 前端Vue 3 Composition API, Vant UI, SCSS
- 数据库SQLite
**约束**
- 保持向后兼容,不破坏现有 API 契约
- 遵循项目现有的代码风格(中文注释、文件作用域命名空间)
- 样式修改需保持暗色主题兼容性
## Goals / Non-Goals
**Goals:**
- 修复收入预算实际金额的计算错误,确保数据准确性
- 为存款计划添加明细展示功能,提升透明度
- 统一预算页面与统计页面的卡片样式,提升 UI 一致性
- 添加单元测试覆盖修复的逻辑
**Non-Goals:**
- 不重构整个预算计算系统
- 不改变预算数据模型结构
- 不修改其他页面的卡片样式
- 不改变存款计划的计算算法本身
## Decisions
### 决策 1问题 1 的修复策略
**决定**:采用 TDD测试驱动开发方式修复计算错误
**方案对比**
- **方案 A选择**:先编写失败的单元测试复现 bug然后修复代码最后验证测试通过
- ✅ 确保 bug 被正确理解和修复
- ✅ 防止回归
- ⏱️ 需要额外编写测试
- **方案 B**:直接修复代码,手动验证
- ❌ 无法保证不引入新问题
- ❌ 缺少回归保护
**实施细节**
1.`WebApi.Test/` 中创建测试用例,使用实际数据复现"家庭年终奖金"的计算错误
2. 诊断 `BudgetRepository.GetCurrentAmountAsync` 的查询条件:
- 检查 `SelectedCategories``Contains` 匹配逻辑
- 验证日期范围过滤是否正确(`>=``<=`
- 确认 `TransactionType` 过滤条件
3. 修复后运行测试验证
### 决策 2存款计划明细的数据来源
**决定**:前端计算明细,不新增后端 API
**方案对比**
- **方案 A选择**:前端基于现有数据计算明细
- ✅ 不增加后端复杂度
- ✅ 响应速度快
- ⚠️ 前端需要理解计算逻辑
- **方案 B**:新增后端 API 返回计算明细
- ⏱️ 需要设计新的 DTO 和 API
- 🔄 增加网络往返
**实施细节**
1. 明细弹窗展示内容:
- **收入预算总计**:所有收入预算的限额和实际值
- **支出预算总计**:所有支出预算的限额和实际值
- **计划存款公式**`收入预算 - 支出预算 = 计划存款`
- **实际存款**:从 budget 对象获取
- **差额**`计划存款 - 实际存款`
2. 使用 Vant 的 Popup 组件实现弹窗
3.`BudgetCard.vue``header-actions` slot 添加"明细"图标按钮(`icon="info"`
### 决策 3样式统一的方案
**决定**:直接修改 `.chart-card` 样式,使其继承或匹配 `.common-card`
**方案对比**
- **方案 A选择**:修改 `BudgetChartAnalysis.vue` 中的 `.chart-card` 样式
- ✅ 改动最小,影响范围可控
- ✅ 立即见效
- **方案 B**:将所有预算卡片改为使用 `.common-card`
- ⏱️ 需要大量 HTML 结构修改
- ⚠️ 可能影响现有布局逻辑
- **方案 C**:创建新的统一卡片组件
- 🔄 过度设计
- ⏱️ 需要重构多个页面
**实施细节**
1. 修改 `Web/src/components/Budget/BudgetChartAnalysis.vue``.chart-card` 样式:
```scss
.chart-card {
background: var(--van-background-2);
border-radius: 16px; // 改为 16px
padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); // 统一阴影
border: 1px solid var(--van-border-color); // 添加边框
margin: 0 12px 16px; // 添加边距
}
```
2. 检查 `.gauge-card` 的特殊样式是否需要保留
3. 验证暗色主题下的效果
## Risks / Trade-offs
**风险 1问题 1 的修复可能影响其他预算类型**
- **缓解**:编写覆盖支出、收入、存款三种类型的测试用例
- **回退**:保留 git 历史,可快速回滚
**风险 2明细弹窗的计算逻辑可能与后端不一致**
- **缓解**:参考 `BudgetSavingsService.cs` 的计算逻辑,确保前端实现一致
- **验证**:与后端计算结果进行对比测试
**风险 3样式修改可能在某些设备或浏览器上显示异常**
- **缓解**:修改后在浏览器中测试深色/浅色主题
- **回退**:样式改动独立 commit可快速回滚
**权衡:前端计算 vs 后端计算**
- 选择前端计算明细可以减少 API 开销,但增加了前端复杂度
- 如果未来计算逻辑变得更复杂,可能需要迁移到后端
## Migration Plan
**部署步骤**
1. 后端修复和测试:
- 运行 `dotnet test` 确保所有测试通过
- 构建后端:`dotnet build`
2. 前端修改:
- 运行 `pnpm lint` 检查代码风格
- 构建前端:`pnpm build`
3. 浏览器验证:
- 测试收入预算的实际金额显示
- 测试存款计划明细弹窗
- 验证卡片样式一致性
**回滚策略**
- 所有改动都在独立分支,可快速回滚
- 数据库无结构变更,无需数据迁移
**验证清单**
- [ ] 收入预算的"家庭年终奖金"实际金额正确显示
- [ ] 存款计划卡片有明细按钮,点击显示计算详情
- [ ] 预算页面的卡片样式与统计页面一致
- [ ] 所有单元测试通过
- [ ] 前端 ESLint 无错误
## Open Questions
1. **问题 1 的具体原因**`SelectedCategories` 匹配问题还是日期范围问题?
- 需要查看实际数据库中的 `TransactionRecord` 和 `BudgetRecord` 数据
- 建议在修复前添加详细的日志输出
2. **明细弹窗是否需要支持历史月份查询?**
- 当前设计仅展示当前周期的明细
- 如果需要历史查询,可能需要后端 API 支持
3. **样式修改是否需要同步到其他使用 `.chart-card` 的组件?**
- 需要检查是否有其他页面使用了相同的类名
- 建议全局搜索 `.chart-card` 确认影响范围