2026-07-03 14:43:49 +08:00

369 lines
14 KiB
JavaScript

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();
}