fix(fulfillment): 修复载体重试并完善核销任务列表

This commit is contained in:
yml2213
2026-11-05 18:36:10 +08:00
parent 89a68b5734
commit e8b76b3b73
19 changed files with 945 additions and 104 deletions
@@ -1,7 +1,8 @@
import { ReloadOutlined, SaveOutlined } from '@ant-design/icons'
import { ReloadOutlined, SaveOutlined, SearchOutlined } from '@ant-design/icons'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import {
Button,
Input,
InputNumber,
Select,
Space,
@@ -27,6 +28,7 @@ import {
type VoucherConsumePolicy,
type VoucherConsumeProvider,
} from '@/services/admin'
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
import { formatDateTime } from '@/utils/date-time'
const PROVIDER_LABEL: Record<VoucherConsumeProvider, string> = {
@@ -90,16 +92,63 @@ const JOB_STATUS: Record<string, { color: string; label: string }> = {
cancelled: { color: 'default', label: '已取消' },
}
const PROVIDER_FILTER_OPTIONS: Array<{ value: VoucherConsumeProvider; label: string }> = [
{ value: 'kuaishou_industry', label: PROVIDER_LABEL.kuaishou_industry },
{ value: 'douyin_mashida', label: PROVIDER_LABEL.douyin_mashida },
]
const CHANNEL_FILTER_OPTIONS: Array<{ value: VoucherConsumeChannel; label: string }> = [
{ value: 'affiliate_dash', label: CHANNEL_LABEL.affiliate_dash },
{ value: 'work_order', label: CHANNEL_LABEL.work_order },
{ value: 'manual', label: CHANNEL_LABEL.manual },
{ value: 'third_party_voucher', label: CHANNEL_LABEL.third_party_voucher },
{ value: 'on_account', label: CHANNEL_LABEL.on_account },
]
const JOB_STATUS_FILTER_OPTIONS: Array<{
value: VoucherConsumeJob['status']
label: string
}> = [
{ value: 'pending', label: JOB_STATUS.pending!.label },
{ value: 'processing', label: JOB_STATUS.processing!.label },
{ value: 'succeeded', label: JOB_STATUS.succeeded!.label },
{ value: 'failed', label: JOB_STATUS.failed!.label },
{ value: 'cancelled', label: JOB_STATUS.cancelled!.label },
]
export default function VoucherConsumePolicyPanel() {
const queryClient = useQueryClient()
const [draft, setDraft] = useState<VoucherConsumePolicy | null>(null)
const [keywordDraft, setKeywordDraft] = useState('')
const [keyword, setKeyword] = useState('')
const [providerFilter, setProviderFilter] = useState<VoucherConsumeProvider | ''>('')
const [channelFilter, setChannelFilter] = useState<VoucherConsumeChannel | ''>('')
const [statusFilter, setStatusFilter] = useState<VoucherConsumeJob['status'] | ''>('')
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
const policyQuery = useQuery({
queryKey: ['voucher-consume-policy'],
queryFn: fetchVoucherConsumePolicy,
})
const jobsQuery = useQuery({
queryKey: ['voucher-consume-jobs'],
queryFn: () => fetchVoucherConsumeJobs({ page: 1, pageSize: 20 }),
queryKey: [
'voucher-consume-jobs',
keyword,
providerFilter,
channelFilter,
statusFilter,
page,
pageSize,
],
queryFn: () =>
fetchVoucherConsumeJobs({
keyword: keyword || undefined,
provider: providerFilter || undefined,
channel: channelFilter || undefined,
status: statusFilter || undefined,
page,
pageSize,
}),
refetchInterval: 10_000,
})
@@ -134,6 +183,16 @@ export default function VoucherConsumePolicyPanel() {
})
const rows = useMemo(() => buildPolicyRows(draft), [draft])
const hasJobFilters = Boolean(keyword || providerFilter || channelFilter || statusFilter)
function resetJobFilters() {
setKeywordDraft('')
setKeyword('')
setProviderFilter('')
setChannelFilter('')
setStatusFilter('')
setPage(1)
}
function updateChannel(
provider: VoucherConsumeProvider,
@@ -348,25 +407,92 @@ export default function VoucherConsumePolicyPanel() {
loading={policyQuery.isLoading}
/>
<Space className="full-width" style={{ justifyContent: 'space-between' }}>
<Space className="full-width" style={{ justifyContent: 'space-between' }} wrap>
<Typography.Title level={5} style={{ margin: 0 }}>
最近核销任务
</Typography.Title>
<Button
icon={<ReloadOutlined />}
onClick={() => void queryClient.invalidateQueries({ queryKey: ['voucher-consume-jobs'] })}
>
刷新
</Button>
<Space wrap>
<Input.Search
allowClear
prefix={<SearchOutlined />}
placeholder="任务号 / 订单号 / 平台单号"
value={keywordDraft}
onChange={(event) => {
const value = event.target.value
setKeywordDraft(value)
if (!value) {
setKeyword('')
setPage(1)
}
}}
onSearch={(value) => {
setKeyword(String(value || '').trim())
setPage(1)
}}
style={{ width: 260 }}
/>
<Select
allowClear
placeholder="全部平台"
value={providerFilter || undefined}
options={PROVIDER_FILTER_OPTIONS}
onChange={(value: VoucherConsumeProvider | undefined) => {
setProviderFilter(value || '')
setPage(1)
}}
style={{ width: 150 }}
/>
<Select
allowClear
placeholder="全部渠道"
value={channelFilter || undefined}
options={CHANNEL_FILTER_OPTIONS}
onChange={(value: VoucherConsumeChannel | undefined) => {
setChannelFilter(value || '')
setPage(1)
}}
style={{ width: 150 }}
/>
<Select
allowClear
placeholder="全部状态"
value={statusFilter || undefined}
options={JOB_STATUS_FILTER_OPTIONS}
onChange={(value: VoucherConsumeJob['status'] | undefined) => {
setStatusFilter(value || '')
setPage(1)
}}
style={{ width: 130 }}
/>
<Button disabled={!hasJobFilters} onClick={resetJobFilters}>
重置
</Button>
<Button
icon={<ReloadOutlined />}
onClick={() =>
void queryClient.invalidateQueries({ queryKey: ['voucher-consume-jobs'] })
}
>
刷新
</Button>
</Space>
</Space>
<PercentTable
rowKey="id"
size="small"
columns={jobColumns}
dataSource={jobsQuery.data?.data.items || []}
loading={jobsQuery.isLoading}
pagination={false}
scroll={{ x: 1100 }}
loading={jobsQuery.isFetching}
pagination={buildAdminTablePagination({
current: jobsQuery.data?.data.page || page,
pageSize: jobsQuery.data?.data.pageSize || pageSize,
total: jobsQuery.data?.data.total || 0,
onChange: (nextPage, nextPageSize) => {
setPage(nextPageSize !== pageSize ? 1 : nextPage)
setPageSize(nextPageSize)
},
})}
scroll={{ x: 1200 }}
/>
</Space>
)