feat(kuaishou): 增加历史电子券补发并核销
This commit is contained in:
@@ -6,15 +6,18 @@ import {
|
||||
SearchOutlined,
|
||||
SendOutlined,
|
||||
SyncOutlined,
|
||||
ThunderboltOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Collapse,
|
||||
Descriptions,
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Select,
|
||||
Space,
|
||||
@@ -39,6 +42,7 @@ import {
|
||||
fetchAdminKuaishouIndustryShops,
|
||||
fetchAdminKuaishouIndustryVouchers,
|
||||
listAdminKuaishouIndustryRefunds,
|
||||
reissueAndConsumeAdminKuaishouIndustryVoucher,
|
||||
resendAdminKuaishouIndustryVoucherCode,
|
||||
reverseAdminKuaishouIndustryVoucher,
|
||||
} from '@/services/admin'
|
||||
@@ -69,6 +73,7 @@ type IndustryActionKind =
|
||||
| 'destroy'
|
||||
| 'reverse'
|
||||
| 'resend'
|
||||
| 'reissue-consume'
|
||||
| 'refund-list'
|
||||
| 'refund-approve'
|
||||
| 'refund-disagree'
|
||||
@@ -120,6 +125,11 @@ export default function AdminKuaishouIndustryPage() {
|
||||
const [actionLoading, setActionLoading] = useState('')
|
||||
const [consumingVoucherId, setConsumingVoucherId] = useState<number | null>(null)
|
||||
const [reversingVoucherId, setReversingVoucherId] = useState<number | null>(null)
|
||||
const [reissueConsumeOpen, setReissueConsumeOpen] = useState(false)
|
||||
const [reissueConsumeMinutes, setReissueConsumeMinutes] = useState<number>(10)
|
||||
const [reissueConsumeReason, setReissueConsumeReason] =
|
||||
useState('历史发码失败补偿,补发后立即核销')
|
||||
const [reissueConsumeConfirmed, setReissueConsumeConfirmed] = useState(false)
|
||||
const [vouchers, setVouchers] = useState<AdminKuaishouIndustryVoucher[]>([])
|
||||
const [selectedVoucher, setSelectedVoucher] = useState<AdminKuaishouIndustryVoucher | null>(null)
|
||||
const [voucherTotal, setVoucherTotal] = useState(0)
|
||||
@@ -646,6 +656,64 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
}
|
||||
|
||||
function openReissueConsumeModal() {
|
||||
if (!selectedVoucher && !String(toolForm.voucherCode || '').trim()) {
|
||||
showError('请先选择或填写本地券码')
|
||||
return
|
||||
}
|
||||
if (selectedVoucher) {
|
||||
const sendSucceeded = selectedVoucher.sendCallbackStatus === 'success'
|
||||
const stillValid = Number(selectedVoucher.validEndTime || 0) > Date.now()
|
||||
if (selectedVoucher.status !== 'UNUSED') {
|
||||
showError('仅未使用电子凭证可以补发并核销')
|
||||
return
|
||||
}
|
||||
if (sendSucceeded && stillValid) {
|
||||
showError('该券已发码成功且仍在有效期,请直接执行手动核销')
|
||||
return
|
||||
}
|
||||
}
|
||||
setReissueConsumeConfirmed(false)
|
||||
setReissueConsumeOpen(true)
|
||||
}
|
||||
|
||||
async function runReissueConsume() {
|
||||
if (!reissueConsumeConfirmed) {
|
||||
showError('请先确认已核实订单仍需履约')
|
||||
return
|
||||
}
|
||||
|
||||
setActionLoading('reissue-consume')
|
||||
try {
|
||||
const response = await reissueAndConsumeAdminKuaishouIndustryVoucher({
|
||||
...buildVoucherToolPayload(),
|
||||
validMinutes: reissueConsumeMinutes,
|
||||
reason: reissueConsumeReason,
|
||||
})
|
||||
publishActionResult(
|
||||
buildIndustryActionResultView('reissue-consume', '历史券补发并核销', response.data),
|
||||
)
|
||||
if (response.data.success) {
|
||||
showSuccess('原券码已同步到快手并完成核销')
|
||||
setReissueConsumeOpen(false)
|
||||
} else {
|
||||
showError(response.data.error || '历史券补发并核销失败')
|
||||
}
|
||||
await loadVouchers()
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '历史券补发并核销失败'
|
||||
publishActionResult(
|
||||
buildIndustryActionResultView('reissue-consume', '历史券补发并核销', {
|
||||
success: false,
|
||||
error: message,
|
||||
}),
|
||||
)
|
||||
showError(message)
|
||||
} finally {
|
||||
setActionLoading('')
|
||||
}
|
||||
}
|
||||
|
||||
async function runDestroy() {
|
||||
setActionLoading('destroy')
|
||||
try {
|
||||
@@ -870,6 +938,98 @@ export default function AdminKuaishouIndustryPage() {
|
||||
onChange={(key) => setActiveTab(key as IndustryTab)}
|
||||
items={tabItems}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="历史券补发并核销"
|
||||
open={reissueConsumeOpen}
|
||||
okText="补发并核销"
|
||||
cancelText="取消"
|
||||
confirmLoading={actionLoading === 'reissue-consume'}
|
||||
okButtonProps={{
|
||||
disabled:
|
||||
!reissueConsumeConfirmed ||
|
||||
!String(reissueConsumeReason || '').trim() ||
|
||||
reissueConsumeMinutes < 1 ||
|
||||
reissueConsumeMinutes > 60,
|
||||
}}
|
||||
onOk={runReissueConsume}
|
||||
onCancel={() => setReissueConsumeOpen(false)}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Space direction="vertical" size={16} className="full-width">
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
message="该操作会重发原券码并立即核销"
|
||||
description="仅用于已确认订单仍需履约、但历史发码失败或凭证已过期的补偿场景。操作会先临时延长本地券有效期,发码成功后立即核销。"
|
||||
/>
|
||||
|
||||
<Descriptions
|
||||
size="small"
|
||||
bordered
|
||||
column={1}
|
||||
items={[
|
||||
{
|
||||
key: 'voucherCode',
|
||||
label: '本地券码',
|
||||
children:
|
||||
selectedVoucher?.voucherCode || String(toolForm.voucherCode || '').trim() || '-',
|
||||
},
|
||||
{
|
||||
key: 'oid',
|
||||
label: '平台订单',
|
||||
children:
|
||||
selectedVoucher?.oid ||
|
||||
String(toolForm.oid || toolForm.orderId || '').trim() ||
|
||||
'-',
|
||||
},
|
||||
{
|
||||
key: 'validEndTime',
|
||||
label: '原有效期至',
|
||||
children: selectedVoucher
|
||||
? formatAdminDateTime(
|
||||
new Date(Number(selectedVoucher.validEndTime || 0)).toISOString(),
|
||||
)
|
||||
: '-',
|
||||
},
|
||||
{
|
||||
key: 'sendError',
|
||||
label: '最近发码错误',
|
||||
children: selectedVoucher?.sendCallbackLastError || '-',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>临时有效期(分钟)</span>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={60}
|
||||
precision={0}
|
||||
value={reissueConsumeMinutes}
|
||||
onChange={(value) => setReissueConsumeMinutes(Number(value || 10))}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>补发原因</span>
|
||||
<Input
|
||||
maxLength={200}
|
||||
showCount
|
||||
value={reissueConsumeReason}
|
||||
onChange={(event) => setReissueConsumeReason(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<Checkbox
|
||||
checked={reissueConsumeConfirmed}
|
||||
onChange={(event) => setReissueConsumeConfirmed(event.target.checked)}
|
||||
>
|
||||
我已核实该订单仍需履约,并确认补发后立即核销
|
||||
</Checkbox>
|
||||
</Space>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1210,6 +1370,15 @@ export default function AdminKuaishouIndustryPage() {
|
||||
>
|
||||
手动核销
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<ThunderboltOutlined />}
|
||||
loading={actionLoading === 'reissue-consume'}
|
||||
disabled={!hasVoucherTarget}
|
||||
onClick={openReissueConsumeModal}
|
||||
>
|
||||
历史补发并核销
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<RollbackOutlined />}
|
||||
@@ -1677,6 +1846,7 @@ function buildIndustryActionResultView(
|
||||
const responseData = asRecord(response?.data)
|
||||
const success = resolveActionSuccess(kind, record, openApi)
|
||||
const platformMessage = pickFirstString([
|
||||
...(kind === 'reissue-consume' ? [record.error] : []),
|
||||
response?.error_msg,
|
||||
response?.msg,
|
||||
response?.sub_msg,
|
||||
@@ -1788,6 +1958,24 @@ function buildIndustryActionResultView(
|
||||
}
|
||||
}
|
||||
|
||||
if (kind === 'reissue-consume') {
|
||||
pushHighlight(highlights, '执行阶段', String(record.stage || '-'))
|
||||
if (record.validMinutes) {
|
||||
pushHighlight(highlights, '临时有效期', `${String(record.validMinutes)} 分钟`)
|
||||
}
|
||||
for (const [label, value] of [
|
||||
['发码成功', record.sendSuccess],
|
||||
['核销成功', record.consumeSuccess],
|
||||
] as const) {
|
||||
if (typeof value === 'boolean') {
|
||||
pushHighlight(highlights, label, value ? '是' : '否')
|
||||
}
|
||||
}
|
||||
if (record.reason) {
|
||||
pushHighlight(highlights, '补发原因', String(record.reason))
|
||||
}
|
||||
}
|
||||
|
||||
if (platformMessage) {
|
||||
pushHighlight(highlights, '平台消息', platformMessage)
|
||||
}
|
||||
@@ -1855,6 +2043,12 @@ function buildActionSummary(
|
||||
: context.platformMessage || '重发发码失败。'
|
||||
}
|
||||
|
||||
if (kind === 'reissue-consume') {
|
||||
return success
|
||||
? '原券码已同步到快手并完成核销。'
|
||||
: context.platformMessage || '历史券补发并核销失败。'
|
||||
}
|
||||
|
||||
if (kind === 'refund-list') {
|
||||
return success
|
||||
? `查询成功,共 ${context.refundCount ?? 0} 条售后单,已填充列表。`
|
||||
|
||||
@@ -8,6 +8,8 @@ import type {
|
||||
AdminKuaishouIndustryVoucherConsumeResult,
|
||||
AdminKuaishouIndustryVoucherDestroyResult,
|
||||
AdminKuaishouIndustryVoucherListResult,
|
||||
AdminKuaishouIndustryVoucherReissueConsumePayload,
|
||||
AdminKuaishouIndustryVoucherReissueConsumeResult,
|
||||
AdminKuaishouIndustryVoucherResendResult,
|
||||
AdminKuaishouIndustryVoucherToolPayload,
|
||||
} from '@/types/admin'
|
||||
@@ -84,6 +86,15 @@ export function resendAdminKuaishouIndustryVoucherCode(
|
||||
)
|
||||
}
|
||||
|
||||
export function reissueAndConsumeAdminKuaishouIndustryVoucher(
|
||||
payload: AdminKuaishouIndustryVoucherReissueConsumePayload,
|
||||
) {
|
||||
return apiPost<AdminKuaishouIndustryVoucherReissueConsumeResult>(
|
||||
'/api/v1/admin/kuaishou-industry/vouchers/reissue-consume',
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function destroyAdminKuaishouIndustryVoucher(
|
||||
payload: AdminKuaishouIndustryVoucherToolPayload,
|
||||
) {
|
||||
|
||||
@@ -81,6 +81,8 @@ export type {
|
||||
AdminKuaishouIndustryShopListResult,
|
||||
AdminKuaishouIndustryVoucherConsumeResult,
|
||||
AdminKuaishouIndustryVoucherDestroyResult,
|
||||
AdminKuaishouIndustryVoucherReissueConsumeResult,
|
||||
AdminKuaishouIndustryVoucherReissueConsumePayload,
|
||||
AdminKuaishouIndustryVoucherResendResult,
|
||||
AdminKuaishouIndustryRefundListPayload,
|
||||
AdminKuaishouIndustryRefundApprovePayload,
|
||||
|
||||
@@ -94,6 +94,26 @@ export interface AdminKuaishouIndustryVoucherResendResult {
|
||||
voucher: AdminKuaishouIndustryVoucher | null
|
||||
}
|
||||
|
||||
export interface AdminKuaishouIndustryVoucherReissueConsumeResult {
|
||||
success: boolean
|
||||
stage: 'send' | 'consume' | 'completed' | string
|
||||
reason: string
|
||||
validMinutes: number
|
||||
originalValidEndTime: number
|
||||
validEndTime: number
|
||||
sendSuccess: boolean
|
||||
consumeSuccess: boolean
|
||||
response: Record<string, unknown> | null
|
||||
error: string
|
||||
voucher: AdminKuaishouIndustryVoucher | null
|
||||
task: null | {
|
||||
taskId: number
|
||||
taskNo: string
|
||||
status: string
|
||||
deliveryStatus: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface AdminKuaishouIndustryRefundListPayload {
|
||||
sellerId?: string
|
||||
beginTime?: number | string
|
||||
@@ -149,3 +169,8 @@ export interface AdminKuaishouIndustryVoucherToolPayload {
|
||||
expressNo?: string
|
||||
etickets?: Array<{ id?: string; code?: string; num?: number | string }>
|
||||
}
|
||||
|
||||
export interface AdminKuaishouIndustryVoucherReissueConsumePayload extends AdminKuaishouIndustryVoucherToolPayload {
|
||||
validMinutes: number | string
|
||||
reason: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user