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

6.3 KiB
Raw Blame History

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 的查询条件:
    • 检查 SelectedCategoriesContains 匹配逻辑
    • 验证日期范围过滤是否正确(>=<=
    • 确认 TransactionType 过滤条件
  3. 修复后运行测试验证

决策 2存款计划明细的数据来源

决定:前端计算明细,不新增后端 API

方案对比

  • 方案 A选择:前端基于现有数据计算明细
    • 不增加后端复杂度
    • 响应速度快
    • ⚠️ 前端需要理解计算逻辑
  • 方案 B:新增后端 API 返回计算明细
    • ⏱️ 需要设计新的 DTO 和 API
    • 🔄 增加网络往返

实施细节

  1. 明细弹窗展示内容:
    • 收入预算总计:所有收入预算的限额和实际值
    • 支出预算总计:所有支出预算的限额和实际值
    • 计划存款公式收入预算 - 支出预算 = 计划存款
    • 实际存款:从 budget 对象获取
    • 差额计划存款 - 实际存款
  2. 使用 Vant 的 Popup 组件实现弹窗
  3. BudgetCard.vueheader-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 样式:
    .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 匹配问题还是日期范围问题?

    • 需要查看实际数据库中的 TransactionRecordBudgetRecord 数据
    • 建议在修复前添加详细的日志输出
  2. 明细弹窗是否需要支持历史月份查询?

    • 当前设计仅展示当前周期的明细
    • 如果需要历史查询,可能需要后端 API 支持
  3. 样式修改是否需要同步到其他使用 .chart-card 的组件?

    • 需要检查是否有其他页面使用了相同的类名
    • 建议全局搜索 .chart-card 确认影响范围