fix(admin-service): 修复金额显示单位错误

移除错误的 1e8 乘除转换:
- 数据库存储的是实际金额 (Decimal(20,8)),不需要缩放
- decimalToBigint → decimalToString: 直接格式化为字符串
- 移除 controller 中不再需要的 formatDecimal 方法
- 更新接口类型: bigint → string (金额相关字段)

影响的接口:
- PlantingSummary.totalAmount
- PlantingLedgerItem.totalAmount
- WalletSummary 所有余额字段
- WalletLedgerItem.amount/balanceAfter
- SystemAccountLedger.amount/balanceAfter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-07 21:15:28 -08:00
parent 1c5fd9eaad
commit eccc637a02
3 changed files with 81 additions and 93 deletions

View File

@ -217,7 +217,7 @@ export class UserDetailController {
summary: { summary: {
totalOrders: summary?.totalOrders || 0, totalOrders: summary?.totalOrders || 0,
totalTreeCount: summary?.totalTreeCount || 0, totalTreeCount: summary?.totalTreeCount || 0,
totalAmount: this.formatDecimal(summary?.totalAmount), totalAmount: summary?.totalAmount || '0',
effectiveTreeCount: summary?.effectiveTreeCount || 0, effectiveTreeCount: summary?.effectiveTreeCount || 0,
pendingTreeCount: summary?.pendingTreeCount || 0, pendingTreeCount: summary?.pendingTreeCount || 0,
firstPlantingAt: summary?.firstPlantingAt?.toISOString() || null, firstPlantingAt: summary?.firstPlantingAt?.toISOString() || null,
@ -227,7 +227,7 @@ export class UserDetailController {
orderId: item.orderId.toString(), orderId: item.orderId.toString(),
orderNo: item.orderNo, orderNo: item.orderNo,
treeCount: item.treeCount, treeCount: item.treeCount,
totalAmount: this.formatDecimal(item.totalAmount), totalAmount: item.totalAmount,
status: item.status, status: item.status,
selectedProvince: item.selectedProvince, selectedProvince: item.selectedProvince,
selectedCity: item.selectedCity, selectedCity: item.selectedCity,
@ -281,32 +281,32 @@ export class UserDetailController {
return { return {
summary: { summary: {
usdtAvailable: this.formatDecimal(summary?.usdtAvailable), usdtAvailable: summary?.usdtAvailable || '0',
usdtFrozen: this.formatDecimal(summary?.usdtFrozen), usdtFrozen: summary?.usdtFrozen || '0',
dstAvailable: this.formatDecimal(summary?.dstAvailable), dstAvailable: summary?.dstAvailable || '0',
dstFrozen: this.formatDecimal(summary?.dstFrozen), dstFrozen: summary?.dstFrozen || '0',
bnbAvailable: this.formatDecimal(summary?.bnbAvailable), bnbAvailable: summary?.bnbAvailable || '0',
bnbFrozen: this.formatDecimal(summary?.bnbFrozen), bnbFrozen: summary?.bnbFrozen || '0',
ogAvailable: this.formatDecimal(summary?.ogAvailable), ogAvailable: summary?.ogAvailable || '0',
ogFrozen: this.formatDecimal(summary?.ogFrozen), ogFrozen: summary?.ogFrozen || '0',
rwadAvailable: this.formatDecimal(summary?.rwadAvailable), rwadAvailable: summary?.rwadAvailable || '0',
rwadFrozen: this.formatDecimal(summary?.rwadFrozen), rwadFrozen: summary?.rwadFrozen || '0',
hashpower: this.formatDecimal(summary?.hashpower), hashpower: summary?.hashpower || '0',
pendingUsdt: this.formatDecimal(summary?.pendingUsdt), pendingUsdt: summary?.pendingUsdt || '0',
pendingHashpower: this.formatDecimal(summary?.pendingHashpower), pendingHashpower: summary?.pendingHashpower || '0',
settleableUsdt: this.formatDecimal(summary?.settleableUsdt), settleableUsdt: summary?.settleableUsdt || '0',
settleableHashpower: this.formatDecimal(summary?.settleableHashpower), settleableHashpower: summary?.settleableHashpower || '0',
settledTotalUsdt: this.formatDecimal(summary?.settledTotalUsdt), settledTotalUsdt: summary?.settledTotalUsdt || '0',
settledTotalHashpower: this.formatDecimal(summary?.settledTotalHashpower), settledTotalHashpower: summary?.settledTotalHashpower || '0',
expiredTotalUsdt: this.formatDecimal(summary?.expiredTotalUsdt), expiredTotalUsdt: summary?.expiredTotalUsdt || '0',
expiredTotalHashpower: this.formatDecimal(summary?.expiredTotalHashpower), expiredTotalHashpower: summary?.expiredTotalHashpower || '0',
}, },
items: ledger.items.map((item) => ({ items: ledger.items.map((item) => ({
entryId: item.entryId.toString(), entryId: item.entryId.toString(),
entryType: item.entryType, entryType: item.entryType,
assetType: item.assetType, assetType: item.assetType,
amount: this.formatDecimal(item.amount), amount: item.amount,
balanceAfter: item.balanceAfter ? this.formatDecimal(item.balanceAfter) : null, balanceAfter: item.balanceAfter,
refOrderId: item.refOrderId, refOrderId: item.refOrderId,
refTxHash: item.refTxHash, refTxHash: item.refTxHash,
memo: item.memo, memo: item.memo,
@ -380,8 +380,8 @@ export class UserDetailController {
accountId: ledger.accountId.toString(), accountId: ledger.accountId.toString(),
accountType: ledger.accountType, accountType: ledger.accountType,
entryType: ledger.entryType, entryType: ledger.entryType,
amount: this.formatDecimal(ledger.amount), amount: ledger.amount,
balanceAfter: this.formatDecimal(ledger.balanceAfter), balanceAfter: ledger.balanceAfter,
sourceOrderId: ledger.sourceOrderId?.toString() || null, sourceOrderId: ledger.sourceOrderId?.toString() || null,
sourceRewardId: ledger.sourceRewardId?.toString() || null, sourceRewardId: ledger.sourceRewardId?.toString() || null,
txHash: ledger.txHash, txHash: ledger.txHash,
@ -408,10 +408,4 @@ export class UserDetailController {
} }
} }
private formatDecimal(value: bigint | null | undefined): string {
if (!value) return '0';
// bigint 已经乘以 1e8需要转回小数
const num = Number(value) / 1e8;
return num.toFixed(8).replace(/\.?0+$/, '');
}
} }

View File

@ -36,7 +36,7 @@ export interface ReferralNode {
export interface PlantingSummary { export interface PlantingSummary {
totalOrders: number; totalOrders: number;
totalTreeCount: number; totalTreeCount: number;
totalAmount: bigint; // Decimal as bigint for precision totalAmount: string; // 格式化后的金额字符串
effectiveTreeCount: number; effectiveTreeCount: number;
pendingTreeCount: number; pendingTreeCount: number;
firstPlantingAt: Date | null; firstPlantingAt: Date | null;
@ -47,7 +47,7 @@ export interface PlantingLedgerItem {
orderId: bigint; orderId: bigint;
orderNo: string; orderNo: string;
treeCount: number; treeCount: number;
totalAmount: bigint; totalAmount: string; // 格式化后的金额字符串
status: string; status: string;
selectedProvince: string | null; selectedProvince: string | null;
selectedCity: string | null; selectedCity: string | null;
@ -70,33 +70,33 @@ export interface PlantingLedgerResult {
// ============================================================================ // ============================================================================
export interface WalletSummary { export interface WalletSummary {
usdtAvailable: bigint; usdtAvailable: string;
usdtFrozen: bigint; usdtFrozen: string;
dstAvailable: bigint; dstAvailable: string;
dstFrozen: bigint; dstFrozen: string;
bnbAvailable: bigint; bnbAvailable: string;
bnbFrozen: bigint; bnbFrozen: string;
ogAvailable: bigint; ogAvailable: string;
ogFrozen: bigint; ogFrozen: string;
rwadAvailable: bigint; rwadAvailable: string;
rwadFrozen: bigint; rwadFrozen: string;
hashpower: bigint; hashpower: string;
pendingUsdt: bigint; pendingUsdt: string;
pendingHashpower: bigint; pendingHashpower: string;
settleableUsdt: bigint; settleableUsdt: string;
settleableHashpower: bigint; settleableHashpower: string;
settledTotalUsdt: bigint; settledTotalUsdt: string;
settledTotalHashpower: bigint; settledTotalHashpower: string;
expiredTotalUsdt: bigint; expiredTotalUsdt: string;
expiredTotalHashpower: bigint; expiredTotalHashpower: string;
} }
export interface WalletLedgerItem { export interface WalletLedgerItem {
entryId: bigint; entryId: bigint;
entryType: string; entryType: string;
assetType: string; assetType: string;
amount: bigint; amount: string; // 格式化后的金额字符串
balanceAfter: bigint | null; balanceAfter: string | null; // 格式化后的余额字符串
refOrderId: string | null; refOrderId: string | null;
refTxHash: string | null; refTxHash: string | null;
memo: string | null; memo: string | null;
@ -164,8 +164,8 @@ export interface SystemAccountLedger {
accountId: bigint; accountId: bigint;
accountType: string; accountType: string;
entryType: string; entryType: string;
amount: bigint; amount: string; // 格式化后的金额字符串
balanceAfter: bigint; balanceAfter: string; // 格式化后的余额字符串
sourceOrderId: bigint | null; sourceOrderId: bigint | null;
sourceRewardId: bigint | null; sourceRewardId: bigint | null;
txHash: string | null; txHash: string | null;

View File

@ -208,7 +208,7 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
return { return {
totalOrders: orderStats._count, totalOrders: orderStats._count,
totalTreeCount: orderStats._sum.treeCount || 0, totalTreeCount: orderStats._sum.treeCount || 0,
totalAmount: this.decimalToBigint(orderStats._sum.totalAmount), totalAmount: this.decimalToString(orderStats._sum.totalAmount),
effectiveTreeCount: position?.effectiveTreeCount || 0, effectiveTreeCount: position?.effectiveTreeCount || 0,
pendingTreeCount: position?.pendingTreeCount || 0, pendingTreeCount: position?.pendingTreeCount || 0,
firstPlantingAt: firstOrder?.paidAt || null, firstPlantingAt: firstOrder?.paidAt || null,
@ -246,7 +246,7 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
orderId: item.id, orderId: item.id,
orderNo: item.orderNo, orderNo: item.orderNo,
treeCount: item.treeCount, treeCount: item.treeCount,
totalAmount: this.decimalToBigint(item.totalAmount), totalAmount: this.decimalToString(item.totalAmount),
status: item.status, status: item.status,
selectedProvince: item.selectedProvince, selectedProvince: item.selectedProvince,
selectedCity: item.selectedCity, selectedCity: item.selectedCity,
@ -278,25 +278,25 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
if (!wallet) return null; if (!wallet) return null;
return { return {
usdtAvailable: this.decimalToBigint(wallet.usdtAvailable), usdtAvailable: this.decimalToString(wallet.usdtAvailable),
usdtFrozen: this.decimalToBigint(wallet.usdtFrozen), usdtFrozen: this.decimalToString(wallet.usdtFrozen),
dstAvailable: this.decimalToBigint(wallet.dstAvailable), dstAvailable: this.decimalToString(wallet.dstAvailable),
dstFrozen: this.decimalToBigint(wallet.dstFrozen), dstFrozen: this.decimalToString(wallet.dstFrozen),
bnbAvailable: this.decimalToBigint(wallet.bnbAvailable), bnbAvailable: this.decimalToString(wallet.bnbAvailable),
bnbFrozen: this.decimalToBigint(wallet.bnbFrozen), bnbFrozen: this.decimalToString(wallet.bnbFrozen),
ogAvailable: this.decimalToBigint(wallet.ogAvailable), ogAvailable: this.decimalToString(wallet.ogAvailable),
ogFrozen: this.decimalToBigint(wallet.ogFrozen), ogFrozen: this.decimalToString(wallet.ogFrozen),
rwadAvailable: this.decimalToBigint(wallet.rwadAvailable), rwadAvailable: this.decimalToString(wallet.rwadAvailable),
rwadFrozen: this.decimalToBigint(wallet.rwadFrozen), rwadFrozen: this.decimalToString(wallet.rwadFrozen),
hashpower: this.decimalToBigint(wallet.hashpower), hashpower: this.decimalToString(wallet.hashpower),
pendingUsdt: this.decimalToBigint(wallet.pendingUsdt), pendingUsdt: this.decimalToString(wallet.pendingUsdt),
pendingHashpower: this.decimalToBigint(wallet.pendingHashpower), pendingHashpower: this.decimalToString(wallet.pendingHashpower),
settleableUsdt: this.decimalToBigint(wallet.settleableUsdt), settleableUsdt: this.decimalToString(wallet.settleableUsdt),
settleableHashpower: this.decimalToBigint(wallet.settleableHashpower), settleableHashpower: this.decimalToString(wallet.settleableHashpower),
settledTotalUsdt: this.decimalToBigint(wallet.settledTotalUsdt), settledTotalUsdt: this.decimalToString(wallet.settledTotalUsdt),
settledTotalHashpower: this.decimalToBigint(wallet.settledTotalHashpower), settledTotalHashpower: this.decimalToString(wallet.settledTotalHashpower),
expiredTotalUsdt: this.decimalToBigint(wallet.expiredTotalUsdt), expiredTotalUsdt: this.decimalToString(wallet.expiredTotalUsdt),
expiredTotalHashpower: this.decimalToBigint(wallet.expiredTotalHashpower), expiredTotalHashpower: this.decimalToString(wallet.expiredTotalHashpower),
}; };
} }
@ -337,8 +337,8 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
entryId: item.id, entryId: item.id,
entryType: item.entryType, entryType: item.entryType,
assetType: item.assetType, assetType: item.assetType,
amount: this.decimalToBigint(item.amount), amount: this.decimalToString(item.amount),
balanceAfter: item.balanceAfter ? this.decimalToBigint(item.balanceAfter) : null, balanceAfter: item.balanceAfter ? this.decimalToString(item.balanceAfter) : null,
refOrderId: item.refOrderId, refOrderId: item.refOrderId,
refTxHash: item.refTxHash, refTxHash: item.refTxHash,
memo: item.memo, memo: item.memo,
@ -431,19 +431,13 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
// 辅助方法 // 辅助方法
// ============================================================================ // ============================================================================
private decimalToBigint(decimal: Decimal | null | undefined): bigint { /**
if (!decimal) return BigInt(0); * Decimal
// 转换为字符串后解析,保留精度 * 8
const str = decimal.toString(); */
// 移除小数点,按整数处理 private decimalToString(decimal: Decimal | null | undefined): string {
const parts = str.split('.'); if (!decimal) return '0';
if (parts.length === 1) { // 直接转字符串,去掉尾部多余的 0
return BigInt(parts[0]); return decimal.toFixed(8).replace(/\.?0+$/, '');
}
// 有小数部分,乘以 10^小数位数
const scale = parts[1].length;
const intPart = parts[0] + parts[1];
// 返回原始数值(不做缩放,保持 decimal 格式)
return BigInt(Math.round(parseFloat(str) * 1e8));
} }
} }