优化退款

This commit is contained in:
yml2213
2026-07-10 09:53:42 +08:00
parent 3642ad8aec
commit 763f5034fd
4 changed files with 342 additions and 62 deletions
@@ -32,6 +32,7 @@ import {
checkAdminKuaishouIndustryVoucherAvailable,
consumeAdminKuaishouIndustryVoucher,
disagreeAdminKuaishouIndustryRefund,
fetchAdminKuaishouIndustrySourceConfig,
fetchAdminKuaishouIndustryVouchers,
listAdminKuaishouIndustryRefunds,
resendAdminKuaishouIndustryVoucherCode,
@@ -42,6 +43,7 @@ import type {
AdminKuaishouIndustryRefundApprovePayload,
AdminKuaishouIndustryRefundDisagreePayload,
AdminKuaishouIndustryRefundListPayload,
AdminKuaishouIndustryShopConfig,
AdminKuaishouIndustryVoucher,
AdminKuaishouIndustryVoucherToolPayload,
} from '@/types/admin'
@@ -124,6 +126,17 @@ export default function AdminKuaishouIndustryPage() {
const [lastResultTitle, setLastResultTitle] = useState('接口结果')
const [lastResult, setLastResult] = useState<unknown>(null)
const [refundRows, setRefundRows] = useState<Array<Record<string, unknown>>>([])
const [industryShops, setIndustryShops] = useState<AdminKuaishouIndustryShopConfig[]>([])
const refundShopOptions = useMemo(
() =>
industryShops
.filter((shop) => shop.enabled !== false && shop.sellerId)
.map((shop) => ({
label: formatShopOptionLabel(shop),
value: shop.sellerId,
})),
[industryShops],
)
const columns = useMemo<TableColumnsType<AdminKuaishouIndustryVoucher>>(
() => [
@@ -206,6 +219,7 @@ export default function AdminKuaishouIndustryPage() {
() => [
{ title: '售后单', dataIndex: 'refundId', minWidth: 160 },
{ title: '订单', dataIndex: 'oid', minWidth: 160 },
{ title: '卖家', dataIndex: 'sellerId', minWidth: 140 },
{ title: '状态', dataIndex: 'status', width: 100 },
{ title: '协商', dataIndex: 'negotiateStatus', width: 100 },
{ title: '金额', dataIndex: 'refundFee', width: 100 },
@@ -230,8 +244,26 @@ export default function AdminKuaishouIndustryPage() {
useEffect(() => {
void loadVouchers()
void loadIndustryShops()
}, [])
async function loadIndustryShops() {
try {
const response = await fetchAdminKuaishouIndustrySourceConfig()
const shops = response.data.source.shops || []
setIndustryShops(shops)
const enabledSellerIds = shops
.filter((shop) => shop.enabled !== false && shop.sellerId)
.map((shop) => shop.sellerId)
if (enabledSellerIds.length === 1) {
applyDefaultSellerId(enabledSellerIds[0] as string)
}
} catch (error) {
showError(error instanceof Error ? error.message : '读取快手授权店铺失败')
}
}
async function loadVouchers(nextFilters: Partial<VoucherFilterState> = {}) {
const filters = { ...voucherFilters, ...nextFilters }
setVoucherFilters(filters)
@@ -323,12 +355,22 @@ export default function AdminKuaishouIndustryPage() {
async function runRefundList() {
const [begin, end] = refundDateRange || []
await runOpenApiAction('售后单列表', 'refund-list', () =>
listAdminKuaishouIndustryRefunds({
...refundListForm,
beginTime: begin?.valueOf(),
endTime: end?.valueOf(),
} satisfies AdminKuaishouIndustryRefundListPayload),
const sellerId = resolveRefundSellerId(refundListForm.sellerId)
if (!sellerId && refundShopOptions.length > 1) {
showError('请先选择快手售后退款店铺 sellerId')
return
}
await runOpenApiAction(
'售后单列表',
'refund-list',
() =>
listAdminKuaishouIndustryRefunds({
...refundListForm,
sellerId,
beginTime: begin?.valueOf(),
endTime: end?.valueOf(),
} satisfies AdminKuaishouIndustryRefundListPayload),
(result) => {
const rows = extractRefundRows(result.response)
setRefundRows(rows)
@@ -337,19 +379,33 @@ export default function AdminKuaishouIndustryPage() {
}
async function runApproveRefund() {
const sellerId = resolveRefundSellerId(approveForm.sellerId)
if (!sellerId && refundShopOptions.length > 1) {
showError('请先选择快手售后退款店铺 sellerId')
return
}
await runOpenApiAction('同意退款', 'refund-approve', () =>
approveAdminKuaishouIndustryRefund({
...approveForm,
sellerId: approveForm.sellerId || refundListForm.sellerId,
sellerId,
orderId: approveForm.orderId || refundListForm.orderId,
}),
)
}
async function runDisagreeRefund() {
const sellerId = resolveRefundSellerId(disagreeForm.sellerId)
if (!sellerId && refundShopOptions.length > 1) {
showError('请先选择快手售后退款店铺 sellerId')
return
}
await runOpenApiAction('不同意退款', 'refund-disagree', () =>
disagreeAdminKuaishouIndustryRefund({
...disagreeForm,
sellerId: disagreeForm.sellerId || refundListForm.sellerId,
sellerId,
orderId: disagreeForm.orderId || refundListForm.orderId,
}),
)
}
@@ -389,21 +445,33 @@ export default function AdminKuaishouIndustryPage() {
function fillRefundAction(row: Record<string, unknown>) {
const refundId = String(row.refundId || '').trim()
const sellerId = String(row.sellerId || refundListForm.sellerId || '').trim()
const orderId = String(row.oid || row.orderId || refundListForm.orderId || '').trim()
const status = String(row.status || '').trim()
const negotiateStatus = String(row.negotiateStatus || '').trim()
setApproveForm((current) => ({
...current,
sellerId: sellerId || current.sellerId,
orderId: orderId || current.orderId,
refundId,
status,
negotiateStatus,
refundAmount: String(row.refundFee || current.refundAmount || ''),
refundHandingWay: String(row.handlingWay || current.refundHandingWay || ''),
}))
setDisagreeForm((current) => ({
...current,
sellerId: sellerId || current.sellerId,
orderId: orderId || current.orderId,
refundId,
status,
negotiateStatus: negotiateStatus || current.negotiateStatus,
}))
setRefundListForm((current) => ({
...current,
sellerId: sellerId || current.sellerId,
orderId: orderId || current.orderId,
}))
showSuccess('已填入退款操作区')
}
@@ -444,14 +512,22 @@ export default function AdminKuaishouIndustryPage() {
allowClear
placeholder="订单号 oid"
value={voucherFilters.oid}
onChange={(event) => setVoucherFilters({ ...voucherFilters, oid: event.target.value })}
onChange={(event) =>
setVoucherFilters({
...voucherFilters,
oid: event.target.value,
})
}
/>
<Input
allowClear
placeholder="券码"
value={voucherFilters.voucherCode}
onChange={(event) =>
setVoucherFilters({ ...voucherFilters, voucherCode: event.target.value })
setVoucherFilters({
...voucherFilters,
voucherCode: event.target.value,
})
}
/>
<Input
@@ -459,7 +535,10 @@ export default function AdminKuaishouIndustryPage() {
placeholder="任务 ID"
value={voucherFilters.taskId}
onChange={(event) =>
setVoucherFilters({ ...voucherFilters, taskId: event.target.value })
setVoucherFilters({
...voucherFilters,
taskId: event.target.value,
})
}
/>
<Input
@@ -467,7 +546,10 @@ export default function AdminKuaishouIndustryPage() {
placeholder="卖家 ID"
value={voucherFilters.sellerId}
onChange={(event) =>
setVoucherFilters({ ...voucherFilters, sellerId: event.target.value })
setVoucherFilters({
...voucherFilters,
sellerId: event.target.value,
})
}
/>
<Select
@@ -522,7 +604,10 @@ export default function AdminKuaishouIndustryPage() {
placeholder="订单号 oid"
value={toolForm.oid}
onChange={(event) =>
updateToolForm({ oid: event.target.value, orderId: event.target.value })
updateToolForm({
oid: event.target.value,
orderId: event.target.value,
})
}
/>
<Input
@@ -609,11 +694,9 @@ export default function AdminKuaishouIndustryPage() {
<div className="page-stack">
<Card title="售后单列表">
<Space wrap className="filter-form">
<Input
placeholder="卖家 ID"
value={refundListForm.sellerId}
onChange={(event) => updateRefundListForm({ sellerId: event.target.value })}
/>
{renderSellerIdControl(refundListForm.sellerId, (sellerId) =>
updateRefundListForm({ sellerId }),
)}
<Input
placeholder="订单号"
value={refundListForm.orderId}
@@ -633,8 +716,16 @@ export default function AdminKuaishouIndustryPage() {
]}
onChange={(value) => updateRefundListForm({ type: value })}
/>
<Input placeholder="状态" value={refundListForm.status} onChange={(event) => updateRefundListForm({ status: event.target.value })} />
<Input placeholder="游标" value={refundListForm.pcursor} onChange={(event) => updateRefundListForm({ pcursor: event.target.value })} />
<Input
placeholder="状态"
value={refundListForm.status}
onChange={(event) => updateRefundListForm({ status: event.target.value })}
/>
<Input
placeholder="游标"
value={refundListForm.pcursor}
onChange={(event) => updateRefundListForm({ pcursor: event.target.value })}
/>
<InputNumber
min={1}
max={100}
@@ -654,7 +745,7 @@ export default function AdminKuaishouIndustryPage() {
rowKey={(row) => String(row.refundId || row.oid || JSON.stringify(row))}
columns={refundColumns}
dataSource={refundRows}
scroll={{ x: 980 }}
scroll={{ x: 1120 }}
pagination={{ pageSize: 10 }}
style={{ marginTop: 16 }}
/>
@@ -663,21 +754,27 @@ export default function AdminKuaishouIndustryPage() {
<div className="kuaishou-industry-split">
<Card title="同意退款">
<div className="kuaishou-industry-tool-grid">
<Input
placeholder="卖家 ID"
value={approveForm.sellerId || refundListForm.sellerId}
onChange={(event) => setApproveForm({ ...approveForm, sellerId: event.target.value })}
/>
{renderSellerIdControl(approveForm.sellerId || refundListForm.sellerId, (sellerId) =>
setApproveForm({ ...approveForm, sellerId }),
)}
<Input
placeholder="退款单编号"
value={String(approveForm.refundId || '')}
onChange={(event) => setApproveForm({ ...approveForm, refundId: event.target.value })}
onChange={(event) =>
setApproveForm({
...approveForm,
refundId: event.target.value,
})
}
/>
<Input
placeholder="退款金额(分)"
value={String(approveForm.refundAmount || '')}
onChange={(event) =>
setApproveForm({ ...approveForm, refundAmount: event.target.value })
setApproveForm({
...approveForm,
refundAmount: event.target.value,
})
}
/>
<Input
@@ -689,14 +786,20 @@ export default function AdminKuaishouIndustryPage() {
placeholder="协商状态"
value={String(approveForm.negotiateStatus || '')}
onChange={(event) =>
setApproveForm({ ...approveForm, negotiateStatus: event.target.value })
setApproveForm({
...approveForm,
negotiateStatus: event.target.value,
})
}
/>
<Input
placeholder="退款方式"
value={String(approveForm.refundHandingWay || '')}
onChange={(event) =>
setApproveForm({ ...approveForm, refundHandingWay: event.target.value })
setApproveForm({
...approveForm,
refundHandingWay: event.target.value,
})
}
/>
<Input
@@ -717,46 +820,57 @@ export default function AdminKuaishouIndustryPage() {
<Card title="不同意退款">
<div className="kuaishou-industry-tool-grid">
<Input
placeholder="卖家 ID"
value={disagreeForm.sellerId || refundListForm.sellerId}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, sellerId: event.target.value })
}
/>
{renderSellerIdControl(disagreeForm.sellerId || refundListForm.sellerId, (sellerId) =>
setDisagreeForm({ ...disagreeForm, sellerId }),
)}
<Input
placeholder="退款单编号"
value={String(disagreeForm.refundId || '')}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, refundId: event.target.value })
setDisagreeForm({
...disagreeForm,
refundId: event.target.value,
})
}
/>
<Input
placeholder="拒绝原因枚举"
value={String(disagreeForm.sellerDisagreeReason || '')}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, sellerDisagreeReason: event.target.value })
setDisagreeForm({
...disagreeForm,
sellerDisagreeReason: event.target.value,
})
}
/>
<Input
placeholder="拒绝说明"
value={disagreeForm.sellerDisagreeDesc}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, sellerDisagreeDesc: event.target.value })
setDisagreeForm({
...disagreeForm,
sellerDisagreeDesc: event.target.value,
})
}
/>
<Input
placeholder="退款单状态"
value={String(disagreeForm.status || '')}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, status: event.target.value })
setDisagreeForm({
...disagreeForm,
status: event.target.value,
})
}
/>
<Input
placeholder="协商状态"
value={String(disagreeForm.negotiateStatus || '')}
onChange={(event) =>
setDisagreeForm({ ...disagreeForm, negotiateStatus: event.target.value })
setDisagreeForm({
...disagreeForm,
negotiateStatus: event.target.value,
})
}
/>
</div>
@@ -810,9 +924,52 @@ export default function AdminKuaishouIndustryPage() {
function updateRefundListForm(patch: Partial<RefundListState>) {
setRefundListForm((current) => ({ ...current, ...patch }))
}
function applyDefaultSellerId(sellerId: string) {
setRefundListForm((current) => (current.sellerId ? current : { ...current, sellerId }))
setApproveForm((current) => (current.sellerId ? current : { ...current, sellerId }))
setDisagreeForm((current) => (current.sellerId ? current : { ...current, sellerId }))
setToolForm((current) => (current.sellerId ? current : { ...current, sellerId }))
}
function resolveRefundSellerId(value?: unknown): string {
return (
String(value || refundListForm.sellerId || '').trim() ||
(refundShopOptions.length === 1 ? refundShopOptions[0]?.value || '' : '')
)
}
function renderSellerIdControl(value: string | undefined, onChange: (sellerId: string) => void) {
const normalizedValue = String(value || '').trim()
if (refundShopOptions.length > 0) {
return (
<Select
allowClear
showSearch
optionFilterProp="label"
placeholder="授权店铺"
style={{ minWidth: 220 }}
value={normalizedValue || undefined}
options={refundShopOptions}
onChange={(nextValue) => onChange(String(nextValue || '').trim())}
/>
)
}
return (
<Input
allowClear
placeholder="卖家 ID"
value={normalizedValue}
onChange={(event) => onChange(event.target.value)}
/>
)
}
}
function extractRefundRows(response: Record<string, unknown> | null): Array<Record<string, unknown>> {
function extractRefundRows(
response: Record<string, unknown> | null,
): Array<Record<string, unknown>> {
const data = response?.data
if (!data || typeof data !== 'object' || Array.isArray(data)) {
return []
@@ -827,12 +984,14 @@ function extractRefundRows(response: Record<string, unknown> | null): Array<Reco
}
function resolveOpenApiResultSummary(value: unknown) {
const result = value && typeof value === 'object' && !Array.isArray(value)
? value as Record<string, unknown>
: {}
const response = result.response && typeof result.response === 'object' && !Array.isArray(result.response)
? result.response as Record<string, unknown>
: null
const result =
value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, unknown>)
: {}
const response =
result.response && typeof result.response === 'object' && !Array.isArray(result.response)
? (result.response as Record<string, unknown>)
: null
const success = Boolean(result.success)
const code = String(response?.code || response?.sub_code || '').trim()
const resultCode = String(response?.result || '').trim()
@@ -856,7 +1015,8 @@ function resolveOpenApiResultSummary(value: unknown) {
return {
success,
title: success ? '接口返回成功' : '接口返回失败',
description: [markers.join(' / '), message].filter(Boolean).join('') || '请查看下方原始响应。',
description:
[markers.join(' / '), message].filter(Boolean).join('') || '请查看下方原始响应。',
}
}
@@ -875,6 +1035,13 @@ function getVoucherStatusLabel(status: string) {
return status || '-'
}
function formatShopOptionLabel(shop: AdminKuaishouIndustryShopConfig) {
const name = shop.customShopName || shop.shopName || '未命名店铺'
const tokenStatus = shop.hasAccessToken ? '' : ' / 未授权'
return `${name} / ${shop.sellerId}${tokenStatus}`
}
function formatTimestamp(value: unknown) {
const timestamp = Number(value || 0)
if (!Number.isFinite(timestamp) || timestamp <= 0) {
@@ -76,6 +76,7 @@ export interface AdminKuaishouIndustryRefundListPayload {
export interface AdminKuaishouIndustryRefundApprovePayload {
sellerId?: string
orderId?: string
refundId?: number | string
desc?: string
refundAmount?: number | string
@@ -86,6 +87,7 @@ export interface AdminKuaishouIndustryRefundApprovePayload {
export interface AdminKuaishouIndustryRefundDisagreePayload {
sellerId?: string
orderId?: string
refundId?: number | string
sellerDisagreeReason?: number | string
sellerDisagreeDesc?: string