客户领取页 ui与逻辑优化
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
import { reserveInventoryForTask } from '../order/inventory-service.js'
|
||||
import { buildClaimUrl } from './claim-service.js'
|
||||
import { ensureAgisoXianyuAutoDeliveryForDeliveredTask } from '../platforms/agiso/xianyu/auto-delivery-service.js'
|
||||
import { syncKuaishouCloudRoleInfo } from './kuaishou-cloud-sync-service.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
|
||||
import { nowIso } from '../../utils/time.js'
|
||||
@@ -33,7 +34,13 @@ const KUAISHOU_CLOUD_CLAIM_GUIDE_BASE_PATH = '/kuaishou-cloud-guide'
|
||||
|
||||
export async function getClaimDetail(token, { includeQrImage = true } = {}) {
|
||||
const context = await getClaimContext(token)
|
||||
const { task, session } = await loadTaskSession(context.task, { includeQrImage })
|
||||
let { task, session } = await loadTaskSession(context.task, { includeQrImage })
|
||||
|
||||
// 如果是快手 Cloud 流程,尝试同步角色信息
|
||||
if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
|
||||
task = await syncKuaishouCloudRoleInfo(task)
|
||||
}
|
||||
|
||||
const syncedTask = session ? await syncTaskWithSession(task, session) : task
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
@@ -879,6 +886,8 @@ function mapClaimKuaishouCloudFulfillment(task, order) {
|
||||
}
|
||||
|
||||
const binding = source.binding && typeof source.binding === 'object' ? source.binding : {}
|
||||
const role = source.role && typeof source.role === 'object' ? source.role : {}
|
||||
const purchase = source.purchase && typeof source.purchase === 'object' ? source.purchase : {}
|
||||
const ticket = source.ticket && typeof source.ticket === 'object' ? source.ticket : {}
|
||||
const dispatch = source.dispatch && typeof source.dispatch === 'object' ? source.dispatch : {}
|
||||
const returnNumber = source.returnNumber && typeof source.returnNumber === 'object' ? source.returnNumber : {}
|
||||
@@ -903,9 +912,32 @@ function mapClaimKuaishouCloudFulfillment(task, order) {
|
||||
},
|
||||
binding: {
|
||||
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
||||
cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
|
||||
skuId: Number(binding.skuId || 0) || 0,
|
||||
skuName: String(binding.skuName || '').trim(),
|
||||
vnKey: String(binding.vnKey || '').trim(),
|
||||
vnId: Number(binding.vnId || 0) || 0,
|
||||
bindUrl: String(binding.bindUrl || '').trim(),
|
||||
bindPreparedAt: binding.bindPreparedAt || null,
|
||||
vnPhone: String(binding.vnPhone || '').trim(),
|
||||
roleName: String(binding.roleName || '').trim(),
|
||||
roleId: String(binding.roleId || '').trim(),
|
||||
},
|
||||
role: {
|
||||
status: String(role.status || 'pending').trim() || 'pending',
|
||||
name: String(role.name || binding.roleName || '').trim(),
|
||||
rid: String(role.rid || binding.roleId || '').trim(),
|
||||
refreshedAt: role.refreshedAt || null,
|
||||
errorMessage: String(role.errorMessage || '').trim(),
|
||||
},
|
||||
purchase: {
|
||||
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
||||
minAssetReserve: Number(purchase.minAssetReserve || 0) || 0,
|
||||
usedKnapsack: purchase.usedKnapsack === true,
|
||||
purchaseTriggered: purchase.purchaseTriggered === true,
|
||||
assetBefore: Number(purchase.assetBefore || 0) || 0,
|
||||
assetAfter: Number(purchase.assetAfter || 0) || 0,
|
||||
purchaseAt: purchase.purchaseAt || null,
|
||||
},
|
||||
dispatch: {
|
||||
status: String(dispatch.status || 'pending').trim() || 'pending',
|
||||
|
||||
@@ -11,6 +11,12 @@ import {
|
||||
resolveKuaishouEticketShopConfig,
|
||||
} from '../platforms/kuaishou-eticket/source-config-service.js'
|
||||
import { queryKuaishouEticketConsumeDetail } from '../platforms/kuaishou-eticket/consume-service.js'
|
||||
import {
|
||||
dispatchKuaishouCloudFulfillmentTask,
|
||||
normalizeKuaishouCloudFlow,
|
||||
prepareKuaishouCloudFulfillmentTask,
|
||||
refreshKuaishouCloudTaskRoleInfo,
|
||||
} from '../fulfillment/kuaishou-cloud-task-service.js'
|
||||
import { getClaimContext, getClaimDetail } from './claim-session-service.js'
|
||||
|
||||
const KUAISHOU_CLOUD_GUIDE_DIR = path.resolve(PROJECT_ROOT, '../../tems/imgs')
|
||||
@@ -28,12 +34,6 @@ export async function verifyKuaishouCloudClaimTicket(token, payload = {}) {
|
||||
}
|
||||
|
||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(context.task).kuaishouCloudFulfillment)
|
||||
if (!flow.binding.bindUrl || flow.binding.prepareStatus !== 'ready') {
|
||||
throw createHttpError('当前任务还没有准备好绑定资源,请联系客服稍后重试', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_kuaishou_cloud_binding_not_ready',
|
||||
})
|
||||
}
|
||||
|
||||
const ticketCode = String(payload.ticketCode || payload.eTicketId || '').trim()
|
||||
if (!ticketCode) {
|
||||
@@ -96,7 +96,7 @@ export async function verifyKuaishouCloudClaimTicket(token, payload = {}) {
|
||||
},
|
||||
}
|
||||
|
||||
await updateTask(context.task.id, {
|
||||
const updatedTask = await updateTask(context.task.id, {
|
||||
claim_token: context.claimToken.token,
|
||||
claim_expires_at: context.claimToken.expired_at,
|
||||
claimed_at: context.task.claimed_at || now,
|
||||
@@ -112,6 +112,14 @@ export async function verifyKuaishouCloudClaimTicket(token, payload = {}) {
|
||||
goodsTitle: String(detailResult.goods?.itemTitle || '').trim(),
|
||||
}, now)
|
||||
|
||||
const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment)
|
||||
if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) {
|
||||
await prepareKuaishouCloudFulfillmentTask(updatedTask, {
|
||||
source: 'claim_page_ticket_verified',
|
||||
actor: { source: 'claim_page' },
|
||||
})
|
||||
}
|
||||
|
||||
return getClaimDetail(token, { includeQrImage: false })
|
||||
}
|
||||
|
||||
@@ -135,47 +143,6 @@ export async function getKuaishouCloudClaimGuideAssetPath(filename) {
|
||||
return filePath
|
||||
}
|
||||
|
||||
function normalizeKuaishouCloudFlow(value) {
|
||||
const source = value && typeof value === 'object' ? value : {}
|
||||
const binding = source.binding && typeof source.binding === 'object' ? source.binding : {}
|
||||
const consume = source.consume && typeof source.consume === 'object' ? source.consume : {}
|
||||
const ticket = source.ticket && typeof source.ticket === 'object' ? source.ticket : {}
|
||||
|
||||
return {
|
||||
...source,
|
||||
ticket: {
|
||||
code: String(ticket.code || '').trim(),
|
||||
status: String(ticket.status || 'pending').trim() || 'pending',
|
||||
capturedAt: ticket.capturedAt || null,
|
||||
capturedBy: ticket.capturedBy || null,
|
||||
verifiedAt: ticket.verifiedAt || null,
|
||||
oid: String(ticket.oid || '').trim(),
|
||||
formToken: String(ticket.formToken || '').trim(),
|
||||
leftCount: Number(ticket.leftCount || 0) || 0,
|
||||
goodsTitle: String(ticket.goodsTitle || '').trim(),
|
||||
},
|
||||
binding: {
|
||||
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
||||
cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
|
||||
skuId: Number(binding.skuId || 0) || 0,
|
||||
skuName: String(binding.skuName || '').trim(),
|
||||
vnKey: String(binding.vnKey || '').trim(),
|
||||
vnId: Number(binding.vnId || 0) || 0,
|
||||
vnPhone: String(binding.vnPhone || '').trim(),
|
||||
bindUrl: String(binding.bindUrl || '').trim(),
|
||||
bindPreparedAt: binding.bindPreparedAt || null,
|
||||
},
|
||||
consume: {
|
||||
status: String(consume.status || 'pending').trim() || 'pending',
|
||||
shopId: String(consume.shopId || '').trim(),
|
||||
shopName: String(consume.shopName || '').trim(),
|
||||
autoConsumeEnabled: consume.autoConsumeEnabled === true,
|
||||
consumedAt: consume.consumedAt || null,
|
||||
errorMessage: String(consume.errorMessage || '').trim(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function parseTaskContext(task) {
|
||||
const rawValue = task?.context_json
|
||||
if (!rawValue) {
|
||||
@@ -193,11 +160,84 @@ function parseTaskContext(task) {
|
||||
}
|
||||
}
|
||||
|
||||
function maskCode(value) {
|
||||
const normalized = String(value || '').trim()
|
||||
if (normalized.length <= 6) {
|
||||
return normalized
|
||||
export async function confirmKuaishouCloudClaimRole(token) {
|
||||
const context = await getClaimContext(token)
|
||||
const now = nowIso()
|
||||
|
||||
if (String(context.task.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
|
||||
throw createHttpError('当前领取链接不是快手 Cloud 客户领取流程', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_not_kuaishou_cloud',
|
||||
})
|
||||
}
|
||||
|
||||
return `${normalized.slice(0, 3)}***${normalized.slice(-3)}`
|
||||
if (['role_confirmed', 'dispatched_pending_return', 'completed'].includes(String(context.task.task_status || '').trim())) {
|
||||
return getClaimDetail(token, { includeQrImage: false })
|
||||
}
|
||||
|
||||
const refreshed = await refreshKuaishouCloudTaskRoleInfo(context.task, {
|
||||
source: 'claim_page_role_confirm',
|
||||
actor: { source: 'claim_page' },
|
||||
recordEvent: false,
|
||||
})
|
||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(refreshed.task).kuaishouCloudFulfillment)
|
||||
|
||||
if (flow.ticket.status !== 'verified') {
|
||||
throw createHttpError('请先验证核销码', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_kuaishou_cloud_ticket_not_verified',
|
||||
})
|
||||
}
|
||||
|
||||
if (!flow.binding.vnPhone || !flow.binding.roleName || !flow.binding.roleId) {
|
||||
throw createHttpError('角色信息还未刷新到系统,请完成绑定后稍等片刻再试', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_kuaishou_cloud_role_not_ready',
|
||||
})
|
||||
}
|
||||
|
||||
await updateTask(context.task.id, {
|
||||
task_status: 'role_confirmed',
|
||||
role_id: flow.binding.roleId,
|
||||
role_name: flow.binding.roleName,
|
||||
user_action_status: 'role_confirmed',
|
||||
role_confirmed_at: now,
|
||||
last_error: '',
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(context.task.id, 'kuaishou_cloud_role_confirmed', {
|
||||
vnPhone: flow.binding.vnPhone,
|
||||
roleName: flow.binding.roleName,
|
||||
roleId: flow.binding.roleId,
|
||||
}, now)
|
||||
|
||||
return getClaimDetail(token, { includeQrImage: false })
|
||||
}
|
||||
|
||||
export async function redeemKuaishouCloudClaim(token) {
|
||||
const context = await getClaimContext(token)
|
||||
const now = nowIso()
|
||||
|
||||
if (String(context.task.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
|
||||
throw createHttpError('当前领取链接不是快手 Cloud 客户领取流程', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_not_kuaishou_cloud',
|
||||
})
|
||||
}
|
||||
|
||||
if (context.task.task_status !== 'role_confirmed') {
|
||||
throw createHttpError('请先确认角色信息', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_kuaishou_cloud_role_not_confirmed',
|
||||
})
|
||||
}
|
||||
|
||||
await dispatchKuaishouCloudFulfillmentTask(context.task, {
|
||||
source: 'claim_page_redeem',
|
||||
actor: { source: 'claim_page' },
|
||||
autoFinalize: true,
|
||||
})
|
||||
|
||||
return getClaimDetail(token, { includeQrImage: false })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
normalizeKuaishouCloudFlow,
|
||||
refreshKuaishouCloudTaskRoleInfo,
|
||||
} from '../fulfillment/kuaishou-cloud-task-service.js'
|
||||
|
||||
export async function syncKuaishouCloudRoleInfo(task) {
|
||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
|
||||
|
||||
if (!flow.binding.vnId || !flow.binding.vnKey || !flow.binding.vnPhone) {
|
||||
return task
|
||||
}
|
||||
|
||||
if (flow.binding.roleName && flow.binding.roleId) {
|
||||
return task
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await refreshKuaishouCloudTaskRoleInfo(task, {
|
||||
source: 'claim_page_polling',
|
||||
actor: { source: 'system' },
|
||||
recordEvent: false,
|
||||
})
|
||||
return result.task
|
||||
} catch {
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
function parseTaskContext(task) {
|
||||
const rawValue = task?.context_json
|
||||
|
||||
if (!rawValue) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (typeof rawValue === 'object') {
|
||||
return rawValue
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(String(rawValue || '{}'))
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user