重构 lewan 履约:拆分发货模块并统一 registry 入口
将 task-finalization 拆为 dispatch/stock/return/redeem/confirm 等用例, claim 与 admin 经 executor registry 调用;admin 退号仅清理虚拟号不核销。
This commit is contained in:
@@ -7,10 +7,6 @@ import {
|
||||
listKuaishouIndustryVouchersByOid,
|
||||
listKuaishouIndustryVouchersByTaskId,
|
||||
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import {
|
||||
backCloudtentaclesVirtualNumber,
|
||||
getCloudtentaclesBindInfo,
|
||||
} from '../../platforms/cloudtentacles/virtual-number-service.js'
|
||||
import { resendKuaishouIndustryVoucherSendCallback } from '../../platforms/kuaishou-industry/send-code-service.js'
|
||||
import { consumeKuaishouIndustryVoucher } from '../../platforms/kuaishou-industry/voucher-service.js'
|
||||
import {
|
||||
@@ -22,17 +18,16 @@ import { nowIso } from '../../../utils/time.js'
|
||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||
import { createAdminViewerContext, parseTaskContext } from '../admin-read-shared-helpers.js'
|
||||
import { getRequiredTask, mapTaskActionPayload } from '../admin-task-read-helpers.js'
|
||||
import { maskCode, maskPhone } from '../write-helpers.js'
|
||||
import {
|
||||
dispatchKuaishouCloudFulfillmentTask,
|
||||
prepareKuaishouCloudFulfillmentTask,
|
||||
rebindKuaishouCloudTaskRole,
|
||||
} from '../../fulfillment/kuaishou-cloud/index.js'
|
||||
dispatchFulfillmentTask,
|
||||
prepareFulfillmentBinding,
|
||||
rebindFulfillmentRole,
|
||||
refreshFulfillmentRole,
|
||||
returnFulfillmentNumber,
|
||||
} from '../../fulfillment/executors/registry.js'
|
||||
import {
|
||||
isKuaishouCloudTask,
|
||||
normalizeKuaishouCloudFlow,
|
||||
normalizeKuaishouCloudRoleInfo,
|
||||
resolvePersistedCloudtentaclesContext,
|
||||
} from './kuaishou-cloud-helpers.js'
|
||||
|
||||
import type {
|
||||
@@ -66,15 +61,21 @@ export async function prepareAdminTaskKuaishouCloudFulfillment(
|
||||
})
|
||||
}
|
||||
|
||||
const result = await prepareKuaishouCloudFulfillmentTask(task, {
|
||||
const result = await prepareFulfillmentBinding(task, {
|
||||
source: 'admin_task_prepare',
|
||||
actor: buildAdminActionActor(session),
|
||||
})
|
||||
if (!result?.task) {
|
||||
throw createHttpError('当前任务不支持准备绑定资源', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_prepare_not_supported',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
task: mapTaskActionPayload(result.task),
|
||||
claimUrl: result.claimUrl,
|
||||
token: result.token,
|
||||
claimUrl: String(result.claimUrl || ''),
|
||||
token: String(result.token || ''),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,11 +100,17 @@ export async function dispatchAdminTaskKuaishouCloudFulfillment(
|
||||
})
|
||||
}
|
||||
|
||||
const result = await dispatchKuaishouCloudFulfillmentTask(task, {
|
||||
const result = await dispatchFulfillmentTask(task, {
|
||||
source: 'admin_task_dispatch',
|
||||
actor: buildAdminActionActor(session),
|
||||
autoFinalize: false,
|
||||
})
|
||||
if (!result?.task) {
|
||||
throw createHttpError('当前任务不支持 executor 发货', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_dispatch_not_supported',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
task: mapTaskActionPayload(result.task),
|
||||
@@ -115,7 +122,6 @@ export async function refreshAdminTaskKuaishouCloudRoleInfo(
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<AdminTaskActionResponse> {
|
||||
const task = await getRequiredTask(taskId)
|
||||
const now = nowIso()
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
|
||||
if (!viewerContext.canOperateAssistedTask) {
|
||||
@@ -132,74 +138,21 @@ export async function refreshAdminTaskKuaishouCloudRoleInfo(
|
||||
})
|
||||
}
|
||||
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
||||
throw createHttpError('当前任务还没有可查询的绑定角色信息,请先准备绑定资源', {
|
||||
const result = await refreshFulfillmentRole(task, {
|
||||
source: 'admin_task_refresh_role',
|
||||
actor: buildAdminActionActor(session),
|
||||
forceProbe: true,
|
||||
recordEvent: true,
|
||||
})
|
||||
if (!result?.task) {
|
||||
throw createHttpError('当前任务不支持刷新角色信息', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_kuaishou_cloud_missing_bind_info_context',
|
||||
errorCode: 'admin_task_refresh_not_supported',
|
||||
})
|
||||
}
|
||||
|
||||
const cloudContext = resolvePersistedCloudtentaclesContext([
|
||||
flow.binding.resolvedSourceKey,
|
||||
...flow.binding.cloudSourceKeys,
|
||||
])
|
||||
const bindInfoResult = await getCloudtentaclesBindInfo({
|
||||
...cloudContext,
|
||||
key: flow.binding.vnKey,
|
||||
id: flow.binding.vnId,
|
||||
})
|
||||
|
||||
const bindInfo = normalizeKuaishouCloudRoleInfo(bindInfoResult.bindInfo)
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
binding: {
|
||||
...flow.binding,
|
||||
roleName: bindInfo.name,
|
||||
roleId: bindInfo.rid,
|
||||
},
|
||||
role: {
|
||||
status: bindInfo.name || bindInfo.rid ? 'ready' : 'pending',
|
||||
name: bindInfo.name,
|
||||
rid: bindInfo.rid,
|
||||
refreshedAt: now,
|
||||
errorMessage:
|
||||
bindInfo.name || bindInfo.rid ? '' : '当前还没有查询到角色信息,请让客户完成绑定后再刷新',
|
||||
rawInfo: bindInfo.rawInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
role_id: bindInfo.rid || '',
|
||||
role_name: bindInfo.name || '',
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_cloud_role_info_refreshed',
|
||||
{
|
||||
roleName: bindInfo.name,
|
||||
roleId: bindInfo.rid,
|
||||
vnId: flow.binding.vnId,
|
||||
refreshedBy: session
|
||||
? {
|
||||
userId: Number(session.userId || 0) || 0,
|
||||
username: String(session.username || '').trim(),
|
||||
role: String(session.role || '').trim(),
|
||||
}
|
||||
: null,
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
return {
|
||||
task: mapTaskActionPayload(updatedTask),
|
||||
task: mapTaskActionPayload(result.task),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,21 +171,21 @@ export async function rebindAdminTaskKuaishouCloudRole(
|
||||
})
|
||||
}
|
||||
|
||||
const result = await rebindKuaishouCloudTaskRole(task, {
|
||||
const result = await rebindFulfillmentRole(task, {
|
||||
source: 'admin_task_rebind_role',
|
||||
actor: session
|
||||
? {
|
||||
userId: Number(session.userId || 0) || 0,
|
||||
username: String(session.username || '').trim(),
|
||||
role: String(session.role || '').trim(),
|
||||
}
|
||||
: null,
|
||||
actor: buildAdminActionActor(session),
|
||||
})
|
||||
if (!result?.task) {
|
||||
throw createHttpError('当前任务不支持换绑角色', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_rebind_not_supported',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
task: mapTaskActionPayload(result.task),
|
||||
claimUrl: result.claimUrl,
|
||||
token: result.token,
|
||||
claimUrl: String(result.claimUrl || ''),
|
||||
token: String(result.token || ''),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +194,6 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
|
||||
session: AdminViewerSessionInput | null = null,
|
||||
): Promise<AdminTaskActionResponse> {
|
||||
const task = await getRequiredTask(taskId)
|
||||
const now = nowIso()
|
||||
const viewerContext = createAdminViewerContext(session)
|
||||
|
||||
if (!viewerContext.canManageTaskLifecycle) {
|
||||
@@ -258,108 +210,21 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
|
||||
})
|
||||
}
|
||||
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
const cloudContext = resolvePersistedCloudtentaclesContext([
|
||||
flow.binding.resolvedSourceKey,
|
||||
...flow.binding.cloudSourceKeys,
|
||||
])
|
||||
|
||||
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
||||
throw createHttpError('当前任务缺少可退还的虚拟号信息', {
|
||||
// admin 退号 = 清理虚拟号占位,不核销电子凭证
|
||||
const result = await returnFulfillmentNumber(task, {
|
||||
source: 'admin_task_return_number',
|
||||
actor: buildAdminActionActor(session),
|
||||
consumeIndustryVoucher: false,
|
||||
})
|
||||
if (!result?.task) {
|
||||
throw createHttpError('当前任务不支持退还号码', {
|
||||
statusCode: 409,
|
||||
errorCode: 'admin_task_kuaishou_cloud_missing_return_context',
|
||||
errorCode: 'admin_task_return_not_supported',
|
||||
})
|
||||
}
|
||||
|
||||
await backCloudtentaclesVirtualNumber({
|
||||
...cloudContext,
|
||||
key: flow.binding.vnKey,
|
||||
id: flow.binding.vnId,
|
||||
})
|
||||
|
||||
const ticketCode = String(flow.ticket.code || '').trim()
|
||||
const consumeAlreadyCompleted = flow.consume.status === 'success'
|
||||
const consumeStatus = consumeAlreadyCompleted ? 'success' : 'skipped'
|
||||
let consumeErrorMessage = ''
|
||||
const consumedAt = consumeAlreadyCompleted ? flow.consume.consumedAt || now : null
|
||||
const nextTaskStatus = 'completed'
|
||||
const nextResultCode = consumeAlreadyCompleted
|
||||
? 'kuaishou_cloud_completed'
|
||||
: 'kuaishou_cloud_completed_without_eticket_consume'
|
||||
const nextResultMessage = consumeAlreadyCompleted
|
||||
? 'cloudtentacles 发货、退号并完成电子凭证核销'
|
||||
: 'cloudtentacles 发货、退号并收口'
|
||||
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
returnNumber: {
|
||||
...flow.returnNumber,
|
||||
status: 'success',
|
||||
returnedAt: now,
|
||||
returnedBy: session
|
||||
? {
|
||||
userId: Number(session.userId || 0) || 0,
|
||||
username: String(session.username || '').trim(),
|
||||
role: String(session.role || '').trim(),
|
||||
}
|
||||
: null,
|
||||
},
|
||||
consume: {
|
||||
...flow.consume,
|
||||
status: consumeStatus,
|
||||
shopId: flow.consume.shopId,
|
||||
shopName: flow.consume.shopName,
|
||||
autoConsumeEnabled: flow.consume.autoConsumeEnabled === true,
|
||||
consumedAt,
|
||||
errorMessage: consumeErrorMessage,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: nextTaskStatus,
|
||||
delivery_status: 'delivered',
|
||||
result_code: nextResultCode,
|
||||
result_message: nextResultMessage,
|
||||
redeemed_at: now,
|
||||
last_error: consumeErrorMessage,
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
'kuaishou_cloud_number_returned',
|
||||
{
|
||||
vnId: flow.binding.vnId,
|
||||
vnPhoneMasked: maskPhone(flow.binding.vnPhone),
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
const consumeEventType = consumeAlreadyCompleted
|
||||
? 'kuaishou_cloud_consume_already_completed'
|
||||
: 'kuaishou_cloud_consume_skipped'
|
||||
|
||||
await createTaskEvent(
|
||||
task.id,
|
||||
consumeEventType,
|
||||
{
|
||||
ticketCodeMasked: maskCode(ticketCode),
|
||||
shopId: flow.consume.shopId,
|
||||
shopName: flow.consume.shopName,
|
||||
consumeStatus,
|
||||
consumeMode: 'legacy_writeoff_disabled',
|
||||
errorMessage: consumeErrorMessage,
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
return {
|
||||
task: mapTaskActionPayload(updatedTask),
|
||||
task: mapTaskActionPayload(result.task),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user