Files
hfb_sys/docs/前端金额字段适配指南.md
T
yml 86c080ae40 前端适配: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组件
- 测试金额显示和计算精度
2026-06-09 14:07:55 +08:00

12 KiB
Raw Blame History

前端金额字段适配指南

项目: HFB_SYS
目标: 适配后端金额分字段重构
状态: 后端已完成 ,前端待适配 📝


一、已完成的工作

1. Money 工具函数更新

文件: frontend/src/shared/utils/money.ts

新增函数:

// 分转角(四舍五入到0.1元)
export function centToJiao(cent: number | undefined | null): number

// 格式化分为角字符串(保留1位小数)
export function formatCent(cent: number | undefined | null): string

// 格式化分为角字符串并添加货币符号
export function formatCentWithSymbol(cent: number | undefined | null): string

// 元转分(四舍五入)
export function yuanToCent(yuan: number | undefined | null): number

2. Wallet API 类型定义更新

文件: frontend/src/features/wallet/api/wallet.ts

// 已修改
export interface WalletAccount {
  user_id: number
  available_balance_cent: number  // 改为 _cent
  frozen_balance_cent: number     // 改为 _cent
  status: WalletStatus
}

export interface WalletLedger {
  id: number
  ledger_no: string
  user_id: number
  order_id?: number
  direction: LedgerDirection
  amount_cent: number          // 改为 _cent
  balance_after_cent: number   // 改为 _cent
  balance_type: BalanceType
  biz_type: string
  biz_no: string
  remark: string
  created_at: string
}

// 已修改:发送时将元转为分
export async function rechargeWallet(amountYuan: number) {
  const amount_cent = Math.round(amountYuan * 100)
  const { data } = await apiClient.post<ApiResponse<WalletAccount>>('/wallet/recharge', { amount_cent })
  return data.data
}

二、待适配的文件清单

模块 1: Wallet(钱包)

1.1 WalletView.vue ⚠️ 需要修改

文件: frontend/src/features/wallet/views/WalletView.vue

需要修改的地方

<!--  54 余额展示 -->
<!-- 旧代码 -->
value: formatMoney(account.value.available_balance),

<!-- 新代码 -->
value: formatCent(account.value.available_balance_cent),

<!--  61 冻结余额展示 -->
<!-- 旧代码 -->
value: formatMoney(account.value.frozen_balance),

<!-- 新代码 -->
value: formatCent(account.value.frozen_balance_cent),

<!--  282 可用余额展示 -->
<!-- 旧代码 -->
<strong>{{ account ? formatMoney(account.available_balance) : '¥0.00' }}</strong>

<!-- 新代码 -->
<strong>{{ account ? formatCentWithSymbol(account.available_balance_cent) : '¥0.0' }}</strong>

<!--  380 流水金额展示 -->
<!-- 旧代码 -->
{{ amountPrefix(row.direction) }}{{ formatMoney(row.amount) }}

<!-- 新代码 -->
{{ amountPrefix(row.direction) }}{{ formatCent(row.amount_cent) }}

<!--  383 余额展示 -->
<!-- 旧代码 -->
{{ formatMoney(row.balance_after) }}

<!-- 新代码 -->
{{ formatCent(row.balance_after_cent) }}

<!--  408 支付金额展示 -->
<!-- 旧代码 -->
formatMoney(activeRechargePayment.amount_cent / 100)

<!-- 新代码 -->
formatCent(activeRechargePayment.amount_cent)

需要添加的 import

<script setup lang="ts">
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
// ... 其他 imports
</script>

删除的本地函数

// 删除这个函数(第 224-226 行),使用全局的 formatCent
function formatMoney(value: number) {
  return ${(Math.round(Number(value || 0) * 10) / 10).toFixed(1)}`
}

模块 2: Withdrawal(提现)

2.1 withdrawal.ts API ⚠️ 需要修改

文件: frontend/src/features/wallet/api/withdrawal.ts

需要修改的类型定义

export interface WithdrawalRequest {
  id: number
  withdraw_no: string
  user_id: number
  amount_cent: number          // 改为 _cent
  fee_cent: number             // 改为 _cent
  actual_amount_cent: number   // 改为 _cent
  payment_account_id: number
  // ... 其他字段
}

// 创建提现请求函数需要转换
export async function createWithdrawal(amountYuan: number, payment_account_id: number) {
  const amount_cent = Math.round(amountYuan * 100)
  const { data } = await apiClient.post<ApiResponse<WithdrawalRequest>>(
    '/wallet/withdrawal',
    { amount_cent, payment_account_id }
  )
  return data.data
}

2.2 WithdrawalView.vue ⚠️ 需要修改

文件: frontend/src/features/wallet/views/WithdrawalView.vue

需要修改的地方

<!-- 余额展示 -->
<!-- 旧代码 -->
<div class="balance-value">¥{{ formatMoney(account?.available_balance) }}</div>
<div class="balance-value frozen">¥{{ formatMoney(account?.frozen_balance) }}</div>

<!-- 新代码 -->
<div class="balance-value">{{ formatCentWithSymbol(account?.available_balance_cent) }}</div>
<div class="balance-value frozen">{{ formatCentWithSymbol(account?.frozen_balance_cent) }}</div>

<!-- 提现金额比较 -->
<!-- 旧代码 77 -->
account.value && withdrawForm.value.amount <= account.value.available_balance

<!-- 新代码 -->
account.value && withdrawForm.value.amount <= (account.value.available_balance_cent / 100)

<!-- 提现记录展示 -->
<!-- 需要找到所有 withdrawal.amount 的地方改为 -->
{{ formatCent(withdrawal.amount_cent) }}
{{ formatCent(withdrawal.fee_cent) }}
{{ formatCent(withdrawal.actual_amount_cent) }}

常量更新

// 这些是用户输入的元,不需要改
const MIN_AMOUNT = 10
const MAX_AMOUNT = 5000

模块 3: Order(订单)

3.1 orders.ts API ⚠️ 需要修改

文件: frontend/src/features/orders/api/orders.ts

需要修改的类型定义

export interface Order {
  id: number
  order_no: string
  // ... 其他字段
  display_amount_cent: number
  rent_amount_cent?: number
  owner_rent_amount_cent?: number
  deposit_amount_cent: number
  deposit_original_amount_cent: number
  deposit_waived_amount_cent: number
  platform_fee_cent?: number
  // ... 其他字段
}

export interface Checkout {
  id: number
  order_id: number
  // ... 其他字段
  display_amount_cent: number
  rent_amount_cent?: number
  owner_rent_amount_cent?: number
  platform_fee_cent?: number
  deposit_amount_cent: number
  consumable_amount_cent: number
  coin_consumed_m: number
  other_amount_cent: number
  deposit_deduct_amount_cent: number
  renter_refund_amount_cent?: number
  owner_income_amount_cent?: number
  // ... 其他字段
}

3.2 OrderDetailView.vue ⚠️ 需要修改

文件: frontend/src/features/orders/views/OrderDetailView.vue

查找所有使用金额的地方,改为使用 formatCent

<!-- 示例 -->
{{ formatCent(order.display_amount_cent) }}
{{ formatCent(order.rent_amount_cent) }}
{{ formatCent(order.deposit_amount_cent) }}
{{ formatCent(checkout.renter_refund_amount_cent) }}
{{ formatCent(checkout.owner_income_amount_cent) }}

3.3 MobileOrdersView.vue ⚠️ 需要修改

类似地修改所有金额展示。


模块 4: Listing(商品)

4.1 listings.ts API ⚠️ 需要修改

文件: frontend/src/features/listings/api/listings.ts

export interface Listing {
  id: number
  listing_no: string
  // ... 其他字段
  price_cent: number          // 改为 _cent
  deposit_amount_cent: number // 改为 _cent
  // ... 其他字段
}

// 创建/更新 Listing 时需要转换
export async function createListing(data: {
  title: string
  price: number
  deposit_amount: number
  // ... 其他字段
}) {
  const payload = {
    ...data,
    price_cent: Math.round(data.price * 100),
    deposit_amount_cent: Math.round(data.deposit_amount * 100),
  }
  // 删除旧字段
  delete payload.price
  delete payload.deposit_amount
  
  const { data: response } = await apiClient.post<ApiResponse<Listing>>(
    '/listings',
    payload
  )
  return response.data
}

4.2 ListingCard.vue ⚠️ 需要修改

查找所有显示价格和押金的地方:

{{ formatCent(listing.price_cent) }}
{{ formatCent(listing.deposit_amount_cent) }}

三、批量替换建议

全局搜索替换模式

  1. API 响应字段名

    available_balance → available_balance_cent
    frozen_balance → frozen_balance_cent
    amount → amount_cent
    balance_after → balance_after_cent
    price → price_cent
    deposit_amount → deposit_amount_cent
    rent_amount → rent_amount_cent
    owner_rent_amount → owner_rent_amount_cent
    platform_fee → platform_fee_cent
    
  2. 格式化函数调用

    formatMoney(xxx.amount) → formatCent(xxx.amount_cent)
    formatMoneyWithSymbol(xxx.amount) → formatCentWithSymbol(xxx.amount_cent)
    
  3. 金额比较

    // 旧代码
    if (inputAmount > account.available_balance) { ... }
    
    // 新代码
    if (inputAmount > (account.available_balance_cent / 100)) { ... }
    

四、测试检查清单

功能测试

  • 钱包余额正确显示(角精度)
  • 充值功能正常(输入元,后端收到分)
  • 提现功能正常(输入元,后端收到分)
  • 订单金额正确显示
  • 订单结算金额计算正确
  • 商品价格正确显示
  • 商品发布价格输入正常

显示精度测试

测试用例:

// 123.45 元 = 12345 分 → 显示 123.5 元
formatCent(12345) // 应该输出 "123.5"

// 123.44 元 = 12344 分 → 显示 123.4 元
formatCent(12344) // 应该输出 "123.4"

// 0.01 元 = 1 分 → 显示 0.0 元
formatCent(1) // 应该输出 "0.0"

边界值测试

  • 最小充值金额:0.01 元 = 1 分
  • 最小提现金额:10 元 = 1000 分
  • 最大提现金额:5000 元 = 500000 分
  • 零金额显示:formatCent(0) → "0.0"

五、常见问题

Q1: 为什么显示是角精度而不是分精度?

A: 业务需求只展示到角(0.1元),分精度对用户来说太细致且无实际意义。

Q2: 用户输入 123.45 元,后端收到多少?

A: yuanToCent(123.45) = 12345 分

Q3: 后端返回 12345 分,前端显示什么?

A: formatCent(12345) = "123.5" 元(四舍五入到角)

Q4: 如何处理浮点输入的精度?

A: 使用 Math.round(yuan * 100) 四舍五入到分,避免浮点误差。

Q5: 旧的 formatMoney 函数还能用吗?

A: 可以,但仅用于已经是元的数值。对于后端返回的分字段,必须使用 formatCent。


六、快速参考

常用代码片段

<script setup lang="ts">
import { formatCent, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'

// 显示金额
const displayAmount = formatCent(wallet.available_balance_cent)
const displayWithSymbol = formatCentWithSymbol(order.rent_amount_cent)

// 提交表单
async function submit() {
  const amountCent = yuanToCent(form.amount)
  await apiClient.post('/api/endpoint', { amount_cent: amountCent })
}

// 金额比较
if (inputAmount > (account.available_balance_cent / 100)) {
  console.log('余额不足')
}
</script>

<template>
  <!-- 显示金额 -->
  <div>余额{{ formatCentWithSymbol(account.available_balance_cent) }}</div>
  
  <!-- 输入金额 -->
  <el-input-number v-model="form.amount" :min="0.01" :precision="2" />
</template>

七、推荐的适配顺序

  1. 工具函数(已完成)
  2. Wallet API 类型(已完成)
  3. ⚠️ Wallet 组件(进行中)
  4. ⚠️ Withdrawal API 和组件
  5. ⚠️ Order API 和组件
  6. ⚠️ Listing API 和组件
  7. ⚠️ AdminFinance 组件(如果有)
  8. 🧪 完整测试

预计工作量: 2-3 小时
难度: 中等(主要是查找替换和测试)
风险: 低(类型系统会帮助发现遗漏的地方)


文档生成时间: 2026-06-09
作者: Claude Code AI Assistant