6.1 KiB
6.1 KiB
Context
当前状态:
- 后端
BudgetStatsService已正确计算Description(HTML格式明细) 和Trend(每日累计金额数组) - Service 层的
BudgetStatsDto包含这两个字段 - 问题: Application 层在映射 DTO 时丢失了这两个字段,导致 API 响应不完整
- 前端使用 fallback 逻辑(线性估算)来弥补缺失数据,导致燃尽图显示为直线
约束:
- 修复必须向后兼容,不能破坏现有 API 契约
- 优先修复 Bug #4 和 #5(高优先级),因为修复简单且影响大
- 前端已有完整的数据处理逻辑,只需后端提供正确数据
Goals / Non-Goals
Goals:
- 修复
BudgetStatsDetailDTO 定义,添加Description和Trend字段 - 修复
BudgetApplication.GetCategoryStatsAsync中的 DTO 映射逻辑 - 修复前端路由配置和 Vant 组件注册问题
- 分析并修复账单删除功能和金额不一致问题
- 添加单元测试覆盖修复场景
Non-Goals:
- 不重构
BudgetStatsService的计算逻辑(已验证正确) - 不改变前端图表组件的渲染逻辑(已有完整支持)
- 不修改 API 路由或版本化(向后兼容)
Decisions
决策 1: 使用 init 关键字而非 set 来定义新字段
理由: BudgetStatsDetail 是 record 类型,遵循不可变对象模式。使用 init 确保字段只能在对象初始化时设置。
替代方案:
- 改用
class+set→ 违背现有代码风格,且失去 record 的值语义 - 保持
record+set→ C# 9+ 允许,但不符合不可变设计原则
决策 2: 在 Application 层映射时直接赋值,不做转换
理由: Service 层的 BudgetStatsDto.Trend 和 Description 已经是目标格式(List<decimal?> 和 string),无需额外处理。
实现:
Month = new BudgetStatsDetail
{
Limit = stats.Month.Limit,
Current = stats.Month.Current,
Remaining = stats.Month.Remaining,
UsagePercentage = stats.Month.UsagePercentage,
Trend = stats.Month.Trend, // ⬅️ 新增
Description = stats.Month.Description // ⬅️ 新增
}
决策 3: Bug #1 (路由跳转) - 修改底部导航配置而非路由定义
理由: 经验证,/statistics-v2 路由已存在且正常工作。问题出在底部导航组件中硬编码了错误的路由路径。
定位策略:
- 搜索底部导航组件代码 (通常包含
van-tabbar或router-link) - 检查"统计"标签的
to属性或path配置 - 修改为
/statistics-v2
决策 4: Bug #2 (删除功能) - 添加确认对话框而非直接删除
理由: 删除操作是破坏性的,应符合最佳实践要求用户确认。
实现:
const handleDelete = async () => {
const confirmed = await showConfirmDialog({
title: '确认删除',
message: '确定要删除这条账单吗?此操作无法撤销。'
})
if (confirmed) {
await deleteBill(billId)
closePopup()
}
}
决策 5: Bug #3 (组件警告) - 按需导入而非全局注册
理由: Vant 推荐使用按需导入,减少打包体积。全局注册可能是遗漏导致的警告。
验证步骤:
- 检查
main.ts或全局插件文件是否有DatetimePicker导入 - 如果缺失,添加
import { DatetimePicker } from 'vant'; app.use(DatetimePicker); - 或在使用组件的文件中局部导入
决策 6: Bug #6 (金额不一致) - 先验证是否虚拟消耗导致
理由: Bug-handoff 文档指出硬性预算 (📌标记) 会产生虚拟消耗,这是设计行为而非 bug。
验证逻辑:
- 检查不一致的预算是否标记为硬性预算
- 检查
BudgetService.GetPeriodRange返回的日期范围是否与periodStart/periodEnd一致 - 如果是虚拟消耗:在前端账单列表中添加提示说明
- 如果是日期范围问题:修复
BudgetResult的赋值逻辑
Risks / Trade-offs
风险 1: API 响应体积增大
问题: 新增 Trend 数组(月度31个元素,年度12个元素)会增加响应大小。
缓解措施:
Trend数据是前端绘制图表必需的,体积增长合理- 考虑后续添加 gzip 压缩到 API 响应(可选优化)
风险 2: 前端可能依赖旧的 fallback 逻辑
问题: 如果前端代码中有显式检查 trend.length === 0 的逻辑,可能会在修复后仍执行 fallback。
缓解措施:
- 在修复后端后,验证前端
BudgetChartAnalysis.vue:603和629行的条件是否正确处理非空 trend - 如果逻辑有问题,修改为
if (!trend || trend.length === 0)
风险 3: 测试覆盖不足可能导致回归
问题: 现有测试可能未覆盖 DTO 映射场景。
缓解措施:
- 在
WebApi.Test中添加针对BudgetApplication.GetCategoryStatsAsync的单元测试 - 验证返回的 DTO 包含非空的
Description和Trend - 使用
FluentAssertions编写清晰的断言
Migration Plan
部署步骤:
- 部署后端更新(向后兼容,前端可继续使用旧逻辑)
- 验证 API 响应包含新字段 (使用 Swagger 或浏览器开发工具)
- 前端无需额外部署(已支持新字段,会自动切换到真实数据)
- 清除浏览器缓存以确保使用最新前端代码
回滚策略:
- 如果新版本出现问题,回滚到上一个 commit
- API 是向后兼容的(只添加字段),旧版前端仍可正常工作
验证清单:
- 预算明细弹窗显示完整的 HTML 表格
- 燃尽图显示波动曲线而非直线
- 底部导航"统计"按钮正常跳转
- 删除账单功能弹出确认对话框并正常工作
- 控制台无
van-datetime-picker警告 - 金额不一致问题已分析并修复或说明
Open Questions
- Bug #6 金额不一致的根本原因: 需要在测试环境中验证是否为虚拟消耗导致,还是日期范围计算错误。
- 前端 fallback 逻辑是否需要移除: 当前 fallback 作为容错机制保留是否合理?还是应在有真实数据时完全禁用?
- 是否需要添加 E2E 测试: 当前只计划单元测试,是否需要添加端到端测试覆盖完整流程?