优化快手电子凭证发码和手动处理
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { getAdminOrderDetail, getAdminOrders } from '../../services/admin/admin-read-service.js'
|
||||
import { createJsonHandler } from './session.js'
|
||||
import { resendAdminOrderKuaishouIndustryVoucherCodes } from '../../services/admin/write/kuaishou-cloud-actions.js'
|
||||
import { createJsonHandler, requireAdminRoles } from './session.js'
|
||||
import type {
|
||||
AdminOrderRouteParams,
|
||||
AdminOrderRouteQuery,
|
||||
@@ -19,7 +20,11 @@ router.get('/orders', createJsonHandler(
|
||||
))
|
||||
|
||||
router.get('/orders/:orderId', createJsonHandler(
|
||||
(req) => getAdminOrderDetail(String((req.params as AdminOrderRouteParams).orderId || '')),
|
||||
(req) =>
|
||||
getAdminOrderDetail(
|
||||
String((req.params as AdminOrderRouteParams).orderId || ''),
|
||||
req.adminSession || null,
|
||||
),
|
||||
{
|
||||
successMessage: 'ok',
|
||||
errorMessage: '读取订单详情失败',
|
||||
@@ -27,4 +32,27 @@ router.get('/orders/:orderId', createJsonHandler(
|
||||
},
|
||||
))
|
||||
|
||||
router.post(
|
||||
'/orders/:orderId/kuaishou-industry/resend-code',
|
||||
requireAdminRoles(['admin', 'operator']),
|
||||
createJsonHandler(
|
||||
(req) =>
|
||||
resendAdminOrderKuaishouIndustryVoucherCodes(
|
||||
String((req.params as AdminOrderRouteParams).orderId || ''),
|
||||
req.adminSession || null,
|
||||
),
|
||||
{
|
||||
successMessage: '电子凭证发码回调已重发',
|
||||
errorMessage: '重发电子凭证发码回调失败',
|
||||
scope: '[admin/orders/:orderId/kuaishou-industry/resend-code]',
|
||||
audit: (req, data) => ({
|
||||
action: 'order_kuaishou_industry_resend_code',
|
||||
targetType: 'order',
|
||||
targetId: String((req.params as AdminOrderRouteParams).orderId || ''),
|
||||
data: data && typeof data === 'object' ? data as Record<string, unknown> : {},
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
export default router
|
||||
|
||||
@@ -4,15 +4,18 @@ import { Router } from 'express'
|
||||
import { getAdminTaskDetail, getAdminTasks } from '../../services/admin/admin-read-service.js'
|
||||
import { closeAdminTask } from '../../services/admin/write/task-actions.js'
|
||||
import {
|
||||
consumeAdminTaskKuaishouIndustryVoucher,
|
||||
dispatchAdminTaskKuaishouCloudFulfillment,
|
||||
prepareAdminTaskKuaishouCloudFulfillment,
|
||||
rebindAdminTaskKuaishouCloudRole,
|
||||
refreshAdminTaskKuaishouCloudRoleInfo,
|
||||
resendAdminTaskKuaishouIndustryVoucherCode,
|
||||
returnNumberAdminTaskKuaishouCloudFulfillment,
|
||||
} from '../../services/admin/write/kuaishou-cloud-actions.js'
|
||||
import { createJsonHandler, requireAdminRoles } from './session.js'
|
||||
import type {
|
||||
AdminTaskKuaishouCloudDispatchRouteBody,
|
||||
AdminTaskKuaishouIndustryConsumeRouteBody,
|
||||
AdminTaskRouteParams,
|
||||
AdminTaskRouteQuery,
|
||||
} from '../../types/admin/route-inputs.js'
|
||||
@@ -148,6 +151,40 @@ router.post(
|
||||
),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/tasks/:taskId/kuaishou-industry/resend-code',
|
||||
requireAdminRoles(['admin', 'operator']),
|
||||
createJsonHandler(
|
||||
(req) =>
|
||||
resendAdminTaskKuaishouIndustryVoucherCode(getTaskId(req), req.adminSession || null),
|
||||
{
|
||||
successMessage: '电子凭证发码回调已重发',
|
||||
errorMessage: '重发电子凭证发码回调失败',
|
||||
scope: '[admin/tasks/:taskId/kuaishou-industry/resend-code]',
|
||||
audit: (req, data) => buildTaskAudit('task_kuaishou_industry_resend_code', req, data),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/tasks/:taskId/kuaishou-industry/consume',
|
||||
requireAdminRoles(['admin', 'operator']),
|
||||
createJsonHandler(
|
||||
(req) =>
|
||||
consumeAdminTaskKuaishouIndustryVoucher(
|
||||
getTaskId(req),
|
||||
req.body as AdminTaskKuaishouIndustryConsumeRouteBody,
|
||||
req.adminSession || null,
|
||||
),
|
||||
{
|
||||
successMessage: '电子凭证已执行核销',
|
||||
errorMessage: '执行电子凭证核销失败',
|
||||
scope: '[admin/tasks/:taskId/kuaishou-industry/consume]',
|
||||
audit: (req, data) => buildTaskAudit('task_kuaishou_industry_consume', req, data),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
function buildTaskAudit(action: string, req: Request, data: unknown) {
|
||||
const result = data as AdminTaskActionResponse
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ import { listOrderItemsByOrderId } from '../../repositories/order-item-repo.js'
|
||||
import { getOrderById, listOrders } from '../../repositories/order-repo.js'
|
||||
import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js'
|
||||
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
|
||||
import {
|
||||
listKuaishouIndustryVouchersByOid,
|
||||
listKuaishouIndustryVouchersByTaskId,
|
||||
} from '../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import { listCloudtentaclesSources } from '../platforms/cloudtentacles/source-config-service.js'
|
||||
import { resolveKuaishouFeifeiClaimUrl } from '../fulfillment/kuaishou-feifei/index.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
@@ -83,7 +87,10 @@ export async function getAdminOrders(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAdminOrderDetail(orderId: AdminEntityIdInput): Promise<JsonRecord> {
|
||||
export async function getAdminOrderDetail(
|
||||
orderId: AdminEntityIdInput,
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<JsonRecord> {
|
||||
const order = await getOrderById(Number(orderId))
|
||||
|
||||
if (!order) {
|
||||
@@ -97,7 +104,9 @@ export async function getAdminOrderDetail(orderId: AdminEntityIdInput): Promise<
|
||||
listOrderItemsByOrderId(order.id),
|
||||
listTasksByOrderId(order.id),
|
||||
])
|
||||
const kuaishouIndustryVouchers = await listKuaishouIndustryVouchersByOid(order.platform_order_id)
|
||||
const itemSummary = summarizeOrderItems(items)
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
|
||||
return {
|
||||
order: {
|
||||
@@ -138,6 +147,37 @@ export async function getAdminOrderDetail(orderId: AdminEntityIdInput): Promise<
|
||||
: JSON.parse(String(item.spec_json || '{}')),
|
||||
})),
|
||||
tasks: tasks.map((task) => mapAdminTaskSummary(task)),
|
||||
kuaishouIndustryVouchers: kuaishouIndustryVouchers.map(mapAdminKuaishouIndustryVoucher),
|
||||
operations: {
|
||||
canResendKuaishouIndustryVoucherCode:
|
||||
viewerContext.canManageTaskLifecycle && kuaishouIndustryVouchers.length > 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function mapAdminKuaishouIndustryVoucher(voucher: {
|
||||
voucher_code?: unknown
|
||||
oid?: unknown
|
||||
unit_index?: unknown
|
||||
status?: unknown
|
||||
send_callback_status?: unknown
|
||||
send_callback_attempt_count?: unknown
|
||||
send_callback_last_error?: unknown
|
||||
send_callback_sent_at?: unknown
|
||||
consumed_at?: unknown
|
||||
destroyed_at?: unknown
|
||||
}) {
|
||||
return {
|
||||
oid: String(voucher.oid || '').trim(),
|
||||
voucherCode: String(voucher.voucher_code || '').trim(),
|
||||
unitIndex: Number(voucher.unit_index || 0) || 0,
|
||||
status: String(voucher.status || '').trim(),
|
||||
sendCallbackStatus: String(voucher.send_callback_status || '').trim(),
|
||||
sendCallbackAttemptCount: Number(voucher.send_callback_attempt_count || 0) || 0,
|
||||
sendCallbackLastError: String(voucher.send_callback_last_error || '').trim(),
|
||||
sendCallbackSentAt: voucher.send_callback_sent_at || null,
|
||||
consumedAt: voucher.consumed_at || null,
|
||||
destroyedAt: voucher.destroyed_at || null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,10 +220,11 @@ export async function getAdminTaskDetail(
|
||||
}
|
||||
|
||||
const primaryClaimTokenId = getTaskPrimaryClaimTokenId(task)
|
||||
const [order, claimToken, taskEvents] = await Promise.all([
|
||||
const [order, claimToken, taskEvents, taskKuaishouIndustryVouchers] = await Promise.all([
|
||||
getOrderById(task.order_id),
|
||||
primaryClaimTokenId ? getClaimTokenById(primaryClaimTokenId) : Promise.resolve(null),
|
||||
listTaskEventsByTaskId(task.id),
|
||||
listKuaishouIndustryVouchersByTaskId(task.id),
|
||||
])
|
||||
const orderItems = order ? await listOrderItemsByOrderId(order.id) : []
|
||||
const orderItem = orderItems.find((item) => item.id === task.order_item_id) || null
|
||||
@@ -200,6 +241,18 @@ export async function getAdminTaskDetail(
|
||||
mapKuaishouCloudFulfillmentContext(taskContext.kuaishouCloudFulfillment, {
|
||||
cloudSourceLabelMap,
|
||||
}) || mapKuaishouCloudTaskStateProjection(task, { cloudSourceLabelMap })
|
||||
const firstKuaishouIndustryVoucher = taskKuaishouIndustryVouchers[0] || null
|
||||
const kuaishouIndustryVoucher =
|
||||
firstKuaishouIndustryVoucher
|
||||
? mapAdminKuaishouIndustryVoucher(firstKuaishouIndustryVoucher)
|
||||
: mapKuaishouIndustryVoucherContext(taskContext.kuaishouIndustryVoucher)
|
||||
const hasKuaishouIndustryVoucher = Boolean(
|
||||
kuaishouIndustryVoucher && String(kuaishouIndustryVoucher.voucherCode || '').trim(),
|
||||
)
|
||||
const kuaishouIndustryVoucherStatus =
|
||||
String(kuaishouIndustryVoucher?.status || '').trim().toUpperCase()
|
||||
const kuaishouIndustryVoucherSendCallbackStatus =
|
||||
String(kuaishouIndustryVoucher?.sendCallbackStatus || '').trim().toLowerCase()
|
||||
|
||||
return {
|
||||
task: mapAdminTaskListItem(
|
||||
@@ -250,6 +303,7 @@ export async function getAdminTaskDetail(
|
||||
},
|
||||
redeemResolution: mapRedeemResolutionContext(taskContext.redeemResolution),
|
||||
kuaishouCloudFulfillment,
|
||||
kuaishouIndustryVoucher,
|
||||
manualDispatch: mapManualDispatchContext(taskContext.manualDispatch, viewerContext),
|
||||
events: taskEvents.map(mapAdminTaskEvent),
|
||||
operations: {
|
||||
@@ -289,11 +343,38 @@ export async function getAdminTaskDetail(
|
||||
viewerContext.canManageTaskLifecycle &&
|
||||
String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
|
||||
canReturnKuaishouCloudFulfillmentStatus(task.task_status),
|
||||
canResendKuaishouIndustryVoucherCode:
|
||||
viewerContext.canManageTaskLifecycle && hasKuaishouIndustryVoucher,
|
||||
canConsumeKuaishouIndustryVoucher:
|
||||
viewerContext.canManageTaskLifecycle &&
|
||||
hasKuaishouIndustryVoucher &&
|
||||
kuaishouIndustryVoucherStatus !== 'CONSUMED' &&
|
||||
kuaishouIndustryVoucherSendCallbackStatus === 'success',
|
||||
canViewSensitiveTaskData: viewerContext.canViewSensitiveTaskData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function mapKuaishouIndustryVoucherContext(value: unknown): JsonRecord | null {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const record = value as JsonRecord
|
||||
return {
|
||||
oid: String(record.oid || '').trim(),
|
||||
voucherCode: String(record.voucherCode || record.eticketId || '').trim(),
|
||||
unitIndex: Number(record.unitIndex || 0) || 0,
|
||||
status: String(record.status || '').trim(),
|
||||
sendCallbackStatus: String(record.sendCallbackStatus || '').trim(),
|
||||
sendCallbackLastError: String(record.sendCallbackLastError || '').trim(),
|
||||
sendCallbackAttemptCount: Number(record.sendCallbackAttemptCount || 0) || 0,
|
||||
sendCallbackSentAt: record.sendCallbackSentAt || null,
|
||||
consumedAt: record.consumedAt || null,
|
||||
destroyedAt: record.destroyedAt || null,
|
||||
}
|
||||
}
|
||||
|
||||
function buildCloudSourceLabelMap() {
|
||||
const labelMap = new Map<string, string>()
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { getOrderById } from '../../../repositories/order-repo.js'
|
||||
import { updateTask } from '../../../repositories/task-repo.js'
|
||||
import { listTasksByOrderId, updateTask } from '../../../repositories/task-repo.js'
|
||||
import { createTaskEvent } from '../../../repositories/task-event-repo.js'
|
||||
import {
|
||||
findKuaishouIndustryVoucherByCode,
|
||||
listKuaishouIndustryVouchersByOid,
|
||||
listKuaishouIndustryVouchersByTaskId,
|
||||
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import {
|
||||
buyCloudtentaclesSku,
|
||||
getCloudtentaclesAsset,
|
||||
@@ -14,6 +19,12 @@ import {
|
||||
} from '../../platforms/cloudtentacles/virtual-number-service.js'
|
||||
import { consumeKuaishouEticket } from '../../platforms/kuaishou-eticket/consume-service.js'
|
||||
import { isKuaishouEticketMockTicketCode } from '../../platforms/kuaishou-eticket/mock-ticket-service.js'
|
||||
import { resendKuaishouIndustryVoucherSendCallback } from '../../platforms/kuaishou-industry/send-code-service.js'
|
||||
import { consumeKuaishouIndustryVoucher } from '../../platforms/kuaishou-industry/voucher-service.js'
|
||||
import {
|
||||
attachKuaishouIndustryVoucherToTask,
|
||||
bindKuaishouIndustryVouchersToOrderTasks,
|
||||
} from '../../platforms/kuaishou-industry/voucher-binding-service.js'
|
||||
import {
|
||||
getKuaishouEticketSourceConfig,
|
||||
resolveKuaishouEticketShopConfig,
|
||||
@@ -46,8 +57,12 @@ import type {
|
||||
AdminEntityIdInput,
|
||||
AdminViewerSessionInput,
|
||||
} from '../../../types/admin/read-inputs.js'
|
||||
import type { AdminTaskKuaishouCloudDispatchInput } from '../../../types/admin/write-inputs.js'
|
||||
import type {
|
||||
AdminTaskKuaishouCloudDispatchInput,
|
||||
AdminTaskKuaishouIndustryConsumeInput,
|
||||
} from '../../../types/admin/write-inputs.js'
|
||||
import type { AdminTaskActionResponse } from '../../../types/admin/write-models.js'
|
||||
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
export async function prepareAdminTaskKuaishouCloudFulfillment(
|
||||
taskId: AdminEntityIdInput,
|
||||
@@ -675,3 +690,306 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
|
||||
task: mapTaskActionPayload(updatedTask),
|
||||
}
|
||||
}
|
||||
|
||||
export async function resendAdminTaskKuaishouIndustryVoucherCode(
|
||||
taskId: AdminEntityIdInput,
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<AdminTaskActionResponse> {
|
||||
const task = await getRequiredTask(taskId)
|
||||
const now = nowIso()
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
|
||||
if (!viewerContext.canManageTaskLifecycle) {
|
||||
throw createHttpError('当前账号没有权限重发电子凭证发码回调', {
|
||||
statusCode: 403,
|
||||
errorCode: 'admin_task_kuaishou_industry_resend_forbidden',
|
||||
})
|
||||
}
|
||||
|
||||
const voucher = await getRequiredIndustryVoucherForTask(task)
|
||||
const order = await getOrderById(task.order_id)
|
||||
const result = await resendKuaishouIndustryVoucherSendCallback({
|
||||
voucherCode: voucher.voucher_code,
|
||||
oid: voucher.oid,
|
||||
preferredTotalGoodsValue: Number(order?.total_amount || 0) || 0,
|
||||
})
|
||||
const updatedVoucher = result.voucher || voucher
|
||||
|
||||
if (!result.success) {
|
||||
const message = String(result.error || '电子凭证发码回调重发失败').trim()
|
||||
const failedTask = await updateTask(task.id, {
|
||||
task_status: TASK_STATUS.MANUAL_REVIEW,
|
||||
result_code: 'kuaishou_industry_send_callback_resend_failed',
|
||||
result_message: message,
|
||||
last_error: message,
|
||||
updated_at: now,
|
||||
})
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_industry_send_callback_resend_failed',
|
||||
{
|
||||
oid: voucher.oid,
|
||||
voucherCode: voucher.voucher_code,
|
||||
errorMessage: message,
|
||||
actor: buildAdminActionActor(session),
|
||||
},
|
||||
now,
|
||||
)
|
||||
void failedTask
|
||||
throw createHttpError(message, {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_kuaishou_industry_resend_failed',
|
||||
})
|
||||
}
|
||||
|
||||
const attachedTask = await attachKuaishouIndustryVoucherToTask(task, updatedVoucher, {
|
||||
source: 'admin_resend_kuaishou_industry_code',
|
||||
now,
|
||||
})
|
||||
const resendTargetTask = attachedTask || task
|
||||
const refreshedTask = await updateTask(resendTargetTask.id, {
|
||||
result_code: 'kuaishou_industry_send_callback_resent',
|
||||
result_message: '电子凭证发码回调已重发成功',
|
||||
last_error: '',
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_industry_send_callback_resent',
|
||||
{
|
||||
oid: voucher.oid,
|
||||
voucherCode: voucher.voucher_code,
|
||||
actor: buildAdminActionActor(session),
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
return { task: mapTaskActionPayload(refreshedTask || resendTargetTask) }
|
||||
}
|
||||
|
||||
export async function resendAdminOrderKuaishouIndustryVoucherCodes(
|
||||
orderId: AdminEntityIdInput,
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<{ success: boolean; resentCount: number; failedCount: number; errorMessage: string }> {
|
||||
const order = await getOrderById(Number(orderId))
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
const now = nowIso()
|
||||
|
||||
if (!viewerContext.canManageTaskLifecycle) {
|
||||
throw createHttpError('当前账号没有权限重发电子凭证发码回调', {
|
||||
statusCode: 403,
|
||||
errorCode: 'admin_order_kuaishou_industry_resend_forbidden',
|
||||
})
|
||||
}
|
||||
|
||||
if (!order) {
|
||||
throw createHttpError('订单不存在', {
|
||||
statusCode: 404,
|
||||
errorCode: 'admin_order_not_found',
|
||||
})
|
||||
}
|
||||
|
||||
const vouchers = await resolveIndustryVouchersForOrder(order.platform_order_id)
|
||||
if (vouchers.length === 0) {
|
||||
throw createHttpError('当前订单没有关联电子凭证,无法重发发码回调', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_order_kuaishou_industry_voucher_missing',
|
||||
})
|
||||
}
|
||||
|
||||
let resentCount = 0
|
||||
let failedCount = 0
|
||||
let errorMessage = ''
|
||||
|
||||
for (const voucher of vouchers) {
|
||||
const result = await resendKuaishouIndustryVoucherSendCallback({
|
||||
voucherCode: voucher.voucher_code,
|
||||
oid: voucher.oid,
|
||||
preferredTotalGoodsValue: Number(order.total_amount || 0) || 0,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
resentCount += 1
|
||||
continue
|
||||
}
|
||||
|
||||
failedCount += 1
|
||||
errorMessage = errorMessage || String(result.error || '电子凭证发码回调重发失败').trim()
|
||||
}
|
||||
|
||||
if (failedCount > 0) {
|
||||
throw createHttpError(errorMessage || '电子凭证发码回调重发失败', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_order_kuaishou_industry_resend_failed',
|
||||
context: { resentCount, failedCount },
|
||||
})
|
||||
}
|
||||
|
||||
const tasks = await listTasksByOrderId(order.id)
|
||||
await bindKuaishouIndustryVouchersToOrderTasks(order, tasks, {
|
||||
source: 'admin_order_resend_kuaishou_industry_code',
|
||||
now,
|
||||
})
|
||||
|
||||
return {
|
||||
success: failedCount === 0,
|
||||
resentCount,
|
||||
failedCount,
|
||||
errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
export async function consumeAdminTaskKuaishouIndustryVoucher(
|
||||
taskId: AdminEntityIdInput,
|
||||
payload: AdminTaskKuaishouIndustryConsumeInput = {},
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<AdminTaskActionResponse> {
|
||||
const task = await getRequiredTask(taskId)
|
||||
const now = nowIso()
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
|
||||
if (!viewerContext.canManageTaskLifecycle) {
|
||||
throw createHttpError('当前账号没有权限执行电子凭证核销', {
|
||||
statusCode: 403,
|
||||
errorCode: 'admin_task_kuaishou_industry_consume_forbidden',
|
||||
})
|
||||
}
|
||||
|
||||
const voucher = await getRequiredIndustryVoucherForTask(task)
|
||||
const result = await consumeKuaishouIndustryVoucher(voucher, {
|
||||
source: 'admin_manual_consume',
|
||||
task,
|
||||
token: voucher.token,
|
||||
consumeType: String(payload.consumeType || 'delivery').trim() || 'delivery',
|
||||
consumeTime: Date.now(),
|
||||
...(payload.storeName ? { storeName: payload.storeName } : {}),
|
||||
...(payload.storeAddress ? { storeAddress: payload.storeAddress } : {}),
|
||||
...(payload.expressCode ? { expressCode: payload.expressCode } : {}),
|
||||
...(payload.expressNo ? { expressNo: payload.expressNo } : {}),
|
||||
})
|
||||
|
||||
if (!result.ok || !result.voucher) {
|
||||
const message = String(result.errorMessage || '电子凭证核销失败').trim()
|
||||
const failedTask = await updateTask(task.id, {
|
||||
task_status: TASK_STATUS.MANUAL_REVIEW,
|
||||
result_code: 'kuaishou_industry_manual_consume_failed',
|
||||
result_message: message,
|
||||
last_error: message,
|
||||
updated_at: now,
|
||||
})
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_industry_manual_consume_failed',
|
||||
{
|
||||
oid: voucher.oid,
|
||||
voucherCode: voucher.voucher_code,
|
||||
errorMessage: message,
|
||||
actor: buildAdminActionActor(session),
|
||||
},
|
||||
now,
|
||||
)
|
||||
void failedTask
|
||||
throw createHttpError(message, {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_kuaishou_industry_consume_failed',
|
||||
})
|
||||
}
|
||||
|
||||
const attachedConsumeTask = await attachKuaishouIndustryVoucherToTask(task, result.voucher, {
|
||||
source: 'admin_manual_consume',
|
||||
now,
|
||||
})
|
||||
const consumeTargetTask = attachedConsumeTask || task
|
||||
const taskContext = parseTaskContext(consumeTargetTask)
|
||||
const hasCloudFulfillmentContext = Boolean(
|
||||
taskContext.kuaishouCloudFulfillment &&
|
||||
typeof taskContext.kuaishouCloudFulfillment === 'object' &&
|
||||
!Array.isArray(taskContext.kuaishouCloudFulfillment),
|
||||
)
|
||||
const flow = hasCloudFulfillmentContext
|
||||
? normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
: null
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
...(flow
|
||||
? {
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
consume: {
|
||||
...flow.consume,
|
||||
status: 'success',
|
||||
consumedAt: result.voucher.consumed_at || now,
|
||||
errorMessage: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
const updatedTask = await updateTask(consumeTargetTask.id, {
|
||||
task_status: TASK_STATUS.COMPLETED,
|
||||
delivery_status: 'delivered',
|
||||
result_code: 'kuaishou_industry_manual_consumed',
|
||||
result_message: '电子凭证已手动核销完成',
|
||||
redeemed_at: result.voucher.consumed_at || now,
|
||||
last_error: '',
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_industry_manual_consumed',
|
||||
{
|
||||
oid: result.voucher.oid,
|
||||
voucherCode: result.voucher.voucher_code,
|
||||
consumeSerialNum: result.voucher.consume_serial_num,
|
||||
actor: buildAdminActionActor(session),
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
return { task: mapTaskActionPayload(updatedTask || consumeTargetTask) }
|
||||
}
|
||||
|
||||
async function getRequiredIndustryVoucherForTask(task: TaskRow): Promise<KuaishouIndustryVoucherRow> {
|
||||
const vouchers = await listKuaishouIndustryVouchersByTaskId(task.id)
|
||||
const firstVoucher = vouchers[0]
|
||||
if (firstVoucher) {
|
||||
return firstVoucher
|
||||
}
|
||||
|
||||
const taskContext = parseTaskContext(task)
|
||||
const voucherContext = taskContext.kuaishouIndustryVoucher || {}
|
||||
const voucherCode = String(voucherContext.voucherCode || voucherContext.eticketId || '').trim()
|
||||
if (voucherCode) {
|
||||
const voucher = await findKuaishouIndustryVoucherByCode(voucherCode, task.platform_order_id)
|
||||
if (voucher) {
|
||||
return voucher
|
||||
}
|
||||
}
|
||||
|
||||
throw createHttpError('当前任务没有关联电子凭证,无法执行该操作', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_kuaishou_industry_voucher_missing',
|
||||
})
|
||||
}
|
||||
|
||||
async function resolveIndustryVouchersForOrder(platformOrderId: string): Promise<KuaishouIndustryVoucherRow[]> {
|
||||
const normalizedOid = String(platformOrderId || '').trim()
|
||||
if (!normalizedOid) {
|
||||
return []
|
||||
}
|
||||
|
||||
return listKuaishouIndustryVouchersByOid(normalizedOid)
|
||||
}
|
||||
|
||||
function buildAdminActionActor(session: AdminViewerSessionInput | null) {
|
||||
return session
|
||||
? {
|
||||
userId: Number(session.userId || 0) || 0,
|
||||
username: String(session.username || '').trim(),
|
||||
role: String(session.role || '').trim(),
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { findLatestOrderByPlatformOrderId } from '../../../repositories/order-repo.js'
|
||||
import { listTasksByOrderId } from '../../../repositories/task-repo.js'
|
||||
import {
|
||||
findKuaishouIndustryVoucherByCode,
|
||||
updateKuaishouIndustryVoucherByCode,
|
||||
upsertKuaishouIndustryVoucher,
|
||||
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
@@ -31,6 +32,15 @@ import type { KuaishouIndustryVoucherRow, OrderRow } from '../../../types/reposi
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
type SendCodeCallbackParams = {
|
||||
oid: string
|
||||
sendType: string
|
||||
sellerId: string
|
||||
token: string
|
||||
eticketType?: string
|
||||
ext?: string
|
||||
}
|
||||
|
||||
export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
const config = getKuaishouIndustryConfig()
|
||||
|
||||
@@ -125,6 +135,47 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
)
|
||||
}
|
||||
|
||||
export async function resendKuaishouIndustryVoucherSendCallback(input: {
|
||||
voucherCode: string
|
||||
oid?: string
|
||||
preferredTotalGoodsValue?: number
|
||||
}) {
|
||||
const voucher = await findKuaishouIndustryVoucherByCode(input.voucherCode, input.oid || '')
|
||||
if (!voucher) {
|
||||
return {
|
||||
success: false,
|
||||
error: '电子凭证不存在,无法重发发码回调',
|
||||
voucher: null,
|
||||
}
|
||||
}
|
||||
|
||||
const rawPayload = parseJsonObject(voucher.raw_payload_json)
|
||||
const body = parseJsonObject(rawPayload.body)
|
||||
const params = resolveSendCodeCallbackParams(voucher, body)
|
||||
const eticketType = String(params.eticketType || '').trim()
|
||||
const eticket = buildKuaishouIndustryEticketFromVoucher(voucher, eticketType)
|
||||
const result = await sendAndRecordCallback({
|
||||
oid: params.oid,
|
||||
sendType: params.sendType,
|
||||
etickets: [eticket],
|
||||
vouchers: [voucher],
|
||||
sellerId: params.sellerId,
|
||||
token: params.token,
|
||||
eticketType,
|
||||
source: 'admin_resend',
|
||||
...(input.preferredTotalGoodsValue !== undefined
|
||||
? { preferredTotalGoodsValue: input.preferredTotalGoodsValue }
|
||||
: {}),
|
||||
...(params.ext ? { ext: params.ext } : {}),
|
||||
})
|
||||
const updatedVoucher = await findKuaishouIndustryVoucherByCode(voucher.voucher_code, voucher.oid)
|
||||
|
||||
return {
|
||||
...result,
|
||||
voucher: updatedVoucher || voucher,
|
||||
}
|
||||
}
|
||||
|
||||
async function sendAndRecordCallback(input: {
|
||||
oid: string
|
||||
sendType: string
|
||||
@@ -135,6 +186,7 @@ async function sendAndRecordCallback(input: {
|
||||
eticketType?: string
|
||||
ext?: string
|
||||
preferredTotalGoodsValue?: number
|
||||
source?: string
|
||||
}) {
|
||||
const eticketItems = input.etickets.map((e) => ({
|
||||
id: String(e.id || ''),
|
||||
@@ -156,6 +208,7 @@ async function sendAndRecordCallback(input: {
|
||||
const totalGoodsValue = goodsValuePlan.totalGoodsValue
|
||||
|
||||
logIntegration('[kuaishou-industry/send-code]', '准备发起电子凭证发货回调', {
|
||||
source: input.source || 'send_code',
|
||||
oid: input.oid,
|
||||
sellerId: input.sellerId,
|
||||
sendType: input.sendType,
|
||||
@@ -202,6 +255,7 @@ async function sendAndRecordCallback(input: {
|
||||
|
||||
if (!result.success) {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调执行失败,发码请求已拒绝继续', {
|
||||
source: input.source || 'send_code',
|
||||
oid: input.oid,
|
||||
error: result.error || '',
|
||||
response: result.response || null,
|
||||
@@ -210,6 +264,7 @@ async function sendAndRecordCallback(input: {
|
||||
}
|
||||
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调执行完成', {
|
||||
source: input.source || 'send_code',
|
||||
oid: input.oid,
|
||||
response: result.response || null,
|
||||
})
|
||||
@@ -217,6 +272,20 @@ async function sendAndRecordCallback(input: {
|
||||
return result
|
||||
}
|
||||
|
||||
function resolveSendCodeCallbackParams(
|
||||
voucher: KuaishouIndustryVoucherRow,
|
||||
body: JsonObject,
|
||||
): SendCodeCallbackParams {
|
||||
return {
|
||||
oid: String(body.oid || voucher.oid || '').trim(),
|
||||
sendType: String(body.sendType || 'VIRTUAL').trim() || 'VIRTUAL',
|
||||
sellerId: String(body.sellerId || voucher.seller_id || '').trim(),
|
||||
token: String(body.token || voucher.token || '').trim(),
|
||||
eticketType: String(body.eticketType || '').trim(),
|
||||
ext: String(body.ext || '').trim(),
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSendCallbackFailureMessage(result: Awaited<ReturnType<typeof sendCallback>>) {
|
||||
const error = String(result.error || '').trim()
|
||||
if (error) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import type {
|
||||
AdminNotificationConfigInput,
|
||||
AdminNotificationTestInput,
|
||||
AdminScheduledJobsConfigInput,
|
||||
AdminTaskKuaishouIndustryConsumeInput,
|
||||
AdminTaskKuaishouCloudDispatchInput,
|
||||
AdminTaskManualDispatchInput,
|
||||
} from './write-inputs.js'
|
||||
@@ -54,6 +55,7 @@ export type AdminCloudtentaclesVirtualNumberRouteBody = AdminCloudtentaclesVirtu
|
||||
export type AdminCloudtentaclesFullFlowRouteBody = AdminCloudtentaclesFullFlowInput
|
||||
export type AdminTaskManualDispatchRouteBody = AdminTaskManualDispatchInput
|
||||
export type AdminTaskKuaishouCloudDispatchRouteBody = AdminTaskKuaishouCloudDispatchInput
|
||||
export type AdminTaskKuaishouIndustryConsumeRouteBody = AdminTaskKuaishouIndustryConsumeInput
|
||||
|
||||
export type AdminOrderRouteParams = { orderId?: string }
|
||||
export type AdminTaskRouteParams = { taskId?: string, bindingId?: string }
|
||||
|
||||
@@ -277,3 +277,11 @@ export type AdminTaskManualDispatchInput = {
|
||||
export type AdminTaskKuaishouCloudDispatchInput = {
|
||||
ticketCode?: string
|
||||
}
|
||||
|
||||
export type AdminTaskKuaishouIndustryConsumeInput = {
|
||||
consumeType?: string
|
||||
storeName?: string
|
||||
storeAddress?: string
|
||||
expressCode?: string
|
||||
expressNo?: string
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user