import { APPLICATION_STATUS, FINANCE_OPERATIONS, WALLET_IDENTITIES, operationAllowsZeroRechargeAmount, } from "./constants.js"; export function buildApplicationPayload(form) { const operation = stringValue(form.operation); const payload = { appCode: stringValue(form.appCode), coinAmount: numberValue(form.coinAmount), credentialImageUrl: stringValue(form.credentialImageUrl), credentialText: stringValue(form.credentialText), operation, rechargeAmount: numberValue(form.rechargeAmount), targetUserId: stringValue(form.targetUserId), walletIdentity: requiresWalletIdentity(operation) ? stringValue(form.walletIdentity) : "", }; return payload; } export function validateApplicationPayload(payload) { if (!payload.appCode) { return "请选择 APP"; } if (!payload.operation) { return "请选择操作内容"; } if (!payload.targetUserId) { return "请填写目标用户 ID"; } if (!Number.isFinite(payload.coinAmount) || payload.coinAmount <= 0) { return "请填写大于 0 的金币数量"; } const allowsZeroRechargeAmount = operationAllowsZeroRechargeAmount(payload.operation); if ( !Number.isFinite(payload.rechargeAmount) || payload.rechargeAmount < 0 || (!allowsZeroRechargeAmount && payload.rechargeAmount <= 0) ) { return allowsZeroRechargeAmount ? "请填写不小于 0 的充值金额" : "请填写大于 0 的充值金额"; } if (requiresWalletIdentity(payload.operation) && !payload.walletIdentity) { return "请选择钱包身份"; } if (!payload.credentialImageUrl && !payload.credentialText) { return "请上传凭证图片或填写凭证文字"; } return ""; } export function normalizeApplication(item = {}) { return { appCode: stringValue(item.appCode ?? item.app_code), appName: stringValue(item.appName ?? item.app_name), applicantName: stringValue( item.applicantName ?? item.applicant_name ?? item.applicantAccount ?? item.applicant_account, ), applicantUserId: item.applicantUserId ?? item.applicant_user_id, auditedAtMs: numberOrNull(item.auditedAtMs ?? item.audited_at_ms), auditorName: stringValue(item.auditorName ?? item.auditor_name ?? item.auditorAccount ?? item.auditor_account), auditorUserId: item.auditorUserId ?? item.auditor_user_id, auditRemark: stringValue(item.auditRemark ?? item.audit_remark), coinAmount: numberValue(item.coinAmount ?? item.coin_amount), createdAtMs: numberOrNull(item.createdAtMs ?? item.created_at_ms), credentialImageUrl: stringValue(item.credentialImageUrl ?? item.credential_image_url), credentialText: stringValue(item.credentialText ?? item.credential_text), id: item.id ?? item.applicationId ?? item.application_id, operation: stringValue(item.operation), rechargeAmount: numberValue(item.rechargeAmount ?? item.recharge_amount), status: stringValue(item.status) || "pending", targetUserId: stringValue(item.targetUserId ?? item.target_user_id), walletIdentity: stringValue(item.walletIdentity ?? item.wallet_identity), }; } export function normalizeApplicationPage(data = {}) { const items = Array.isArray(data.items) ? data.items : []; return { items: items.map(normalizeApplication), page: Number(data.page || 1), pageSize: Number(data.pageSize ?? data.page_size ?? items.length), total: Number(data.total ?? items.length), }; } 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 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), status: stringValue(item.status), 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), 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 = { 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 { 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 ["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 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 operationLabel(value, operations = FINANCE_OPERATIONS) { return operations.find((item) => item.value === value)?.label || value || "-"; } export function rechargeTypeLabel(value) { if (value === "coin_seller_transfer") { 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 || "-"; } export function statusLabel(value) { return APPLICATION_STATUS.find(([status]) => status === value)?.[1] || value || "-"; } export function walletIdentityLabel(value) { return WALLET_IDENTITIES.find(([identity]) => identity === value)?.[1] || value || "-"; } 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 })}%`; } export function requiresWalletIdentity(operation) { return FINANCE_OPERATIONS.some((item) => item.value === operation && item.requiresWalletIdentity); } 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 stringValue(value) { return String(value ?? "").trim(); }