647 lines
26 KiB
JavaScript
647 lines
26 KiB
JavaScript
import {
|
||
COIN_SELLER_RECHARGE_CHAINS,
|
||
COIN_SELLER_RECHARGE_GRANT_STATUS,
|
||
COIN_SELLER_RECHARGE_PROVIDERS,
|
||
COIN_SELLER_RECHARGE_STATUS,
|
||
COIN_SELLER_RECHARGE_VERIFY_STATUS,
|
||
WITHDRAWAL_STATUS,
|
||
} from "./constants.js";
|
||
|
||
export function normalizeWithdrawalApplication(item = {}) {
|
||
return {
|
||
appCode: stringValue(item.appCode ?? item.app_code),
|
||
appName: stringValue(item.appName ?? item.app_name),
|
||
approvedAtMs: numberOrNull(item.approvedAtMs ?? item.approved_at_ms),
|
||
approverName: stringValue(
|
||
item.approverName ?? item.approver_name ?? item.approverAccount ?? item.approver_account,
|
||
),
|
||
approverUserId: item.approverUserId ?? item.approver_user_id,
|
||
auditImageUrl: stringValue(item.auditImageUrl ?? item.audit_image_url),
|
||
auditRemark: stringValue(item.auditRemark ?? item.audit_remark),
|
||
auditTransactionId: stringValue(item.auditTransactionId ?? item.audit_transaction_id),
|
||
createdAtMs: numberOrNull(item.createdAtMs ?? item.created_at_ms),
|
||
freezeTransactionId: stringValue(item.freezeTransactionId ?? item.freeze_transaction_id),
|
||
id: item.id ?? item.withdrawalApplicationId ?? item.withdrawal_application_id,
|
||
salaryAssetType: stringValue(item.salaryAssetType ?? item.salary_asset_type),
|
||
status: stringValue(item.status),
|
||
updatedAtMs: numberOrNull(item.updatedAtMs ?? item.updated_at_ms),
|
||
userId: stringValue(item.userId ?? item.user_id),
|
||
withdrawAddress: stringValue(item.withdrawAddress ?? item.withdraw_address),
|
||
withdrawAmount: numberValue(item.withdrawAmount ?? item.withdraw_amount),
|
||
withdrawAmountMinor: numberValue(item.withdrawAmountMinor ?? item.withdraw_amount_minor),
|
||
withdrawMethod: stringValue(item.withdrawMethod ?? item.withdraw_method),
|
||
};
|
||
}
|
||
|
||
export function normalizeWithdrawalApplicationPage(data = {}) {
|
||
const items = Array.isArray(data.items) ? data.items : [];
|
||
return {
|
||
items: items.map(normalizeWithdrawalApplication),
|
||
page: Number(data.page || 1),
|
||
pageSize: Number(data.pageSize ?? data.page_size ?? items.length),
|
||
total: Number(data.total ?? items.length),
|
||
};
|
||
}
|
||
|
||
export function buildCoinSellerRechargeOrderPayload(form) {
|
||
const providerCode = stringValue(form.providerCode);
|
||
const providerCurrencyCode = stringValue(form.providerCurrencyCode).toUpperCase();
|
||
const providerAmountMinor = providerAmountMinorFromForm(form);
|
||
const payload = {
|
||
appCode: stringValue(form.appCode),
|
||
chain: providerCode === "usdt" ? stringValue(form.chain).toUpperCase() : "",
|
||
externalOrderNo: stringValue(form.externalOrderNo),
|
||
isMakeup: Boolean(form.isMakeup),
|
||
orderDateMs: numberValue(form.orderDateMs),
|
||
providerAmountMinor: requiresThirdPartyProviderAmount(providerCode) ? providerAmountMinor : "",
|
||
providerCode,
|
||
providerCurrencyCode: requiresThirdPartyProviderAmount(providerCode) ? providerCurrencyCode : "",
|
||
remark: stringValue(form.remark),
|
||
targetUserId: stringValue(form.targetUserId),
|
||
usdAmount: numberValue(form.usdAmount),
|
||
};
|
||
|
||
return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== ""));
|
||
}
|
||
|
||
export function buildCoinSellerRechargeReceiptPayload(form) {
|
||
const providerCode = stringValue(form.providerCode);
|
||
const providerCurrencyCode = stringValue(form.providerCurrencyCode).toUpperCase();
|
||
const providerAmountMinor = providerAmountMinorFromForm(form);
|
||
return Object.fromEntries(
|
||
Object.entries({
|
||
appCode: stringValue(form.appCode),
|
||
chain: providerCode === "usdt" ? stringValue(form.chain).toUpperCase() : "",
|
||
externalOrderNo: stringValue(form.externalOrderNo),
|
||
orderDateMs: numberValue(form.orderDateMs),
|
||
providerAmountMinor: requiresThirdPartyProviderAmount(providerCode) ? providerAmountMinor : "",
|
||
providerCode,
|
||
providerCurrencyCode: requiresThirdPartyProviderAmount(providerCode) ? providerCurrencyCode : "",
|
||
usdAmount: numberValue(form.usdAmount),
|
||
}).filter(([, value]) => value !== ""),
|
||
);
|
||
}
|
||
|
||
export function validateCoinSellerRechargeOrderPayload(payload) {
|
||
if (!payload.appCode) {
|
||
return "请选择 APP";
|
||
}
|
||
if (!payload.targetUserId) {
|
||
return "请填写目标用户 ID";
|
||
}
|
||
if (!Number.isFinite(payload.usdAmount) || payload.usdAmount <= 0) {
|
||
return "请填写大于 0 的 USD 金额";
|
||
}
|
||
if (!Number.isInteger(payload.orderDateMs) || payload.orderDateMs <= 0) {
|
||
return "请选择订单时间";
|
||
}
|
||
if (!payload.providerCode) {
|
||
return "请选择支付渠道";
|
||
}
|
||
if (!payload.externalOrderNo) {
|
||
return "请填写三方订单号";
|
||
}
|
||
if (payload.providerCode === "usdt" && !payload.chain) {
|
||
return "请选择 USDT 链";
|
||
}
|
||
if (requiresThirdPartyProviderAmount(payload.providerCode)) {
|
||
if (!payload.providerCurrencyCode) {
|
||
return "请填写币种";
|
||
}
|
||
if (!Number.isInteger(payload.providerAmountMinor) || payload.providerAmountMinor <= 0) {
|
||
return "请填写大于 0 的币种金额";
|
||
}
|
||
}
|
||
if (payload.remark && payload.remark.length > 512) {
|
||
return "备注不能超过 512 字";
|
||
}
|
||
return "";
|
||
}
|
||
|
||
export function buildCoinSellerRechargeQuotePayload(form) {
|
||
return {
|
||
appCode: stringValue(form.appCode),
|
||
targetUserId: stringValue(form.targetUserId),
|
||
usdAmount: numberValue(form.usdAmount),
|
||
};
|
||
}
|
||
|
||
export function buildCoinSellerRechargeExchangeRatePayload(config) {
|
||
return {
|
||
tiers: (config.tiers || []).map((item) => ({
|
||
coinsPerUsd: numberValue(item.coinsPerUsd),
|
||
maxUsdAmount: numberValue(item.maxUsdAmount),
|
||
minUsdAmount: numberValue(item.minUsdAmount),
|
||
})),
|
||
whitelist: (config.whitelist || []).map((item) => ({
|
||
coinsPerUsd: numberValue(item.coinsPerUsd),
|
||
userId: stringValue(item.userId),
|
||
})),
|
||
};
|
||
}
|
||
|
||
export function validateCoinSellerRechargeExchangeRatePayload(payload) {
|
||
if (!payload.tiers.length) {
|
||
return "至少添加一个 USD 金额区间";
|
||
}
|
||
const tiers = [...payload.tiers].sort((left, right) => left.minUsdAmount - right.minUsdAmount);
|
||
for (let index = 0; index < tiers.length; index += 1) {
|
||
const item = tiers[index];
|
||
if (
|
||
!Number.isFinite(item.minUsdAmount) ||
|
||
item.minUsdAmount <= 0 ||
|
||
!Number.isFinite(item.maxUsdAmount) ||
|
||
item.maxUsdAmount < item.minUsdAmount ||
|
||
!Number.isInteger(item.coinsPerUsd) ||
|
||
item.coinsPerUsd <= 0
|
||
) {
|
||
return "请填写正确的 USD 金额区间和金币汇率";
|
||
}
|
||
if (index > 0 && item.minUsdAmount <= tiers[index - 1].maxUsdAmount) {
|
||
return "USD 金额区间不能重叠";
|
||
}
|
||
}
|
||
const userIds = new Set();
|
||
for (const item of payload.whitelist) {
|
||
if (!item.userId || !Number.isInteger(item.coinsPerUsd) || item.coinsPerUsd <= 0) {
|
||
return "请填写正确的白名单用户 ID 和金币汇率";
|
||
}
|
||
if (userIds.has(item.userId)) {
|
||
return `白名单用户 ID 重复:${item.userId}`;
|
||
}
|
||
userIds.add(item.userId);
|
||
}
|
||
return "";
|
||
}
|
||
|
||
export function normalizeCoinSellerRechargeOrder(item = {}) {
|
||
return {
|
||
appCode: stringValue(item.appCode ?? item.app_code),
|
||
chain: stringValue(item.chain),
|
||
coinAmount: numberValue(item.coinAmount ?? item.coin_amount),
|
||
createdAtMs: numberOrNull(item.createdAtMs ?? item.created_at_ms),
|
||
externalOrderNo: stringValue(item.externalOrderNo ?? item.external_order_no),
|
||
failureReason: stringValue(item.failureReason ?? item.failure_reason),
|
||
grantStatus: stringValue(item.grantStatus ?? item.grant_status) || "pending",
|
||
grantedAtMs: numberOrNull(item.grantedAtMs ?? item.granted_at_ms),
|
||
grantedByName: stringValue(item.grantedByName ?? item.granted_by_name),
|
||
id: stringValue(item.id ?? item.orderId ?? item.order_id),
|
||
operatorName: stringValue(item.operatorName ?? item.operator_name),
|
||
orderDateMs: numberOrNull(item.orderDateMs ?? item.order_date_ms),
|
||
providerAmountMinor: numberOrNull(item.providerAmountMinor ?? item.provider_amount_minor),
|
||
providerCode: stringValue(item.providerCode ?? item.provider_code),
|
||
providerCurrencyCode: stringValue(item.providerCurrencyCode ?? item.provider_currency_code),
|
||
providerOrderId: stringValue(item.providerOrderId ?? item.provider_order_id),
|
||
providerPayloadJson: stringValue(item.providerPayloadJson ?? item.provider_payload_json),
|
||
providerStatus: stringValue(item.providerStatus ?? item.provider_status),
|
||
receiveAddress: stringValue(item.receiveAddress ?? item.receive_address),
|
||
remark: stringValue(item.remark),
|
||
status: stringValue(item.status) || "pending",
|
||
targetDisplayUserId: stringValue(item.targetDisplayUserId ?? item.target_display_user_id),
|
||
targetUserId: stringValue(item.targetUserId ?? item.target_user_id),
|
||
usdAmount: item.usdAmount ?? item.usd_amount ?? "",
|
||
usdMinorAmount: numberOrNull(item.usdMinorAmount ?? item.usd_minor_amount),
|
||
verifiedAtMs: numberOrNull(item.verifiedAtMs ?? item.verified_at_ms),
|
||
verifiedByName: stringValue(item.verifiedByName ?? item.verified_by_name),
|
||
verifyStatus: stringValue(item.verifyStatus ?? item.verify_status) || "pending",
|
||
walletAmountDelta: numberOrNull(item.walletAmountDelta ?? item.wallet_amount_delta),
|
||
walletAssetType: stringValue(item.walletAssetType ?? item.wallet_asset_type),
|
||
walletBalanceAfter: numberOrNull(item.walletBalanceAfter ?? item.wallet_balance_after),
|
||
walletCommandId: stringValue(item.walletCommandId ?? item.wallet_command_id),
|
||
walletTransactionId: stringValue(item.walletTransactionId ?? item.wallet_transaction_id),
|
||
};
|
||
}
|
||
|
||
export function normalizeCoinSellerRechargeOrderPage(data = {}) {
|
||
const items = Array.isArray(data.items) ? data.items : [];
|
||
return {
|
||
items: items.map(normalizeCoinSellerRechargeOrder),
|
||
page: Number(data.page || 1),
|
||
pageSize: Number(data.pageSize ?? data.page_size ?? items.length),
|
||
total: Number(data.total ?? items.length),
|
||
};
|
||
}
|
||
|
||
export function normalizeRechargeBill(item = {}) {
|
||
return {
|
||
appCode: stringValue(item.appCode ?? item.app_code),
|
||
appName: stringValue(item.appName ?? item.app_name),
|
||
billAmountMinor: numberOrNull(item.billAmountMinor ?? item.bill_amount_minor),
|
||
billAmountText: stringValue(
|
||
item.billAmountText ?? item.bill_amount_text ?? item.billAmount ?? item.bill_amount,
|
||
),
|
||
coinAmount: numberValue(item.coinAmount ?? item.coin_amount),
|
||
commandId: stringValue(item.commandId ?? item.command_id),
|
||
createdAtMs: numberOrNull(item.createdAtMs ?? item.created_at_ms),
|
||
currencyCode: stringValue(item.currencyCode ?? item.currency_code) || "USD",
|
||
exchangeCoinAmount: numberValue(item.exchangeCoinAmount ?? item.exchange_coin_amount),
|
||
exchangeUsdMinorAmount: numberValue(item.exchangeUsdMinorAmount ?? item.exchange_usd_minor_amount),
|
||
externalRef: stringValue(item.externalRef ?? item.external_ref),
|
||
id: stringValue(item.id ?? item.transactionId ?? item.transaction_id),
|
||
paidSyncedAtMs: numberOrNull(item.paidSyncedAtMs ?? item.paid_synced_at_ms),
|
||
providerAmountMinor: numberOrNull(item.providerAmountMinor ?? item.provider_amount_minor),
|
||
providerCode: stringValue(item.providerCode ?? item.provider_code),
|
||
providerFeeMicro: numberOrNull(item.providerFeeMicro ?? item.provider_fee_micro),
|
||
providerNetMicro: numberOrNull(item.providerNetMicro ?? item.provider_net_micro),
|
||
providerTaxMicro: numberOrNull(item.providerTaxMicro ?? item.provider_tax_micro),
|
||
rechargeType: stringValue(item.rechargeType ?? item.recharge_type),
|
||
sellerRegionId: numberOrNull(item.sellerRegionId ?? item.seller_region_id),
|
||
sellerUserId: numberOrNull(item.sellerUserId ?? item.seller_user_id),
|
||
status: stringValue(item.status),
|
||
targetRegionId: numberOrNull(item.targetRegionId ?? item.target_region_id),
|
||
taxDeductionMinor: numberOrNull(
|
||
item.taxDeductionMinor ??
|
||
item.tax_deduction_minor ??
|
||
item.thirdPartyTaxDeductionMinor ??
|
||
item.third_party_tax_deduction_minor,
|
||
),
|
||
taxDeductionText: stringValue(
|
||
item.taxDeductionText ??
|
||
item.tax_deduction_text ??
|
||
item.thirdPartyTaxDeduction ??
|
||
item.third_party_tax_deduction,
|
||
),
|
||
taxRate: numberOrNull(item.taxRate ?? item.tax_rate ?? item.thirdPartyTaxRate ?? item.third_party_tax_rate),
|
||
transactionId: stringValue(item.transactionId ?? item.transaction_id),
|
||
usdMinorAmount: numberValue(item.usdMinorAmount ?? item.usd_minor_amount),
|
||
user: {
|
||
displayUserId: stringValue(item.user?.displayUserId ?? item.user?.display_user_id),
|
||
userId: stringValue(item.user?.userId ?? item.user?.user_id) || stringValue(item.userId ?? item.user_id),
|
||
username: stringValue(item.user?.username),
|
||
},
|
||
userPaidAmountMicro: numberOrNull(
|
||
item.userPaidAmountMicro ?? item.user_paid_amount_micro ?? item.paidAmountMicro ?? item.paid_amount_micro,
|
||
),
|
||
userPaidAmountMinor: numberOrNull(
|
||
item.userPaidAmountMinor ??
|
||
item.user_paid_amount_minor ??
|
||
item.paidAmountMinor ??
|
||
item.paid_amount_minor ??
|
||
item.providerAmountMinor ??
|
||
item.provider_amount_minor,
|
||
),
|
||
userPaidCurrencyCode: stringValue(item.userPaidCurrencyCode ?? item.user_paid_currency_code),
|
||
userPaidText: stringValue(
|
||
item.userPaidText ??
|
||
item.user_paid_text ??
|
||
item.userPaidAmount ??
|
||
item.user_paid_amount ??
|
||
item.paidAmount ??
|
||
item.paid_amount,
|
||
),
|
||
};
|
||
}
|
||
|
||
export function normalizeRechargeBillPage(data = {}) {
|
||
const rawItems = Array.isArray(data.items) ? data.items : Array.isArray(data.bills) ? data.bills : [];
|
||
return {
|
||
items: rawItems.map(normalizeRechargeBill),
|
||
page: Number(data.page || 1),
|
||
pageSize: Number(data.pageSize ?? data.page_size ?? rawItems.length),
|
||
total: Number(data.total ?? rawItems.length),
|
||
};
|
||
}
|
||
|
||
const EMPTY_RECHARGE_SUMMARY_BUCKET = { billCount: 0, coinAmount: 0, usdMinorAmount: 0 };
|
||
|
||
export const EMPTY_RECHARGE_SUMMARY = {
|
||
coinSeller: EMPTY_RECHARGE_SUMMARY_BUCKET,
|
||
googlePlay: EMPTY_RECHARGE_SUMMARY_BUCKET,
|
||
thirdParty: EMPTY_RECHARGE_SUMMARY_BUCKET,
|
||
total: EMPTY_RECHARGE_SUMMARY_BUCKET,
|
||
};
|
||
|
||
function normalizeRechargeSummaryBucket(bucket = {}) {
|
||
return {
|
||
billCount: numberValue(bucket.billCount ?? bucket.bill_count) || 0,
|
||
coinAmount: numberValue(bucket.coinAmount ?? bucket.coin_amount) || 0,
|
||
usdMinorAmount: numberValue(bucket.usdMinorAmount ?? bucket.usd_minor_amount) || 0,
|
||
};
|
||
}
|
||
|
||
export function normalizeRechargeSummary(data = {}) {
|
||
return {
|
||
coinSeller: normalizeRechargeSummaryBucket(data.coinSeller ?? data.coin_seller),
|
||
googlePlay: normalizeRechargeSummaryBucket(data.googlePlay ?? data.google_play),
|
||
thirdParty: normalizeRechargeSummaryBucket(data.thirdParty ?? data.third_party),
|
||
total: normalizeRechargeSummaryBucket(data.total),
|
||
};
|
||
}
|
||
|
||
export function mergeRechargeSummaries(summaries = []) {
|
||
const merged = normalizeRechargeSummary({});
|
||
for (const summary of summaries) {
|
||
for (const key of ["coinSeller", "googlePlay", "thirdParty", "total"]) {
|
||
merged[key] = {
|
||
billCount: merged[key].billCount + summary[key].billCount,
|
||
coinAmount: merged[key].coinAmount + summary[key].coinAmount,
|
||
usdMinorAmount: merged[key].usdMinorAmount + summary[key].usdMinorAmount,
|
||
};
|
||
}
|
||
}
|
||
return merged;
|
||
}
|
||
|
||
export const EMPTY_RECHARGE_OVERVIEW = {
|
||
daily: [],
|
||
googlePaid: {
|
||
coveredUsdMinor: 0,
|
||
estFeeUsdMinor: 0,
|
||
estNetUsdMinor: 0,
|
||
estTaxUsdMinor: 0,
|
||
googleBillCount: 0,
|
||
syncedCount: 0,
|
||
unsyncedCount: 0,
|
||
},
|
||
regions: [],
|
||
withdrawal: {
|
||
approvedCount: 0,
|
||
approvedUsdMinor: 0,
|
||
totalUsdMinor: 0,
|
||
transferCount: 0,
|
||
transferUsdMinor: 0,
|
||
},
|
||
};
|
||
|
||
export function normalizeRechargeOverview(data = {}) {
|
||
const daily = Array.isArray(data.daily) ? data.daily : [];
|
||
const regions = Array.isArray(data.regions) ? data.regions : [];
|
||
const paid = data.googlePaid ?? data.google_paid ?? {};
|
||
const withdrawal = data.withdrawal ?? {};
|
||
return {
|
||
daily: daily.map((item) => ({
|
||
coinSellerCoinAmount: numberValue(item.coinSellerCoinAmount ?? item.coin_seller_coin_amount) || 0,
|
||
coinSellerUsdMinor: numberValue(item.coinSellerUsdMinor ?? item.coin_seller_usd_minor) || 0,
|
||
date: stringValue(item.date),
|
||
googleCoinAmount: numberValue(item.googleCoinAmount ?? item.google_coin_amount) || 0,
|
||
googleUsdMinor: numberValue(item.googleUsdMinor ?? item.google_usd_minor) || 0,
|
||
thirdPartyCoinAmount: numberValue(item.thirdPartyCoinAmount ?? item.third_party_coin_amount) || 0,
|
||
thirdPartyUsdMinor: numberValue(item.thirdPartyUsdMinor ?? item.third_party_usd_minor) || 0,
|
||
})),
|
||
googlePaid: {
|
||
coveredUsdMinor: numberValue(paid.coveredUsdMinor ?? paid.covered_usd_minor) || 0,
|
||
estFeeUsdMinor: numberValue(paid.estFeeUsdMinor ?? paid.est_fee_usd_minor) || 0,
|
||
estNetUsdMinor: numberValue(paid.estNetUsdMinor ?? paid.est_net_usd_minor) || 0,
|
||
estTaxUsdMinor: numberValue(paid.estTaxUsdMinor ?? paid.est_tax_usd_minor) || 0,
|
||
googleBillCount: numberValue(paid.googleBillCount ?? paid.google_bill_count) || 0,
|
||
syncedCount: numberValue(paid.syncedCount ?? paid.synced_count) || 0,
|
||
unsyncedCount: numberValue(paid.unsyncedCount ?? paid.unsynced_count) || 0,
|
||
},
|
||
regions: regions.map((item) => ({
|
||
billCount: numberValue(item.billCount ?? item.bill_count) || 0,
|
||
name: stringValue(item.name),
|
||
regionId: numberOrNull(item.regionId ?? item.region_id),
|
||
usdMinorAmount: numberValue(item.usdMinorAmount ?? item.usd_minor_amount) || 0,
|
||
})),
|
||
withdrawal: {
|
||
approvedCount: numberValue(withdrawal.approvedCount ?? withdrawal.approved_count) || 0,
|
||
approvedUsdMinor: numberValue(withdrawal.approvedUsdMinor ?? withdrawal.approved_usd_minor) || 0,
|
||
totalUsdMinor: numberValue(withdrawal.totalUsdMinor ?? withdrawal.total_usd_minor) || 0,
|
||
transferCount: numberValue(withdrawal.transferCount ?? withdrawal.transfer_count) || 0,
|
||
transferUsdMinor: numberValue(withdrawal.transferUsdMinor ?? withdrawal.transfer_usd_minor) || 0,
|
||
},
|
||
};
|
||
}
|
||
|
||
// mergeRechargeOverviews 把多 App 概览合并成全局口径:daily 按日期求和,googlePaid 求和;
|
||
// 区域 ID 属于单 App 语义,跨 App 合并时丢弃区域分布。
|
||
export function mergeRechargeOverviews(overviews = []) {
|
||
const dailyByDate = new Map();
|
||
const merged = normalizeRechargeOverview({});
|
||
for (const overview of overviews) {
|
||
for (const bucket of overview.daily) {
|
||
const existing = dailyByDate.get(bucket.date);
|
||
if (!existing) {
|
||
dailyByDate.set(bucket.date, { ...bucket });
|
||
continue;
|
||
}
|
||
for (const key of [
|
||
"coinSellerCoinAmount",
|
||
"coinSellerUsdMinor",
|
||
"googleCoinAmount",
|
||
"googleUsdMinor",
|
||
"thirdPartyCoinAmount",
|
||
"thirdPartyUsdMinor",
|
||
]) {
|
||
existing[key] += bucket[key];
|
||
}
|
||
}
|
||
for (const key of Object.keys(merged.googlePaid)) {
|
||
merged.googlePaid[key] += overview.googlePaid[key];
|
||
}
|
||
for (const key of Object.keys(merged.withdrawal)) {
|
||
merged.withdrawal[key] += overview.withdrawal[key];
|
||
}
|
||
}
|
||
merged.daily = [...dailyByDate.values()].sort((left, right) => left.date.localeCompare(right.date));
|
||
return merged;
|
||
}
|
||
|
||
export function normalizeRechargeRegions(data = {}) {
|
||
const items = Array.isArray(data.items) ? data.items : Array.isArray(data) ? data : [];
|
||
return items
|
||
.map((item) => {
|
||
const regionId = numberOrNull(item.regionId ?? item.region_id);
|
||
if (!regionId) {
|
||
return null;
|
||
}
|
||
return {
|
||
name: stringValue(item.name),
|
||
regionCode: stringValue(item.regionCode ?? item.region_code),
|
||
regionId,
|
||
};
|
||
})
|
||
.filter(Boolean);
|
||
}
|
||
|
||
export function rechargeTypeLabel(value) {
|
||
if (value === "coin_seller_transfer") {
|
||
return "币商充值";
|
||
}
|
||
if (value === "coin_seller_stock_purchase") {
|
||
return "币商进货";
|
||
}
|
||
if (value === "coin_seller_stock_deduction") {
|
||
return "币商冲回";
|
||
}
|
||
if (value === "google_play_recharge") {
|
||
return "谷歌充值";
|
||
}
|
||
if (value === "mifapay") {
|
||
return "三方充值 · MiFaPay";
|
||
}
|
||
if (value === "v5pay") {
|
||
return "三方充值 · V5Pay";
|
||
}
|
||
if (value === "usdt_trc20") {
|
||
return "三方充值 · USDT";
|
||
}
|
||
if (value === "apple_recharge") {
|
||
return "苹果充值";
|
||
}
|
||
if (value === "huawei_recharge") {
|
||
return "华为充值";
|
||
}
|
||
if (value === "telegram_recharge") {
|
||
return "Telegram 充值";
|
||
}
|
||
if (value === "web_recharge") {
|
||
return "三方充值 · Web";
|
||
}
|
||
return value || "-";
|
||
}
|
||
|
||
// rechargeSourceTone 决定来源徽章与 KPI 卡片的配色分组:google/thirdparty/coinseller。
|
||
export function rechargeSourceTone(rechargeType) {
|
||
if (rechargeType === "google_play_recharge") {
|
||
return "google";
|
||
}
|
||
if (
|
||
rechargeType === "coin_seller_stock_purchase" ||
|
||
rechargeType === "coin_seller_stock_deduction" ||
|
||
rechargeType === "coin_seller_transfer"
|
||
) {
|
||
return "coinseller";
|
||
}
|
||
return "thirdparty";
|
||
}
|
||
|
||
export function statusLabel(value) {
|
||
return WITHDRAWAL_STATUS.find(([status]) => status === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeProviderLabel(value) {
|
||
return COIN_SELLER_RECHARGE_PROVIDERS.find(([code]) => code === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeChainLabel(value) {
|
||
return COIN_SELLER_RECHARGE_CHAINS.find(([code]) => code === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeStatusLabel(value) {
|
||
return COIN_SELLER_RECHARGE_STATUS.find(([status]) => status === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeVerifyStatusLabel(value) {
|
||
return COIN_SELLER_RECHARGE_VERIFY_STATUS.find(([status]) => status === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeGrantStatusLabel(value) {
|
||
return COIN_SELLER_RECHARGE_GRANT_STATUS.find(([status]) => status === value)?.[1] || value || "-";
|
||
}
|
||
|
||
export function coinSellerRechargeStatusTone(value) {
|
||
if (["granted", "verified", "success", "succeeded", "completed", "passed"].includes(stringValue(value))) {
|
||
return "success";
|
||
}
|
||
if (["failed", "rejected", "canceled", "cancelled"].includes(stringValue(value))) {
|
||
return "danger";
|
||
}
|
||
return "warning";
|
||
}
|
||
|
||
export function isCoinSellerRechargeVerified(order) {
|
||
return (
|
||
["verified", "success", "succeeded", "completed", "passed"].includes(stringValue(order?.verifyStatus)) ||
|
||
stringValue(order?.status) === "verified"
|
||
);
|
||
}
|
||
|
||
export function isCoinSellerRechargeGranted(order) {
|
||
return (
|
||
["granted", "success", "succeeded", "completed"].includes(stringValue(order?.grantStatus)) ||
|
||
stringValue(order?.status) === "granted"
|
||
);
|
||
}
|
||
|
||
export function formatAmount(value) {
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) {
|
||
return "-";
|
||
}
|
||
return number.toLocaleString("zh-CN", { maximumFractionDigits: 2 });
|
||
}
|
||
|
||
export function formatTime(ms) {
|
||
const value = Number(ms);
|
||
if (!Number.isFinite(value) || value <= 0) {
|
||
return "-";
|
||
}
|
||
return new Intl.DateTimeFormat("zh-CN", {
|
||
day: "2-digit",
|
||
hour: "2-digit",
|
||
hourCycle: "h23",
|
||
minute: "2-digit",
|
||
month: "2-digit",
|
||
timeZone: "Asia/Shanghai",
|
||
year: "numeric",
|
||
}).format(value);
|
||
}
|
||
|
||
export function formatUsdMinor(value, currency = "USD") {
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) {
|
||
return "-";
|
||
}
|
||
return `${currency || "USD"} ${(number / 100).toLocaleString("zh-CN", {
|
||
maximumFractionDigits: 2,
|
||
minimumFractionDigits: 2,
|
||
})}`;
|
||
}
|
||
|
||
export function formatMicroMoney(value, currency = "USD") {
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) {
|
||
return "-";
|
||
}
|
||
return `${currency || "USD"} ${(number / 1_000_000).toLocaleString("zh-CN", {
|
||
maximumFractionDigits: 6,
|
||
minimumFractionDigits: 2,
|
||
})}`;
|
||
}
|
||
|
||
export function formatPercent(value) {
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) {
|
||
return "";
|
||
}
|
||
const percent = number > 1 ? number : number * 100;
|
||
return `${percent.toLocaleString("zh-CN", { maximumFractionDigits: 4 })}%`;
|
||
}
|
||
|
||
function numberValue(value) {
|
||
if (value === "" || value === null || value === undefined) {
|
||
return Number.NaN;
|
||
}
|
||
return Number(value);
|
||
}
|
||
|
||
function numberOrNull(value) {
|
||
const nextValue = Number(value);
|
||
return Number.isFinite(nextValue) ? nextValue : null;
|
||
}
|
||
|
||
function providerMoneyToMinor(value) {
|
||
const number = numberValue(value);
|
||
if (!Number.isFinite(number)) {
|
||
return Number.NaN;
|
||
}
|
||
return Math.round(number * 100);
|
||
}
|
||
|
||
function providerAmountMinorFromForm(form) {
|
||
// 表单输入是常规金额,已构建 payload 重用时则直接沿用 minor,避免校验请求二次放大金额。
|
||
if (stringValue(form.providerAmount) !== "") {
|
||
return providerMoneyToMinor(form.providerAmount);
|
||
}
|
||
return numberValue(form.providerAmountMinor);
|
||
}
|
||
|
||
function requiresThirdPartyProviderAmount(providerCode) {
|
||
return ["mifapay", "v5pay"].includes(stringValue(providerCode).toLowerCase());
|
||
}
|
||
|
||
function stringValue(value) {
|
||
return String(value ?? "").trim();
|
||
}
|