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>
This commit is contained in:
yml2213
2026-06-04 16:42:49 +08:00
co-authored by Claude Opus 4.7
parent e41d3a923a
commit 4ebf7f75fe
18 changed files with 1134 additions and 74 deletions
@@ -6,6 +6,7 @@ import { useRoute, useRouter } from "vue-router";
import { fetchListing, type Listing } from "@/features/listings/api/listings";
import { createOrder } from "@/features/orders/api/orders";
import { useSessionStore } from "@/stores/session";
import { roundMoney, formatMoney } from "@/shared/utils/money";
import {
assetRegions,
formatEstimatedRentalDuration,
@@ -45,7 +46,7 @@ onMounted(async () => {
const orderTotal = computed(() => {
if (!listing.value) return "0";
return `${Math.round(getListingDisplayPrice(listing.value))}`;
return formatMoney(getListingDisplayPrice(listing.value));
});
const orderPriceBreakdown = computed(() => {
@@ -59,7 +60,7 @@ const orderPriceBreakdown = computed(() => {
return {
rent: getListingRentPrice(listing.value),
consumable: getListingConsumablePrice(listing.value),
total: Math.round(getListingDisplayPrice(listing.value)),
total: roundMoney(getListingDisplayPrice(listing.value)),
};
});
@@ -158,7 +159,7 @@ function readError(error: unknown, fallback: string) {
}
function listingPrice(item: Listing) {
return `${Math.round(getListingDisplayPrice(item))}`;
return formatMoney(getListingDisplayPrice(item));
}
</script>
@@ -6,6 +6,7 @@ import { showToast, showDialog } from "vant";
import { fetchListing, type Listing } from "@/features/listings/api/listings";
import { createOrder } from "@/features/orders/api/orders";
import { useSessionStore } from "@/stores/session";
import { formatMoney } from "@/shared/utils/money";
import {
assetRegions,
formatHafCoinM,
@@ -43,8 +44,8 @@ onMounted(async () => {
});
const orderTotal = computed(() => {
if (!listing.value) return "0";
return `${Math.round(getListingDisplayPrice(listing.value))}`;
if (!listing.value) return "0.0";
return formatMoney(getListingDisplayPrice(listing.value));
});
const orderPriceBreakdown = computed(() => {
@@ -5,6 +5,7 @@ import { fetchOrders, type Order } from '@/features/orders'
import { useSessionStore } from '@/stores/session'
import { handoffStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
import { formatDateTime } from '@/utils/time'
import { formatMoney } from '@/shared/utils/money'
const session = useSessionStore()
const loading = ref(false)
@@ -61,7 +62,7 @@ function actionText(order: Order) {
}
function money(value: unknown) {
return Math.round(Number(value || 0))
return formatMoney(Number(value || 0))
}
</script>