优化快手电子凭证发码和手动处理

This commit is contained in:
yml2213
2026-07-08 17:28:01 +08:00
parent 647f038061
commit 3ad7c2e50e
17 changed files with 761 additions and 10 deletions
+10 -1
View File
@@ -1,4 +1,4 @@
import { apiGet } from '@/lib/http'
import { apiGet, apiPost } from '@/lib/http'
import type { AdminOrderDetail, AdminOrderListItem, AdminPagination } from '@/types/admin'
export function fetchAdminOrders(params?: Record<string, unknown>) {
@@ -11,3 +11,12 @@ export function fetchAdminOrders(params?: Record<string, unknown>) {
export function fetchAdminOrderDetail(orderId: number | string) {
return apiGet<AdminOrderDetail>(`/api/v1/admin/orders/${orderId}`)
}
export function resendAdminOrderKuaishouIndustryVoucherCode(orderId: number | string) {
return apiPost<{
success: boolean
resentCount: number
failedCount: number
errorMessage: string
}>(`/api/v1/admin/orders/${orderId}/kuaishou-industry/resend-code`, {})
}
@@ -91,3 +91,26 @@ export function returnNumberAdminTaskKuaishouCloudFulfillment(taskId: number | s
{},
)
}
export function resendAdminTaskKuaishouIndustryVoucherCode(taskId: number | string) {
return apiPost<AdminTaskActionResponse>(
`/api/v1/admin/tasks/${taskId}/kuaishou-industry/resend-code`,
{},
)
}
export function consumeAdminTaskKuaishouIndustryVoucher(
taskId: number | string,
payload: {
consumeType?: string
storeName?: string
storeAddress?: string
expressCode?: string
expressNo?: string
} = {},
) {
return apiPost<AdminTaskActionResponse>(
`/api/v1/admin/tasks/${taskId}/kuaishou-industry/consume`,
payload,
)
}
@@ -69,4 +69,19 @@ export interface AdminOrderDetail {
spec: Record<string, unknown>
}>
tasks: AdminTaskListItem[]
kuaishouIndustryVouchers: Array<{
oid: string
voucherCode: string
unitIndex: number
status: string
sendCallbackStatus: string
sendCallbackAttemptCount: number
sendCallbackLastError: string
sendCallbackSentAt: string | null
consumedAt: string | null
destroyedAt: string | null
}>
operations: {
canResendKuaishouIndustryVoucherCode: boolean
}
}
@@ -37,6 +37,8 @@ export interface AdminTaskOperations {
canRebindKuaishouCloudRole: boolean
canDispatchKuaishouCloudFulfillment: boolean
canReturnKuaishouCloudFulfillment: boolean
canResendKuaishouIndustryVoucherCode: boolean
canConsumeKuaishouIndustryVoucher: boolean
canViewSensitiveTaskData: boolean
}
@@ -201,6 +203,18 @@ export interface AdminTaskDetail {
}
notes: string
}
kuaishouIndustryVoucher: null | {
oid: string
voucherCode: string
unitIndex: number
status: string
sendCallbackStatus: string
sendCallbackLastError: string
sendCallbackAttemptCount: number
sendCallbackSentAt: string | null
consumedAt: string | null
destroyedAt: string | null
}
manualDispatch: null | {
outcome: string
deliveryReference: string
@@ -3,27 +3,63 @@ import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import AdminStatusTag from '@/components/admin/AdminStatusTag.vue'
import { fetchAdminOrderDetail } from '@/services/admin'
import { fetchAdminOrderDetail, resendAdminOrderKuaishouIndustryVoucherCode } from '@/services/admin'
import type { AdminOrderDetail } from '@/types/admin'
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
import { formatAdminDateTime } from '@/utils/admin-time'
const route = useRoute()
const loading = ref(true)
const actionLoading = ref(false)
const errorMessage = ref('')
const detail = ref<AdminOrderDetail | null>(null)
type TagType = 'success' | 'primary' | 'warning' | 'danger' | 'info'
onMounted(async () => {
async function loadDetail() {
loading.value = true
try {
const response = await fetchAdminOrderDetail(String(route.params.orderId || ''))
detail.value = response.data
errorMessage.value = ''
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : '读取订单详情失败'
} finally {
loading.value = false
}
})
}
onMounted(loadDetail)
async function handleResendKuaishouIndustryCode() {
if (!detail.value) return
try {
await showConfirm(
`确认重发订单 ${detail.value.order.platformOrderId} 的电子凭证发码回调吗?系统会复用当前核销码。`,
'确认操作',
{
type: 'warning',
confirmButtonText: '继续执行',
cancelButtonText: '取消',
},
)
} catch {
return
}
actionLoading.value = true
try {
const response = await resendAdminOrderKuaishouIndustryVoucherCode(detail.value.order.orderId)
showSuccess(`电子凭证发码回调已重发,共 ${response.data.resentCount}`)
await loadDetail()
} catch (error) {
showError(error instanceof Error ? error.message : '重发电子凭证发码回调失败')
await loadDetail()
} finally {
actionLoading.value = false
}
}
function formatJsonBlock(value: Record<string, unknown>) {
return JSON.stringify(value || {}, null, 2)
@@ -171,6 +207,33 @@ function resolveOrderProgressTone(summary: AdminOrderDetail['order']['fulfillmen
</dl>
</el-card>
<el-card v-if="detail.kuaishouIndustryVouchers.length > 0" shadow="never" class="section-card">
<div class="section-headline">
<h3>电子凭证</h3>
<el-button
v-if="detail.operations.canResendKuaishouIndustryVoucherCode"
:loading="actionLoading"
type="primary"
plain
@click="handleResendKuaishouIndustryCode"
>
重发发码回调
</el-button>
</div>
<el-table :data="detail.kuaishouIndustryVouchers" size="small">
<el-table-column prop="voucherCode" label="核销码" min-width="190" />
<el-table-column prop="status" label="券状态" width="110" />
<el-table-column prop="sendCallbackStatus" label="发码回调" width="120" />
<el-table-column label="回调时间" width="180">
<template #default="{ row }">{{ formatAdminDateTime(row.sendCallbackSentAt) }}</template>
</el-table-column>
<el-table-column prop="sendCallbackAttemptCount" label="次数" width="80" />
<el-table-column label="异常">
<template #default="{ row }">{{ row.sendCallbackLastError || '-' }}</template>
</el-table-column>
</el-table>
</el-card>
<el-card shadow="never" class="section-card">
<h3>履约任务</h3>
<el-table :data="detail.tasks" size="small">
@@ -240,6 +303,18 @@ function resolveOrderProgressTone(summary: AdminOrderDetail['order']['fulfillmen
color: var(--text-primary);
}
.section-headline {
display: flex;
justify-content: space-between;
gap: var(--space-3);
align-items: center;
margin-bottom: var(--space-4);
}
.section-headline h3 {
margin: 0;
}
.order-hero {
display: flex;
justify-content: space-between;
@@ -49,6 +49,8 @@ const {
submitKuaishouCloudDispatch,
submitKuaishouCloudRefreshRoleInfo,
submitKuaishouCloudReturnNumber,
submitKuaishouIndustryConsume,
submitKuaishouIndustryResendCode,
} = useAdminTaskKuaishouCloud(detail, runAction)
onMounted(loadDetail)
@@ -93,6 +95,8 @@ onMounted(loadDetail)
@prepare-kuaishou-cloud="submitKuaishouCloudPrepare"
@dispatch-kuaishou-cloud="submitKuaishouCloudDispatch"
@return-kuaishou-cloud="submitKuaishouCloudReturnNumber"
@resend-industry-code="submitKuaishouIndustryResendCode"
@consume-industry-voucher="submitKuaishouIndustryConsume"
@mark-manual-review="
runAction(
() => markAdminTaskManualReview(detail!.task.taskId),
@@ -16,6 +16,8 @@ const emit = defineEmits<{
prepareKuaishouCloud: []
dispatchKuaishouCloud: []
returnKuaishouCloud: []
resendIndustryCode: []
consumeIndustryVoucher: []
markManualReview: []
submitManualDispatch: [outcome: 'delivered' | 'failed']
closeTask: []
@@ -70,6 +72,24 @@ const emit = defineEmits<{
>
退还号码
</el-button>
<el-button
v-if="canManageTaskLifecycle && detail.operations.canResendKuaishouIndustryVoucherCode"
:loading="actionLoading"
round
@click="emit('resendIndustryCode')"
>
重发发码回调
</el-button>
<el-button
v-if="canManageTaskLifecycle && detail.operations.canConsumeKuaishouIndustryVoucher"
:loading="actionLoading"
round
type="warning"
plain
@click="emit('consumeIndustryVoucher')"
>
手动核销
</el-button>
<el-button
v-if="canManageTaskLifecycle"
:disabled="!detail.operations.canMarkManualReview"
@@ -300,6 +300,25 @@ function formatRebindBinding(
<div>
<span>核销码</span><strong>{{ flow.ticket.code || '-' }}</strong>
</div>
<div>
<span>电子凭证状态</span><strong>{{ detail.kuaishouIndustryVoucher?.status || '-' }}</strong>
</div>
<div>
<span>发码回调</span
><strong>{{ detail.kuaishouIndustryVoucher?.sendCallbackStatus || '-' }}</strong>
</div>
<div>
<span>回调次数</span
><strong>{{ detail.kuaishouIndustryVoucher?.sendCallbackAttemptCount || 0 }}</strong>
</div>
<div>
<span>回调时间</span
><strong>{{ formatAdminDateTime(detail.kuaishouIndustryVoucher?.sendCallbackSentAt) }}</strong>
</div>
<div>
<span>回调异常</span
><strong>{{ detail.kuaishouIndustryVoucher?.sendCallbackLastError || '-' }}</strong>
</div>
<div>
<span>校验状态</span><strong>{{ flow.ticket.status || '-' }}</strong>
</div>
@@ -102,6 +102,7 @@ export function useAdminTaskDetail() {
await loadDetail()
} catch (error) {
showError(error instanceof Error ? error.message : '操作失败')
await loadDetail()
} finally {
actionLoading.value = false
}
@@ -1,9 +1,11 @@
import { computed, type Ref } from 'vue'
import {
consumeAdminTaskKuaishouIndustryVoucher,
dispatchAdminTaskKuaishouCloudFulfillment,
prepareAdminTaskKuaishouCloudFulfillment,
refreshAdminTaskKuaishouCloudRoleInfo,
resendAdminTaskKuaishouIndustryVoucherCode,
returnNumberAdminTaskKuaishouCloudFulfillment,
} from '@/services/admin'
import type { AdminTaskActionResponse, AdminTaskDetail } from '@/types/admin'
@@ -178,6 +180,30 @@ export function useAdminTaskKuaishouCloud(
)
}
async function submitKuaishouIndustryResendCode() {
if (!detail.value) {
return
}
await runAction(
() => resendAdminTaskKuaishouIndustryVoucherCode(detail.value!.task.taskId),
'电子凭证发码回调已重发',
`确认重发任务 ${detail.value.task.taskNo} 的电子凭证发码回调吗?系统会复用当前核销码,不会生成新码。`,
)
}
async function submitKuaishouIndustryConsume() {
if (!detail.value) {
return
}
await runAction(
() => consumeAdminTaskKuaishouIndustryVoucher(detail.value!.task.taskId),
'电子凭证已执行核销',
`确认手动执行任务 ${detail.value.task.taskNo} 的电子凭证核销吗?请先确认快手侧已收到发码回调。`,
)
}
return {
kuaishouCloudFlow,
kuaishouCloudRole,
@@ -189,5 +215,7 @@ export function useAdminTaskKuaishouCloud(
submitKuaishouCloudDispatch,
submitKuaishouCloudRefreshRoleInfo,
submitKuaishouCloudReturnNumber,
submitKuaishouIndustryResendCode,
submitKuaishouIndustryConsume,
}
}