Files
order_site/apps/frontend/src/pages/claim/claim-snapshot.ts
T
yml2213andCursor 8e0cb7e295 fix(claim): 领取页展示游戏角色名,并说明乐玩免扫码场景
持久化 affiliate_dash roleName/gameChannel;免扫码授权时明确提示,有绑定链接时提交前仍可显示二维码。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 12:39:47 +08:00

249 lines
7.2 KiB
TypeScript

import {
TASK_STATUS,
hasKuaishouCloudRedeemResultStatus,
isKuaishouCloudCompletedStatus,
normalizeTaskStatus,
} from '@/domain/task-status'
import type { ClaimAffiliateDashFlowInfo, ClaimDetailData } from '@/types/claim'
export type ResultVariant = 'success' | 'warning' | 'info'
export type ClaimSnapshot = ReturnType<typeof createClaimSnapshot>
export function createClaimSnapshot(detail: ClaimDetailData | null) {
const affiliateDash = detail?.affiliateDash || null
const order = detail?.order || null
const orderItem = detail?.orderItem || null
const product = detail?.product || null
const task = detail?.task || null
const claimIdentity = detail?.claimIdentity || null
const expectedUid = String(claimIdentity?.expectedUid || '').trim()
const hasExpectedUid = Boolean(expectedUid || claimIdentity?.ready)
const channelOffline = Boolean(detail?.channelOffline)
const channelOfflineMessage = String(detail?.channelOfflineMessage || '').trim()
const isAffiliateDashFlow = detail?.flowType === 'affiliate_dash'
const affiliateDashExpectedUid = String(
expectedUid || affiliateDash?.expectedGameAccount || affiliateDash?.gameAccount || '',
).trim()
const affiliateDashBoundAccount = String(affiliateDash?.boundAccount || '').trim()
const isAffiliateDashBound = Boolean(affiliateDash?.bound)
const isAffiliateDashUidMatched = Boolean(
isAffiliateDashBound &&
affiliateDashExpectedUid &&
affiliateDashBoundAccount &&
normalizeUid(affiliateDashExpectedUid) === normalizeUid(affiliateDashBoundAccount),
)
const isAffiliateDashBindMismatch = Boolean(
affiliateDash?.bindMismatch ||
(isAffiliateDashBound &&
affiliateDashExpectedUid &&
affiliateDashBoundAccount &&
!isAffiliateDashUidMatched),
)
const isAffiliateDashReadyToSubmit = Boolean(
isAffiliateDashBound && isAffiliateDashUidMatched && !isAffiliateDashBindMismatch,
)
const normalizedStatus = normalizeTaskStatus(task?.status)
const isRedeemFailed =
normalizedStatus === TASK_STATUS.MANUAL_REVIEW ||
normalizedStatus === TASK_STATUS.FAILED ||
normalizedStatus === TASK_STATUS.CLOSED ||
(isAffiliateDashFlow &&
['ship_failed', 'cancelled'].includes(String(affiliateDash?.orderStatus || '').trim()))
const isCompleted = isKuaishouCloudCompletedStatus(task?.status)
const hasRedeemResult =
hasKuaishouCloudRedeemResultStatus(task?.status) ||
(isAffiliateDashFlow &&
['delivered', 'ship_failed', 'cancelled'].includes(
String(affiliateDash?.orderStatus || '').trim(),
))
const currentStep = resolveCurrentStep({
channelOffline,
hasExpectedUid,
hasRedeemResult,
isAffiliateDashFlow,
isAffiliateDashReadyToSubmit,
})
const progressText = resolveProgressText({
channelOffline,
channelOfflineMessage,
affiliateDash,
isAffiliateDashFlow,
hasExpectedUid,
hasRedeemResult,
isAffiliateDashReadyToSubmit,
isAffiliateDashBindMismatch,
})
const resultTitle = resolveResultTitle({
isRedeemFailed,
isCompleted,
isAffiliateDashFlow,
affiliateDash,
})
const resultDescription = resolveResultDescription({
detail,
affiliateDash,
task,
isRedeemFailed,
isCompleted,
})
const resultVariant: ResultVariant = isRedeemFailed ? 'warning' : isCompleted ? 'success' : 'info'
return {
flow: null as null,
feifei: null as null,
affiliateDash,
order,
orderItem,
product,
task,
claimIdentity,
expectedUid,
hasExpectedUid,
channelOffline,
channelOfflineMessage,
roleName: '',
roleId: '',
isFeifeiFlow: false,
isAffiliateDashFlow,
affiliateDashExpectedUid,
affiliateDashBoundAccount,
isAffiliateDashBound,
isAffiliateDashUidMatched,
isAffiliateDashBindMismatch,
isAffiliateDashReadyToSubmit,
isBindUrlExpired: false,
isBindingPrepared: false,
isBindingPreparing: false,
isRoleReady: false,
isUidMatched: false,
confirmRoleDisabledReason: '',
isRoleConfirmed: false,
isDispatched: false,
isRedeemFailed,
isCompleted,
hasRedeemResult,
currentStep,
progressText,
resultTitle,
resultDescription,
resultVariant,
}
}
export function normalizeUid(value: unknown) {
return String(value || '')
.trim()
.replace(/\s+/g, '')
}
function resolveCurrentStep(options: {
channelOffline: boolean
hasExpectedUid: boolean
hasRedeemResult: boolean
isAffiliateDashFlow: boolean
isAffiliateDashReadyToSubmit: boolean
}) {
if (options.channelOffline) {
return 1
}
if (!options.hasExpectedUid) {
return 1
}
if (options.hasRedeemResult) {
return 4
}
if (options.isAffiliateDashFlow) {
if (!options.isAffiliateDashReadyToSubmit) {
return 2
}
return 3
}
return 2
}
function resolveProgressText(options: {
channelOffline: boolean
channelOfflineMessage: string
affiliateDash: ClaimAffiliateDashFlowInfo | null
isAffiliateDashFlow: boolean
hasExpectedUid: boolean
hasRedeemResult: boolean
isAffiliateDashReadyToSubmit: boolean
isAffiliateDashBindMismatch: boolean
}) {
if (options.channelOffline) {
return options.channelOfflineMessage || '该履约渠道已下线'
}
if (!options.hasExpectedUid) {
return '请填写游戏编号'
}
if (options.isAffiliateDashFlow) {
if (options.hasRedeemResult) {
return '发货结果已生成'
}
if (!options.affiliateDash?.bound) {
return options.affiliateDash?.bindUrl || options.affiliateDash?.qrUrl
? '请扫码绑定账号'
: '绑定链接生成中,请稍候'
}
if (options.isAffiliateDashBindMismatch) {
return '绑定 UID 不一致,请重新扫码'
}
if (!options.isAffiliateDashReadyToSubmit) {
return '绑定账号确认中,请稍候'
}
if (!(options.affiliateDash?.bindUrl || options.affiliateDash?.qrUrl)) {
return '账号已授权,请确认提交发货'
}
return '已绑定,请提交发货'
}
return '当前领取流程不可用'
}
function resolveResultTitle(options: {
isRedeemFailed: boolean
isCompleted: boolean
isAffiliateDashFlow: boolean
affiliateDash: ClaimAffiliateDashFlowInfo | null
}) {
if (options.isRedeemFailed) {
return '发货遇到问题'
}
if (options.isCompleted) {
return '兑换成功'
}
if (options.isAffiliateDashFlow && options.affiliateDash?.orderStatus === 'delivered') {
return '发货已完成'
}
return '结果已记录'
}
function resolveResultDescription(options: {
detail: ClaimDetailData | null
affiliateDash: ClaimAffiliateDashFlowInfo | null
task: ClaimDetailData['task'] | null
isRedeemFailed: boolean
isCompleted: boolean
}) {
if (options.isRedeemFailed) {
const message = String(
options.task?.lastError ||
options.affiliateDash?.failureReason ||
options.detail?.result?.resultMessage ||
'',
).trim()
return message || '当前流程需要客服处理,后续结果请以后台任务界面为准。'
}
if (options.isCompleted) {
return '当前兑换流程已经完成。'
}
if (options.affiliateDash) {
return '你的发货请求已经提交。后续结果会由系统保存在后台任务界面。'
}
return '你的兑换请求已经提交。后续结果会由系统保存在后台任务界面。'
}