Files
hfb_sys/docs/MONEY_PRECISION_REFACTOR.md
T
yml2213andClaude Opus 4.7 4ebf7f75fe refactor: 统一全项目金额精度为角(0.1元)
## 变更概述
将全项目金额处理从整元四舍五入统一为角精度(0.1元),提高金额计算准确性和显示一致性。

## 后端改动
- 新增 pkg/money/format.go 统一金额处理包
  - Round(): 角精度四舍五入
  - Min/Max(): 金额比较
  - Format(): 格式化字符串
- 更新业务模块使用统一金额函数
  - internal/modules/order: 订单结算改为角精度
  - internal/modules/dispute: 纠纷金额处理
  - internal/modules/listing: 商品定价和存储
- 更新测试用例期望值为角精度

## 前端改动
- 新增 shared/utils/money.ts 金额工具函数
  - roundMoney(): 角精度四舍五入
  - formatMoney(): 格式化为字符串(保留1位小数)
  - formatMoneyWithSymbol(): 添加¥符号
- 更新金额计算和显示逻辑
  - shared/utils/pricing.ts: 定价计算
  - shared/utils/listingDisplay.ts: 商品显示
  - shared/composables/useMoney.ts: 组合式函数
- 修复视图文件导入声明
  - features/listings/views: 商品详情页
  - features/seller/views: 卖家管理页

## 效果
- 金额显示:¥123.0(统一保留1位小数)
- 计算精度:12.45 -> 12.5(角精度)
- 减少误差:避免整元四舍五入损失
- 显示一致:全项目统一格式

## 测试
-  后端单元测试通过
-  TypeScript 类型检查通过
-  开发环境正常运行

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-04 16:42:49 +08:00

4.6 KiB

金额精度统一优化说明

修改内容

1. 后端改动

新增统一金额处理包:

  • backend/pkg/money/format.go - 提供统一的金额处理函数

核心函数:

// Round 将金额四舍五入到角(0.1元)
func Round(value float64) float64 {
    return math.Round(value*10) / 10
}

// Min/Max 返回较小/较大金额(角精度)
func Min(a, b float64) float64
func Max(a, b float64) float64

// Format 格式化金额为字符串(保留1位小数)
func Format(value float64) string  // 例如:12.3 -> "12.3"

// FormatWithSymbol 格式化金额并添加货币符号
func FormatWithSymbol(value float64) string  // 例如:12.3 -> "¥12.3"

修改的模块:

  • backend/internal/modules/order/repository.go - 订单金额计算
  • backend/internal/modules/dispute/repository.go - 纠纷金额处理
  • backend/internal/modules/listing/service.go - 商品定价
  • backend/internal/modules/listing/repository.go - 商品金额存储

所有 roundMoney 函数从 Math.Round(value) 改为 money.Round(value)

2. 前端改动

新增统一金额工具:

  • frontend/src/shared/utils/money.ts - 统一金额处理

核心函数:

// roundMoney 将金额四舍五入到角(0.1元)
export function roundMoney(value: number): number {
  return Math.round(value * 10) / 10
}

// formatMoney 格式化金额为字符串(保留1位小数)
export function formatMoney(value: number | undefined | null): string {
  const num = Number(value || 0)
  return roundMoney(num).toFixed(1)  // 例如:12.3 -> "12.3"
}

// formatMoneyWithSymbol 格式化金额并添加货币符号
export function formatMoneyWithSymbol(value: number | undefined | null): string {
  return ${formatMoney(value)}`  // 例如:12.3 -> "¥12.3"
}

修改的文件:

  • frontend/src/shared/utils/pricing.ts - 定价计算逻辑
  • frontend/src/shared/utils/listingDisplay.ts - 商品显示逻辑
  • frontend/src/shared/composables/useMoney.ts - 金额组合式函数
  • frontend/src/features/listings/views/*.vue - 商品详情页
  • frontend/src/features/seller/views/*.vue - 卖家管理页

所有 Math.round(value) 改为 roundMoney(value)(角精度) 所有 Math.round(value * 10) / 10 统一为 roundMoney(value)

3. 数据库字段

现有字段定义保持不变:

DECIMAL(12,2)  -- 仍然支持分精度存储

虽然数据库支持分精度,但业务层统一使用角精度(0.1元),确保:

  • 用户界面显示一致
  • 金额计算规则统一
  • 避免浮点数累积误差

影响范围

价格显示变化

优化前:

  • 商品价格:¥123(整元四舍五入)
  • 押金:¥50(整元)
  • 租金:¥100(整元)

优化后:

  • 商品价格:¥123.5(角精度)
  • 押金:¥50.0(保留1位小数)
  • 租金:¥100.3(角精度)

计算逻辑变化

示例:消耗品金额计算

优化前:

// 12.34 + 8.67 = 21.01 -> Math.round(21.01) = 21

优化后:

// 12.34 + 8.67 = 21.01 -> roundMoney(21.01) = 21.0

测试用例需要更新

后端测试:

// 旧断言
if settlement.ActualRentAmount != 244.00 { ... }

// 新断言(角精度)
if settlement.ActualRentAmount != 244.0 { ... }

前端测试:

// 旧期望值:整数
expect(total).toBe(123)

// 新期望值:保留1位小数
expect(total).toBe('123.0')

验证方法

1. 后端编译测试

cd backend
go build ./pkg/money/...
go test ./internal/modules/order/...
go test ./internal/modules/dispute/...
go test ./internal/modules/listing/...

2. 前端类型检查

cd frontend
npm run typecheck
npm run build

3. 手动测试场景

  1. 创建商品,价格输入 123.45 → 显示 ¥123.5
  2. 下单支付,总价显示角精度
  3. 押金计算,支持 0.1 元精度
  4. 订单结算,租金/押金退款显示角精度
  5. 后台管理,所有金额列显示统一格式

注意事项

  1. 向下兼容:数据库已有数据自动适配,首次读取时会被 roundMoney 调整为角精度
  2. 边界情况:12.35 会四舍五入为 12.4(银行家舍入法)
  3. 显示一致性:前端所有金额都使用 .toFixed(1) 保证显示格式统一
  4. API 响应:后端返回的金额字段保持 DECIMAL(12,2),但计算逻辑已改为角精度

后续优化建议

  1. 补充单元测试覆盖金额边界情况
  2. 添加 E2E 测试验证支付流程金额正确性
  3. 考虑是否需要配置化精度(方便未来调整)
  4. 监控生产环境金额误差(理论上不应有差异)