订单mock和领取页面优化

This commit is contained in:
yml
2026-05-28 09:05:52 +08:00
parent 4a4fe9a94f
commit 9c981991a0
8 changed files with 855 additions and 59 deletions
@@ -5,7 +5,7 @@ import { PROJECT_ROOT } from '../../config/runtime.js'
import { createTaskEvent } from '../../repositories/task-event-repo.js'
import { getTaskById, updateTask, updateTaskStatusIfCurrent } from '../../repositories/task-repo.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import { addHours, nowIso } from '../../utils/time.js'
import {
getKuaishouEticketSourceConfig,
listKuaishouEticketShopConfigs,
@@ -83,8 +83,9 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
})
}
const taskContext = parseTaskContext(context.task)
const nextContext = {
...parseTaskContext(context.task),
...taskContext,
kuaishouCloudFulfillment: {
...flow,
ticket: {
@@ -108,30 +109,47 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
},
}
const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment)
if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) {
await prepareKuaishouCloudFulfillmentTask({
...context.task,
context_json: JSON.stringify(nextContext),
}, {
source: 'claim_page_ticket_verified',
actor: { source: 'claim_page' },
})
} else {
if (isKuaishouCloudMockContext(nextContext)) {
const mockFlow = buildMockVerifiedKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment, now)
await updateTask(context.task.id, {
claim_token: context.claimToken.token,
claim_expires_at: context.claimToken.expired_at,
context_json: JSON.stringify(nextContext),
context_json: JSON.stringify({
...nextContext,
kuaishouCloudFulfillment: mockFlow,
}),
claimed_at: context.task.claimed_at || now,
task_status: 'waiting_binding',
user_action_status: 'pending_claim',
last_error: '',
updated_at: now,
})
}
} else {
const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment)
if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) {
await prepareKuaishouCloudFulfillmentTask({
...context.task,
context_json: JSON.stringify(nextContext),
}, {
source: 'claim_page_ticket_verified',
actor: { source: 'claim_page' },
})
} else {
await updateTask(context.task.id, {
claim_token: context.claimToken.token,
claim_expires_at: context.claimToken.expired_at,
context_json: JSON.stringify(nextContext),
last_error: '',
updated_at: now,
})
}
if (!context.task.claimed_at) {
await updateTask(context.task.id, {
claimed_at: now,
updated_at: now,
})
if (!context.task.claimed_at) {
await updateTask(context.task.id, {
claimed_at: now,
updated_at: now,
})
}
}
await createTaskEvent(context.task.id, 'kuaishou_cloud_ticket_verified', {
@@ -169,7 +187,10 @@ export async function getKuaishouCloudClaimDetail(token: unknown) {
const context = await getClaimContext(token)
let task = context.task
if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
if (
String(task.executor_key || '').trim() === 'kuaishou_ct_assisted'
&& !isKuaishouCloudMockTask(task)
) {
task = await syncKuaishouCloudRoleInfo(task) || task
}
@@ -340,12 +361,16 @@ export async function confirmKuaishouCloudClaimRole(token: unknown) {
return getKuaishouCloudClaimDetail(token)
}
const refreshed = await refreshKuaishouCloudTaskRoleInfo(context.task, {
source: 'claim_page_role_confirm',
actor: { source: 'claim_page' },
recordEvent: false,
forceProbe: true,
})
const contextSource = parseTaskContext(context.task)
const mockMode = isKuaishouCloudMockContext(contextSource)
const refreshed = mockMode
? { task: context.task }
: await refreshKuaishouCloudTaskRoleInfo(context.task, {
source: 'claim_page_role_confirm',
actor: { source: 'claim_page' },
recordEvent: false,
forceProbe: true,
})
const flow = normalizeKuaishouCloudFlow(parseTaskContext(refreshed.task).kuaishouCloudFulfillment)
if (flow.ticket.status !== 'verified') {
@@ -415,6 +440,11 @@ export async function redeemKuaishouCloudClaim(token: unknown) {
return getKuaishouCloudClaimDetail(token)
}
if (isKuaishouCloudMockTask(lockedTask)) {
await completeMockKuaishouCloudClaimTask(lockedTask, now)
return getKuaishouCloudClaimDetail(token)
}
try {
await dispatchKuaishouCloudFulfillmentTask(lockedTask, {
source: 'claim_page_redeem',
@@ -447,3 +477,114 @@ export async function redeemKuaishouCloudClaim(token: unknown) {
return getKuaishouCloudClaimDetail(token)
}
function isKuaishouCloudMockTask(task: Partial<TaskRow> | null | undefined) {
return isKuaishouCloudMockContext(parseTaskContext(task))
}
function isKuaishouCloudMockContext(context: JsonObject = {}) {
const flow = context.kuaishouCloudFulfillment
&& typeof context.kuaishouCloudFulfillment === 'object'
? context.kuaishouCloudFulfillment
: {}
const mock = flow.mock && typeof flow.mock === 'object' ? flow.mock : context.mock
return Boolean(mock && typeof mock === 'object' && mock.enabled === true)
}
function buildMockVerifiedKuaishouCloudFlow(value: unknown, timestamp: string) {
const flow = normalizeKuaishouCloudFlow(value)
const source = flow as JsonObject
const bindUrl = flow.binding.bindUrl
|| `https://example.com/mock-kuaishou-cloud-bind?task=mock&ts=${encodeURIComponent(timestamp)}`
const vnPhone = flow.binding.vnPhone || '13800000000'
const roleName = flow.binding.roleName || flow.role.name || '测试角色'
const roleId = flow.binding.roleId || flow.role.rid || '10001'
return {
...flow,
mock: {
...(source.mock && typeof source.mock === 'object' ? source.mock : {}),
enabled: true,
},
binding: {
...flow.binding,
prepareStatus: 'ready',
vnId: flow.binding.vnId || 900001,
vnPhone,
bindUrl,
bindPreparedAt: flow.binding.bindPreparedAt || timestamp,
bindExpiresAt: flow.binding.bindExpiresAt || addHours(timestamp, 24),
roleName,
roleId,
},
role: {
...flow.role,
status: 'ready',
name: roleName,
rid: roleId,
refreshedAt: flow.role.refreshedAt || timestamp,
errorMessage: '',
rawInfo: flow.role.rawInfo || {
mock: true,
},
},
}
}
async function completeMockKuaishouCloudClaimTask(task: TaskRow, timestamp: string) {
const taskContext = parseTaskContext(task)
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
const source = flow as JsonObject
const nextFlow = {
...flow,
mock: {
...(source.mock && typeof source.mock === 'object' ? source.mock : {}),
enabled: true,
},
dispatch: {
...flow.dispatch,
status: 'success',
dispatchAt: timestamp,
dispatchBy: {
source: 'claim_page_mock',
},
note: '开发 mock 已模拟发货成功',
items: flow.deliveryItems,
},
returnNumber: {
...flow.returnNumber,
status: 'success',
returnedAt: timestamp,
returnedBy: {
source: 'claim_page_mock',
},
},
consume: {
...flow.consume,
status: 'success',
consumedAt: timestamp,
errorMessage: '',
},
}
await updateTask(task.id, {
task_status: 'completed',
delivery_status: 'success',
result_code: 'mock_success',
result_message: '开发 mock 已模拟兑换成功',
user_action_status: 'not_required',
last_error: '',
context_json: JSON.stringify({
...taskContext,
kuaishouCloudFulfillment: nextFlow,
}),
redeemed_at: timestamp,
updated_at: timestamp,
})
await createTaskEvent(task.id, 'kuaishou_cloud_mock_redeemed', {
source: 'claim_page_mock',
deliveryItems: flow.deliveryItems,
}, timestamp)
}