前端适配:Money工具函数和Wallet API类型更新
核心改造: 1. Money工具函数:新增formatCent、formatCentWithSymbol、yuanToCent等函数 2. Wallet API类型:WalletAccount和WalletLedger改为*_cent字段 3. API请求函数:rechargeWallet和startWalletRechargePayment自动转换元为分 4. 文档:生成完整的前端适配指南 技术细节: - formatCent: 分转角并格式化(四舍五入到0.1元) - yuanToCent: 元转分(用于表单提交) - API函数自动处理转换,组件层面仍使用元 - 生成详细的适配指南供后续组件修改参考 下一步: - 按照前端适配指南修改所有Vue组件 - 测试金额显示和计算精度
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
# 金额统一重构完成报告
|
||||
|
||||
**项目**: HFB_SYS - 游戏账号租赁平台
|
||||
**重构目标**: 统一金额存储和计算,从 float64(元)改为 int64(分)
|
||||
**完成时间**: 2026-06-09
|
||||
**状态**: ✅ 后端完成,前端待适配
|
||||
|
||||
---
|
||||
|
||||
## 一、重构目标
|
||||
|
||||
### 问题背景
|
||||
- **浮点精度问题**: 使用 float64 存储金额导致精度丢失
|
||||
- **角精度展示**: 业务需求仅展示到角(0.1元),但存储需要精确到分
|
||||
- **计算误差**: 浮点运算在累加、结算时可能产生误差
|
||||
|
||||
### 解决方案
|
||||
- **全链路整数存储**: 数据库 BIGINT(分) → Go int64 → API int64 → 前端 number
|
||||
- **整数运算**: 所有金额计算使用整数,消除浮点误差
|
||||
- **角精度展示**: 存储精确到分,展示时转换为角(除以10,保留1位小数)
|
||||
|
||||
---
|
||||
|
||||
## 二、已完成的工作
|
||||
|
||||
### 1. 数据库迁移 ✅
|
||||
|
||||
**文件**: `backend/migrations/000006_add_money_cent_fields.sql`
|
||||
|
||||
新增字段:
|
||||
```sql
|
||||
-- rental_orders 表
|
||||
ALTER TABLE rental_orders
|
||||
ADD COLUMN rent_amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN owner_rent_amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN deposit_amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN deposit_original_amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN deposit_waived_amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN platform_fee_cent BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
-- rental_listings 表
|
||||
ALTER TABLE rental_listings
|
||||
ADD COLUMN price_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN deposit_amount_cent BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
-- wallet_accounts 表
|
||||
ALTER TABLE wallet_accounts
|
||||
ADD COLUMN available_balance_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN frozen_balance_cent BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
-- wallet_ledger 表
|
||||
ALTER TABLE wallet_ledger
|
||||
ADD COLUMN amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN balance_after_cent BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
-- withdrawal_requests 表
|
||||
ALTER TABLE withdrawal_requests
|
||||
ADD COLUMN amount_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN fee_cent BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN actual_amount_cent BIGINT NOT NULL DEFAULT 0;
|
||||
```
|
||||
|
||||
历史数据迁移:
|
||||
```sql
|
||||
UPDATE rental_orders SET
|
||||
rent_amount_cent = CAST(ROUND(rent_amount * 100) AS SIGNED),
|
||||
owner_rent_amount_cent = CAST(ROUND(owner_rent_amount * 100) AS SIGNED),
|
||||
...;
|
||||
```
|
||||
|
||||
### 2. Model 层 ✅
|
||||
|
||||
**文件**: `backend/internal/model/*.go`
|
||||
|
||||
为所有金额相关表新增 *Cent 字段:
|
||||
```go
|
||||
type RentalOrder struct {
|
||||
RentAmount float64 `gorm:"type:decimal(12,2)" json:"-"`
|
||||
RentAmountCent int64 `gorm:"not null;default:0" json:"-"`
|
||||
OwnerRentAmount float64 `gorm:"type:decimal(12,2)" json:"-"`
|
||||
OwnerRentAmountCent int64 `gorm:"not null;default:0" json:"-"`
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Money 工具包 ✅
|
||||
|
||||
**文件**: `backend/pkg/money/money.go`
|
||||
|
||||
核心函数:
|
||||
```go
|
||||
// ToCent 元转分(四舍五入)
|
||||
func ToCent(yuan float64) int64
|
||||
|
||||
// FromCent 分转元
|
||||
func FromCent(cent int64) float64
|
||||
|
||||
// FormatCent 格式化分为元字符串(2位小数)
|
||||
func FormatCent(cent int64) string
|
||||
|
||||
// FormatJiao 格式化分为角字符串(1位小数)
|
||||
func FormatJiao(cent int64) string
|
||||
|
||||
// ToJiao 分转角(四舍五入)
|
||||
func ToJiao(cent int64) int64
|
||||
|
||||
// FromJiao 角转分
|
||||
func FromJiao(jiao int64) int64
|
||||
```
|
||||
|
||||
### 4. 核心业务模块 ✅
|
||||
|
||||
#### Wallet 模块 ✅
|
||||
- **DTO**: 所有金额字段改为 `*Cent int64`
|
||||
- **Entry结构**: `AmountCent int64` 替代 `Amount float64`
|
||||
- **整数运算**: `applyEntry` 使用纯整数加减
|
||||
- **删除**: `roundWalletMoney` 函数(不再需要)
|
||||
|
||||
#### Withdrawal 模块 ✅
|
||||
- **DTO**: `WithdrawalDTO`, `CreateWithdrawalRequest` 改为 `*Cent`
|
||||
- **手续费计算**: 改为分单位
|
||||
- **最小/最大金额**: MinWithdrawalAmountCent = 1000 (10元)
|
||||
|
||||
#### Order 模块 ✅(最复杂)
|
||||
- **DTO**: `OrderDTO`, `CheckoutDTO`, `RefundStatusDTO` 改为 `*Cent`
|
||||
- **内部结构体**: `orderPricing`, `checkoutSettlement` 改为分字段
|
||||
- **结算计算**: `calculateCheckoutSettlement` 内部用元计算保持兼容,返回分
|
||||
- **钱包操作**: 所有 `wallet.AppendEntries` 调用改为 `AmountCent`
|
||||
- **退款逻辑**: 3 处改为直接使用分字段相加
|
||||
|
||||
#### Dispute 模块 ✅
|
||||
- **修复**: 2 处 `wallet.Entry` 调用改为 `AmountCent`
|
||||
|
||||
#### Listing 模块 ✅
|
||||
- **DTO**: `PriceCent`, `DepositAmountCent`
|
||||
- **筛选排序**: 改为使用分字段
|
||||
- **价格视图**: `sanitizePriceForOwner` 使用 `PriceCent`
|
||||
|
||||
#### Payment 模块 ✅(已适配)
|
||||
- **DTO**: `PaymentDTO`, `RefundDTO` 已使用 `AmountCent`
|
||||
- **退款函数**: `StartRefund(orderID, refundAmountCent int64, ...)` 已适配
|
||||
|
||||
#### AdminFinance 模块 ✅(已适配)
|
||||
- **DTO**: `FinanceSummaryDTO`, `FinanceDailyDTO` 已使用 `*Cent` 字段
|
||||
- **查询**: 财务统计查询已适配
|
||||
|
||||
---
|
||||
|
||||
## 三、技术实现亮点
|
||||
|
||||
### 1. 数据双字段并存
|
||||
新旧字段共存,便于渐进式迁移和回滚:
|
||||
```go
|
||||
type RentalOrder struct {
|
||||
RentAmount float64 `gorm:"type:decimal(12,2)" json:"-"` // 旧字段
|
||||
RentAmountCent int64 `gorm:"not null;default:0" json:"-"` // 新字段
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 整数运算消除精度问题
|
||||
```go
|
||||
// 旧代码(浮点运算)
|
||||
account.AvailableBalance += amount
|
||||
account.AvailableBalance = roundMoney(account.AvailableBalance)
|
||||
|
||||
// 新代码(整数运算)
|
||||
account.AvailableBalanceCent += amountCent // 直接加减,无精度损失
|
||||
```
|
||||
|
||||
### 3. 复杂结算兼容性处理
|
||||
Order 模块的 `calculateCheckoutSettlement` 函数:
|
||||
```go
|
||||
func calculateCheckoutSettlement(order model.RentalOrder, consumableAmount float64,
|
||||
coinConsumedM float64, depositDeductAmount float64) checkoutSettlement {
|
||||
|
||||
// 读取分字段并转为元(保持现有计算逻辑)
|
||||
orderRentAmount := float64(order.RentAmountCent) / 100
|
||||
orderOwnerRentAmount := float64(order.OwnerRentAmountCent) / 100
|
||||
|
||||
// 现有的复杂计算逻辑...
|
||||
actualRentAmount := minMoney(roundMoney(usedBuyerCoinPrice+usedBuyerConsumablePrice), orderRentAmount)
|
||||
|
||||
// 最后转换为分返回
|
||||
return checkoutSettlement{
|
||||
ActualRentAmountCent: int64(math.Round(actualRentAmount * 100)),
|
||||
OwnerRentIncomeCent: int64(math.Round(ownerRentIncome * 100)),
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 角精度展示
|
||||
```go
|
||||
// 后端返回分
|
||||
dto.PriceCent = 12345 // 123.45 元
|
||||
|
||||
// 前端展示角(formatJiao)
|
||||
formatJiao(12345) → "123.5" // 显示 123.5 元
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、Git 提交记录
|
||||
|
||||
```bash
|
||||
eaa10d8 - 数据库迁移:新增金额分字段并迁移历史数据
|
||||
b80719d - Model层:新增金额分字段定义
|
||||
e2780ff - Money工具包:实现分↔角转换和格式化函数
|
||||
01909ee - Wallet与Withdrawal模块:完成分字段重构
|
||||
d803089 - Order模块:完成分字段重构
|
||||
4f3be22 - Dispute模块:修复wallet.Entry调用
|
||||
2185cd1 - Listing模块:完成分字段重构
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、编译验证
|
||||
|
||||
```bash
|
||||
✅ go build -o /dev/null ./cmd/api
|
||||
编译通过,无错误
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、下一步工作:前端适配
|
||||
|
||||
### 需要修改的文件
|
||||
|
||||
#### 1. TypeScript 类型定义
|
||||
更新所有 API 接口类型定义,将金额字段改为 `*_cent: number`
|
||||
|
||||
#### 2. 工具函数
|
||||
```typescript
|
||||
// frontend/src/utils/money.ts
|
||||
export function formatCent(cent: number): string {
|
||||
return (cent / 100).toFixed(2);
|
||||
}
|
||||
|
||||
export function formatJiao(cent: number): string {
|
||||
return (Math.round(cent / 10) / 10).toFixed(1);
|
||||
}
|
||||
|
||||
export function toCent(yuan: number): number {
|
||||
return Math.round(yuan * 100);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 涉及的模块
|
||||
- **Wallet**: 余额展示、充值输入
|
||||
- **Withdrawal**: 提现金额输入、手续费显示
|
||||
- **Order**: 订单金额展示、结算详情
|
||||
- **Listing**: 商品价格展示、发布价格输入
|
||||
- **AdminFinance**: 财务报表展示
|
||||
|
||||
### 改造原则
|
||||
1. **表单输入**: 用户输入元 → 乘以100转为分 → 发送后端
|
||||
2. **数据展示**: 后端返回分 → 除以10四舍五入 → 显示角(1位小数)
|
||||
3. **内部计算**: 尽量使用分进行计算,避免浮点运算
|
||||
|
||||
---
|
||||
|
||||
## 七、测试建议
|
||||
|
||||
### 1. 单元测试
|
||||
- Order 模块结算计算
|
||||
- Money 工具函数边界值
|
||||
|
||||
### 2. 集成测试
|
||||
- 完整订单流程
|
||||
- 钱包充值提现流程
|
||||
|
||||
### 3. 手工测试场景
|
||||
- 订单结算精度(租金+押金+手续费)
|
||||
- 钱包余额累加(大量小额交易)
|
||||
- 提现手续费计算
|
||||
- 边界值(最小充值、最大提现)
|
||||
|
||||
---
|
||||
|
||||
## 八、回滚方案
|
||||
|
||||
如需回滚,执行以下步骤:
|
||||
|
||||
1. **代码回滚**
|
||||
```bash
|
||||
git revert 2185cd1 # Listing
|
||||
git revert 4f3be22 # Dispute
|
||||
git revert d803089 # Order
|
||||
git revert 01909ee # Wallet & Withdrawal
|
||||
git revert e2780ff # Money工具包
|
||||
git revert b80719d # Model
|
||||
```
|
||||
|
||||
2. **数据库回滚**
|
||||
```sql
|
||||
-- 不需要删除新字段,旧字段仍然存在
|
||||
-- 如果需要,可以执行:
|
||||
ALTER TABLE rental_orders DROP COLUMN rent_amount_cent;
|
||||
-- ... 其他表
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、总结
|
||||
|
||||
✅ **后端重构完成度**: 100%
|
||||
✅ **编译状态**: 通过
|
||||
✅ **代码质量**: 保持原有逻辑,仅替换金额字段
|
||||
✅ **兼容性**: 新旧字段并存,便于迁移
|
||||
|
||||
🎯 **下一阶段**: 前端适配(约需 2-3 小时)
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**: 2026-06-09
|
||||
**作者**: Claude Code AI Assistant
|
||||
Reference in New Issue
Block a user