重构订单履约发货流程
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { buildClaimUrl } from '../../claim/claim-service.js'
|
||||
import {
|
||||
shouldEnsureKuaishouCloudClaimLink,
|
||||
TASK_STATUS,
|
||||
} from '../../../domain/task-status.js'
|
||||
import { ensureTaskClaimLink } from '../kuaishou-cloud/index.js'
|
||||
import {
|
||||
FULFILLMENT_EXECUTOR_KEYS,
|
||||
type FulfillmentDeliveryLink,
|
||||
type FulfillmentExecutor,
|
||||
type FulfillmentPrepareDeps,
|
||||
} from './types.js'
|
||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
export const kuaishouCloudExecutor: FulfillmentExecutor = {
|
||||
key: FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_CLOUD,
|
||||
preparePaidTask,
|
||||
resolveDeliveryLink,
|
||||
}
|
||||
|
||||
async function preparePaidTask(
|
||||
task: TaskRow,
|
||||
deps: FulfillmentPrepareDeps,
|
||||
): Promise<TaskRow | null> {
|
||||
const now = deps.nowIso()
|
||||
|
||||
if (shouldEnsureKuaishouCloudClaimLink(task.task_status)) {
|
||||
if (!task.primary_claim_token_id && !String(task.claim_token || '').trim()) {
|
||||
const claimToken = await deps.createTaskClaimToken(task.id)
|
||||
return deps.updateTask(task.id, {
|
||||
claim_token: claimToken.token,
|
||||
claim_expires_at: claimToken.expired_at,
|
||||
user_action_status: 'pending_claim',
|
||||
updated_at: now,
|
||||
})
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
const claimToken = await deps.createTaskClaimToken(task.id)
|
||||
|
||||
return deps.updateTask(task.id, {
|
||||
task_status: TASK_STATUS.PENDING_BINDING_PREPARE,
|
||||
claim_token: claimToken.token,
|
||||
claim_expires_at: claimToken.expired_at,
|
||||
user_action_status: 'pending_claim',
|
||||
last_error: task.last_error || '领取链接已生成,等待客户提交核销码',
|
||||
updated_at: now,
|
||||
})
|
||||
}
|
||||
|
||||
async function resolveDeliveryLink(task: TaskRow): Promise<FulfillmentDeliveryLink | null> {
|
||||
const primaryToken = String(task.primary_claim_token || task.claim_token || '').trim()
|
||||
const expireTime = task.primary_claim_expires_at || task.claim_expires_at || ''
|
||||
|
||||
if (primaryToken) {
|
||||
return {
|
||||
claimUrl: buildClaimUrl(primaryToken),
|
||||
expireTime,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const ensured = await ensureTaskClaimLink(task)
|
||||
return {
|
||||
claimUrl: String(ensured.claimUrl || '').trim(),
|
||||
expireTime: ensured.expiredAt || expireTime,
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||
import {
|
||||
ensureKuaishouFeifeiClaimShortLink,
|
||||
prepareKuaishouFeifeiTask,
|
||||
resolveKuaishouFeifeiClaimUrl,
|
||||
} from '../kuaishou-feifei/index.js'
|
||||
import { parseTaskContext } from '../../../utils/task-json.js'
|
||||
import {
|
||||
FULFILLMENT_EXECUTOR_KEYS,
|
||||
type FulfillmentDeliveryLink,
|
||||
type FulfillmentExecutor,
|
||||
type FulfillmentPrepareDeps,
|
||||
} from './types.js'
|
||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
export const kuaishouFeifeiExecutor: FulfillmentExecutor = {
|
||||
key: FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_FEIFEI,
|
||||
preparePaidTask,
|
||||
resolveDeliveryLink,
|
||||
}
|
||||
|
||||
async function preparePaidTask(
|
||||
task: TaskRow,
|
||||
deps: FulfillmentPrepareDeps,
|
||||
): Promise<TaskRow | null> {
|
||||
try {
|
||||
return await prepareKuaishouFeifeiTask(task)
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'kuaishou-feifei 订单创建失败'
|
||||
const updatedTask = await deps.updateTask(task.id, {
|
||||
task_status: TASK_STATUS.MANUAL_REVIEW,
|
||||
user_action_status: 'not_required',
|
||||
last_error: message,
|
||||
result_code: 'kuaishou_feifei_prepare_failed',
|
||||
result_message: message,
|
||||
updated_at: deps.nowIso(),
|
||||
})
|
||||
await deps.notifyTaskAutoManualReview({
|
||||
task: updatedTask || task,
|
||||
reason: message,
|
||||
source: 'kuaishou_feifei_prepare_failed',
|
||||
})
|
||||
return updatedTask
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveDeliveryLink(task: TaskRow): Promise<FulfillmentDeliveryLink | null> {
|
||||
let claimUrl = await ensureKuaishouFeifeiClaimShortLink(task)
|
||||
if (!claimUrl) {
|
||||
const context = parseTaskContext(task)
|
||||
claimUrl = resolveKuaishouFeifeiClaimUrl(context.kuaishouFeifei)
|
||||
}
|
||||
|
||||
if (!claimUrl) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
claimUrl,
|
||||
expireTime: '',
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||
import {
|
||||
FULFILLMENT_EXECUTOR_KEYS,
|
||||
type FulfillmentExecutor,
|
||||
type FulfillmentPrepareDeps,
|
||||
} from './types.js'
|
||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
export const manualDispatchExecutor: FulfillmentExecutor = {
|
||||
key: FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH,
|
||||
preparePaidTask,
|
||||
}
|
||||
|
||||
async function preparePaidTask(
|
||||
task: TaskRow,
|
||||
deps: FulfillmentPrepareDeps,
|
||||
): Promise<TaskRow | null> {
|
||||
const lastError = task.last_error || '当前任务需要人工履约处理'
|
||||
const updatedTask = await deps.updateTask(task.id, {
|
||||
task_status: TASK_STATUS.MANUAL_REVIEW,
|
||||
user_action_status: 'not_required',
|
||||
last_error: lastError,
|
||||
updated_at: deps.nowIso(),
|
||||
})
|
||||
|
||||
await deps.notifyTaskAutoManualReview({
|
||||
task: updatedTask || task,
|
||||
reason: lastError,
|
||||
source: 'manual_dispatch_profile',
|
||||
})
|
||||
|
||||
return updatedTask
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { isPaidPreparationStableStatus } from '../../../domain/task-status.js'
|
||||
import { kuaishouCloudExecutor } from './kuaishou-cloud-executor.js'
|
||||
import { kuaishouFeifeiExecutor } from './kuaishou-feifei-executor.js'
|
||||
import { manualDispatchExecutor } from './manual-executor.js'
|
||||
import {
|
||||
FULFILLMENT_EXECUTOR_KEYS,
|
||||
isManualDispatchExecutor,
|
||||
normalizeExecutorKey,
|
||||
type FulfillmentDeliveryLink,
|
||||
type FulfillmentExecutor,
|
||||
type FulfillmentPrepareDeps,
|
||||
} from './types.js'
|
||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
const EXECUTORS = new Map<string, FulfillmentExecutor>([
|
||||
[FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_CLOUD, kuaishouCloudExecutor],
|
||||
[FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_INDUSTRY, kuaishouCloudExecutor],
|
||||
[FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_FEIFEI, kuaishouFeifeiExecutor],
|
||||
[FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH, manualDispatchExecutor],
|
||||
])
|
||||
|
||||
export function getFulfillmentExecutor(executorKey: unknown): FulfillmentExecutor | null {
|
||||
return EXECUTORS.get(normalizeExecutorKey(executorKey)) || null
|
||||
}
|
||||
|
||||
export async function preparePaidFulfillmentTask(
|
||||
task: TaskRow,
|
||||
deps: FulfillmentPrepareDeps,
|
||||
): Promise<TaskRow | null> {
|
||||
if (isPaidPreparationStableStatus(task.task_status)) {
|
||||
return task
|
||||
}
|
||||
|
||||
const executor = getFulfillmentExecutor(task.executor_key)
|
||||
if (executor?.preparePaidTask) {
|
||||
return executor.preparePaidTask(task, deps)
|
||||
}
|
||||
|
||||
if (!task.requires_claim || isManualDispatchExecutor(task.executor_key)) {
|
||||
return manualDispatchExecutor.preparePaidTask?.(task, deps) || task
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
export async function resolveFulfillmentDeliveryLink(
|
||||
task: TaskRow,
|
||||
): Promise<FulfillmentDeliveryLink | null> {
|
||||
const executor = getFulfillmentExecutor(task.executor_key)
|
||||
if (!executor?.resolveDeliveryLink) {
|
||||
return null
|
||||
}
|
||||
|
||||
return executor.resolveDeliveryLink(task)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { TaskUpdatePatch } from '../../../types/repository/inputs.js'
|
||||
import type { TaskRow } from '../../../types/repository/rows.js'
|
||||
|
||||
export const FULFILLMENT_EXECUTOR_KEYS = {
|
||||
MANUAL_DISPATCH: 'manual_dispatch',
|
||||
KUAISHOU_CLOUD: 'kuaishou_ct_assisted',
|
||||
KUAISHOU_INDUSTRY: 'kuaishou-industry',
|
||||
KUAISHOU_FEIFEI: 'kuaishou_feifei',
|
||||
} as const
|
||||
|
||||
export type FulfillmentExecutorKey =
|
||||
(typeof FULFILLMENT_EXECUTOR_KEYS)[keyof typeof FULFILLMENT_EXECUTOR_KEYS] | (string & {})
|
||||
|
||||
export type FulfillmentDeliveryLink = {
|
||||
claimUrl: string
|
||||
expireTime?: unknown
|
||||
}
|
||||
|
||||
export type FulfillmentPrepareDeps = {
|
||||
updateTask: (taskId: number | string, patch: TaskUpdatePatch) => Promise<TaskRow | null>
|
||||
createTaskClaimToken: (taskId: number | string) => Promise<{
|
||||
token: string
|
||||
expired_at: string
|
||||
[key: string]: unknown
|
||||
}>
|
||||
notifyTaskAutoManualReview: (payload: {
|
||||
task: unknown
|
||||
reason: string
|
||||
source: string
|
||||
}) => Promise<unknown> | unknown
|
||||
nowIso: () => string
|
||||
}
|
||||
|
||||
export type FulfillmentExecutor = {
|
||||
key: FulfillmentExecutorKey
|
||||
preparePaidTask?: (
|
||||
task: TaskRow,
|
||||
deps: FulfillmentPrepareDeps,
|
||||
) => Promise<TaskRow | null>
|
||||
resolveDeliveryLink?: (task: TaskRow) => Promise<FulfillmentDeliveryLink | null>
|
||||
}
|
||||
|
||||
export function normalizeExecutorKey(value: unknown): FulfillmentExecutorKey {
|
||||
return String(value || '').trim() as FulfillmentExecutorKey
|
||||
}
|
||||
|
||||
export function isKuaishouCloudExecutor(value: unknown): boolean {
|
||||
const executorKey = normalizeExecutorKey(value)
|
||||
return executorKey === FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_CLOUD ||
|
||||
executorKey === FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_INDUSTRY
|
||||
}
|
||||
|
||||
export function isKuaishouFeifeiExecutor(value: unknown): boolean {
|
||||
return normalizeExecutorKey(value) === FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_FEIFEI
|
||||
}
|
||||
|
||||
export function isManualDispatchExecutor(value: unknown): boolean {
|
||||
return normalizeExecutorKey(value) === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH
|
||||
}
|
||||
Reference in New Issue
Block a user