"use client"; import { useEffect, useState } from "react"; import dynamic from "next/dynamic"; import { RefreshCcw, CheckCircle2, ExternalLink, AlertTriangle, ShieldCheck } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { opsApi } from "@/lib/ops-api"; import type { BillingRiskIssue, BillingRiskPayload, PaymentRuntimePayload, PaymentIncident, PaymentRecord, } from "@/types/ops"; const PaymentIncidentPieChart = dynamic( () => import("./PaymentIncidentPieChart").then((mod) => mod.PaymentIncidentPieChart), { ssr: false, loading: () => 加载图表..., }, ); function paymentExplorerUrl(payment: PaymentRecord): string { const txHash = String(payment.tx_hash || "").trim(); if (!txHash) return ""; const chain = String(payment.chain || "").trim().toLowerCase(); const base = chain.includes("eth") ? "https://etherscan.io" : "https://polygonscan.com"; return `${base}/tx/${txHash}`; } function severityTone(severity?: string) { if (severity === "high") return "border-red-200 bg-red-50 text-red-700"; if (severity === "medium") return "border-amber-200 bg-amber-50 text-amber-700"; return "border-slate-200 bg-slate-50 text-slate-600"; } function severityLabel(severity?: string) { if (severity === "high") return "高"; if (severity === "medium") return "中"; if (severity === "low") return "低"; return severity || "未知"; } function compactDate(value?: string) { if (!value) return "—"; return value.slice(0, 19).replace("T", " "); } function paymentReasonLabel(reason?: string) { const key = String(reason || "").trim().toLowerCase(); if (key === "receiver_mismatch") return "收款地址不匹配"; if (key === "amount_mismatch") return "金额不匹配"; if (key === "chain_mismatch") return "支付网络不匹配"; if (key === "tx_not_found") return "链上交易未找到"; if (key === "tx_reverted") return "链上交易失败"; if (key === "expired") return "订单已过期"; if (key === "unknown") return "未知原因"; return key || "未知原因"; } function compactMono(value?: string, head = 10, tail = 6) { const text = String(value || "").trim(); if (!text) return "—"; if (text.length <= head + tail + 3) return text; return `${text.slice(0, head)}...${text.slice(-tail)}`; } function RiskStat({ label, value, sub, tone = "text-slate-950", }: { label: string; value: number; sub: string; tone?: string; }) { return (
| 级别 | 类型 | 问题 | 用户 | 时间 | 引用 |
|---|---|---|---|---|---|
| {severityLabel(issue.severity)} | {issue.category ?? "—"} |
{issue.title ?? "—"}
{issue.detail ?? "—"}
|
{issue.user_id ? `${issue.user_id.slice(0, 10)}...` : "—"} | {compactDate(issue.created_at)} | {issue.reference ? String(issue.reference).slice(0, 14) : "—"} |
{runtime.receiver_contract}
| ID | 邀请人 | 被邀请人 | 奖励 | Intent | 时间 |
|---|---|---|---|---|---|
| {String(row.id ?? "—")} | {String(row.referrer_user_id ?? "—").slice(0, 10)}... | {String(row.referred_user_id ?? "—").slice(0, 10)}... | +{Number(row.reward_points ?? 0).toLocaleString()} 分 | {String(row.payment_intent_id ?? "—").slice(0, 14)} | {compactDate(String(row.created_at ?? ""))} |
| ID | 原因 / 详情 | 用户 / Intent | 时间 | 操作 |
|---|---|---|---|---|
| {inc.id} |
{paymentReasonLabel(inc.reason)}
{inc.detail || inc.reason || "—"}
|
{compactMono(inc.user_id)}
{compactMono(inc.intent_id, 12, 6)}
|
{inc.created_at?.slice(0, 19) ?? "—"} |
| ID | 用户 | 金额 | 链 | Tx Hash | 时间 |
|---|---|---|---|---|---|
| {p.id} | {p.user_id?.slice(0, 10) ?? "—"}... | {p.amount} {p.currency} | {p.chain ?? "—"} |
{p.tx_hash ? (
{p.tx_hash.slice(0, 8)}...{p.tx_hash.slice(-6)}
|
{p.created_at?.slice(0, 19) ?? "—"} |