import AddOutlined from "@mui/icons-material/AddOutlined"; import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined"; import PaidOutlined from "@mui/icons-material/PaidOutlined"; import RefreshOutlined from "@mui/icons-material/RefreshOutlined"; import SettingsOutlined from "@mui/icons-material/SettingsOutlined"; import Button from "@mui/material/Button"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import { useEffect, useState } from "react"; import { AppIdentity } from "@/shared/ui/AppIdentity.jsx"; import { COIN_SELLER_RECHARGE_PROVIDERS, COIN_SELLER_RECHARGE_STATUS, DEFAULT_COIN_SELLER_RECHARGE_ORDER_FORM, } from "../constants.js"; import { buildCoinSellerRechargeReceiptPayload, buildCoinSellerRechargeOrderPayload, buildCoinSellerRechargeQuotePayload, coinSellerRechargeChainLabel, coinSellerRechargeGrantStatusLabel, coinSellerRechargeProviderLabel, coinSellerRechargeStatusLabel, coinSellerRechargeStatusTone, coinSellerRechargeVerifyStatusLabel, formatAmount, formatTime, formatUsdMinor, isCoinSellerRechargeGranted, isCoinSellerRechargeVerified, validateCoinSellerRechargeOrderPayload, } from "../format.js"; import { FinanceCoinSellerRechargeCreateDialog } from "./FinanceCoinSellerRechargeCreateDialog.jsx"; import { FinanceCoinSellerExchangeRateDialog } from "./FinanceCoinSellerExchangeRateDialog.jsx"; export function FinanceCoinSellerRechargeOrderList({ actionLoading, apps, canCreate, canConfigureExchangeRate, canGrant, canVerify, error, filters, loading, onCreate, onLoadExchangeRate, onFiltersChange, onGrant, onReload, onQuote, onSaveExchangeRate, onVerify, onVerifyReceipt, orders, }) { const [createOpen, setCreateOpen] = useState(false); const [form, setForm] = useState(DEFAULT_COIN_SELLER_RECHARGE_ORDER_FORM); const [formError, setFormError] = useState(""); const [quote, setQuote] = useState(null); const [quoteError, setQuoteError] = useState(""); const [quoteLoading, setQuoteLoading] = useState(false); const [rateConfigOpen, setRateConfigOpen] = useState(false); const [receiptVerification, setReceiptVerification] = useState(null); const [receiptLoading, setReceiptLoading] = useState(false); const items = orders.items || []; const page = Number(orders.page || filters.page || 1); const pageSize = Number(orders.pageSize || 50); const total = Number(orders.total || 0); const hasNextPage = page * pageSize < total; const quoteAppCode = form.appCode; const quoteTargetUserID = form.targetUserId; const quoteUSDAmount = form.usdAmount; const isMakeup = form.isMakeup; useEffect(() => { const payload = buildCoinSellerRechargeQuotePayload({ appCode: quoteAppCode, targetUserId: quoteTargetUserID, usdAmount: quoteUSDAmount, }); if (isMakeup) { setQuote(null); setQuoteError(""); setQuoteLoading(false); setForm((current) => (current.coinAmount === "0" ? current : { ...current, coinAmount: "0" })); return undefined; } if (!createOpen || !payload.appCode || !payload.targetUserId || !Number.isFinite(payload.usdAmount) || payload.usdAmount <= 0) { setQuote(null); setQuoteError(""); setQuoteLoading(false); return undefined; } let active = true; const timer = window.setTimeout(async () => { setQuoteLoading(true); setQuoteError(""); try { const result = await onQuote(payload); if (active) { setQuote(result); setForm((current) => ({ ...current, coinAmount: String(result?.coinAmount ?? "") })); } } catch (quoteRequestError) { if (active) { setQuote(null); setForm((current) => ({ ...current, coinAmount: "" })); setQuoteError(quoteRequestError.message || "未能生成充值金币"); } } finally { if (active) { setQuoteLoading(false); } } }, 300); return () => { active = false; window.clearTimeout(timer); }; }, [createOpen, isMakeup, onQuote, quoteAppCode, quoteTargetUserID, quoteUSDAmount]); const openCreate = () => { setForm({ ...DEFAULT_COIN_SELLER_RECHARGE_ORDER_FORM, appCode: filters.appCode || apps[0]?.appCode || "", orderDateMs: Date.now(), }); setFormError(""); setQuote(null); setQuoteError(""); setReceiptVerification(null); setCreateOpen(true); }; const updateForm = (key, value) => { if (key === "isMakeup") { setQuote(null); setQuoteError(""); setForm((current) => ({ ...current, coinAmount: value ? "0" : "", isMakeup: value })); setFormError(""); return; } if (["appCode", "targetUserId", "usdAmount"].includes(key)) { setQuote(null); setQuoteError(""); setForm((current) => ({ ...current, [key]: value, coinAmount: current.isMakeup ? "0" : "" })); setFormError(""); setReceiptVerification(null); return; } if (key === "providerCode") { setForm((current) => ({ ...current, chain: "", providerAmount: "", providerCode: value, providerCurrencyCode: "", })); setFormError(""); setReceiptVerification(null); return; } setForm((current) => ({ ...current, [key]: value })); setFormError(""); // 订单号校验结果绑定当前 APP、用户输入金额、渠道、链和订单号;任一字段变化都不能复用旧凭证。 setReceiptVerification(null); }; const submitCreate = async (event) => { event.preventDefault(); if (!receiptVerification?.verified) { setFormError("请先校验订单号"); return; } if (!form.isMakeup && !quote?.coinAmount) { setFormError(quoteError || "请等待系统生成充值金币"); return; } const payload = buildCoinSellerRechargeOrderPayload(form); const validationMessage = validateCoinSellerRechargeOrderPayload(payload); if (validationMessage) { setFormError(validationMessage); return; } const created = await onCreate(payload); if (created) { setCreateOpen(false); setReceiptVerification(null); } }; const verifyCreateReceipt = async () => { const payload = buildCoinSellerRechargeReceiptPayload(form); const validationMessage = validateCoinSellerRechargeOrderPayload({ ...buildCoinSellerRechargeOrderPayload(form), }); if (validationMessage) { setFormError(validationMessage); return; } setFormError(""); setReceiptLoading(true); try { const verification = await onVerifyReceipt(payload); setReceiptVerification(verification); if (!verification?.verified) { setFormError(verification?.failureReason || "订单号校验未通过"); } } finally { setReceiptLoading(false); } }; const confirmGrant = (order) => { const userText = order.targetDisplayUserId || order.targetUserId || "-"; const ok = window.confirm( isZeroCoinMakeupOrder(order) ? `确认完成用户 ${userText} 的补单?该订单计入充值,不发放金币。` : `确认给用户 ${userText} 发放 ${formatAmount(order.coinAmount)} 金币?`, ); if (ok) { onGrant(order); } }; return (
onFiltersChange({ appCode: event.target.value })} > 全部 APP {apps.map((app) => ( ))} onFiltersChange({ providerCode: event.target.value })} > 全部渠道 {COIN_SELLER_RECHARGE_PROVIDERS.map(([value, label]) => ( {label} ))} onFiltersChange({ status: event.target.value })} > 全部状态 {COIN_SELLER_RECHARGE_STATUS.map(([value, label]) => ( {label} ))} onFiltersChange({ keyword: event.target.value })} />
{canConfigureExchangeRate ? ( ) : null}
{error ?
{error}
: null} {loading && !items.length ?
: null} {!loading || items.length ? (
{items.length ? ( items.map((item) => ( )) ) : ( )}
订单 APP 目标用户 金额 渠道 三方订单 备注 状态 钱包 操作人 时间 操作
{item.id || "-"} app.appCode === item.appCode)} appCode={item.appCode} size="small" /> {item.remark || "-"} } secondary={`${coinSellerRechargeVerifyStatusLabel(item.verifyStatus)} / ${coinSellerRechargeGrantStatusLabel(item.grantStatus)}`} />
当前无数据
) : null} {total > pageSize ? (
第 {page} 页 / 共 {total} 条
) : null} { setCreateOpen(false); setReceiptVerification(null); setFormError(""); setQuote(null); setQuoteError(""); }} onSubmit={submitCreate} onVerifyReceipt={verifyCreateReceipt} /> setRateConfigOpen(false)} onLoad={onLoadExchangeRate} onSave={onSaveExchangeRate} />
); } function isZeroCoinMakeupOrder(order) { return Number(order?.coinAmount || 0) === 0; } function StatusBadge({ status }) { return ( {coinSellerRechargeStatusLabel(status)} ); } function Stacked({ primary, secondary }) { return ( {primary || "-"} {secondary || ""} ); } function usdText(item) { if (item.usdAmount !== undefined && item.usdAmount !== null && item.usdAmount !== "") { return `USD ${item.usdAmount}`; } return formatUsdMinor(item.usdMinorAmount, "USD"); } function providerAmountText(item) { return item.providerAmountMinor === null || item.providerAmountMinor === undefined ? "-" : formatUsdMinor(item.providerAmountMinor, item.providerCurrencyCode || "USD"); } function providerMeta(item) { if (item.providerCode === "usdt") { return [coinSellerRechargeChainLabel(item.chain), item.providerStatus].filter(Boolean).join(" / ") || "-"; } if (item.providerCode === "mifapay") { return [item.providerStatus, item.providerOrderId].filter(Boolean).join(" / ") || "-"; } if (item.providerCode === "v5pay") { return ( [item.providerStatus, item.providerCurrencyCode, item.providerOrderId].filter(Boolean).join(" / ") || "-" ); } return "-"; } function walletMeta(item) { return [ item.walletAssetType, item.walletAmountDelta === null || item.walletAmountDelta === undefined ? "" : `变动 ${formatAmount(item.walletAmountDelta)}`, item.walletBalanceAfter === null || item.walletBalanceAfter === undefined ? "" : `余额 ${formatAmount(item.walletBalanceAfter)}`, ] .filter(Boolean) .join(" / "); }