Files
order_site/apps/frontend/src/pages/claim/claim-snapshot.ts
T
yml2213 c70c3ff6a5 拆分领取页 ClaimPage 为多模块
将 snapshot、轮询、共享 UI、UID/lewan/feifei 步骤组件物理拆分,主文件只保留页面编排与状态。
2026-07-10 13:55:43 +08:00

261 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
TASK_STATUS,
hasKuaishouCloudRedeemResultStatus,
isKuaishouCloudCompletedStatus,
isKuaishouCloudRoleConfirmedStatus,
normalizeTaskStatus,
} from '@/domain/task-status'
import type {
ClaimDetailData,
ClaimKuaishouCloudFlowInfo,
ClaimKuaishouFeifeiFlowInfo,
} from '@/types/claim'
export type ResultVariant = 'success' | 'warning' | 'info'
export type ClaimSnapshot = ReturnType<typeof createClaimSnapshot>
export function createClaimSnapshot(
detail: ClaimDetailData | null,
options: { rebindingRole?: boolean } = {},
) {
const flow = detail?.kuaishouCloudFulfillment || null
const feifei = detail?.kuaishouFeifei || 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 roleName = flow?.role.name || flow?.binding.roleName || ''
const roleId = flow?.role.rid || flow?.binding.roleId || ''
const isFeifeiFlow = detail?.flowType === 'kuaishou_feifei'
const isBindUrlExpired = isDateExpired(flow?.binding.bindExpiresAt || '')
const isBindingPrepared =
flow?.binding.prepareStatus === 'ready' &&
Boolean(String(flow?.binding.bindUrl || '').trim()) &&
!isBindUrlExpired
const isBindingPreparing = flow?.binding.prepareStatus === 'pending'
const isRoleReady = Boolean(roleName || roleId)
const isUidMatched =
Boolean(expectedUid) && Boolean(roleId) && normalizeUid(expectedUid) === normalizeUid(roleId)
const confirmRoleDisabledReason = resolveConfirmRoleDisabledReason({
isBindingPrepared,
isRoleReady,
isUidMatched,
expectedUid,
roleId,
rebindingRole: Boolean(options.rebindingRole),
})
const isRoleConfirmed = isKuaishouCloudRoleConfirmedStatus(task?.status)
const isDispatched = String(flow?.dispatch.status || '').trim() === 'success'
const normalizedStatus = normalizeTaskStatus(task?.status)
const isRedeemFailed =
normalizedStatus === TASK_STATUS.MANUAL_REVIEW ||
normalizedStatus === TASK_STATUS.FAILED ||
String(flow?.dispatch.status || '').trim() === 'failed' ||
(isFeifeiFlow && [40, 50].includes(Number(feifei?.rechargeStatus || 0)))
const isCompleted =
isKuaishouCloudCompletedStatus(task?.status) ||
(isFeifeiFlow && Number(feifei?.rechargeStatus || 0) === 30)
const hasRedeemResult =
isDispatched ||
hasKuaishouCloudRedeemResultStatus(task?.status) ||
(isFeifeiFlow && [30, 40, 50, 60].includes(Number(feifei?.rechargeStatus || 0)))
const currentStep = resolveCurrentStep({
hasExpectedUid,
hasRedeemResult,
isRoleConfirmed,
isFeifeiFlow,
isUidMatched,
})
const progressText = resolveProgressText({
feifei,
isFeifeiFlow,
hasExpectedUid,
expectedUid,
hasRedeemResult,
isRoleConfirmed,
isBindingPrepared,
isUidMatched,
})
const resultTitle = resolveResultTitle({ isRedeemFailed, isCompleted, isDispatched })
const resultDescription = resolveResultDescription({
detail,
flow,
feifei,
task,
isRedeemFailed,
isCompleted,
})
const resultVariant: ResultVariant = isRedeemFailed
? 'warning'
: isCompleted || isDispatched
? 'success'
: 'info'
return {
flow,
feifei,
order,
orderItem,
product,
task,
claimIdentity,
expectedUid,
hasExpectedUid,
roleName,
roleId,
isFeifeiFlow,
isBindUrlExpired,
isBindingPrepared,
isBindingPreparing,
isRoleReady,
isUidMatched,
confirmRoleDisabledReason,
isRoleConfirmed,
isDispatched,
isRedeemFailed,
isCompleted,
hasRedeemResult,
currentStep,
progressText,
resultTitle,
resultDescription,
resultVariant,
}
}
export function normalizeUid(value: unknown) {
return String(value || '')
.trim()
.replace(/\s+/g, '')
}
function resolveConfirmRoleDisabledReason(options: {
isBindingPrepared: boolean
isRoleReady: boolean
isUidMatched: boolean
expectedUid: string
roleId: string
rebindingRole: boolean
}) {
if (!options.isBindingPrepared) {
return '绑定二维码还在准备中,请稍后自动刷新'
}
if (!options.isRoleReady) {
return '请先扫码绑定自己的角色,并刷新角色信息'
}
if (!options.isUidMatched) {
return `当前绑定角色 ID${options.roleId || '-'})与填写 UID${options.expectedUid || '-'})不一致`
}
if (options.rebindingRole) {
return '正在换绑角色,请稍候'
}
return ''
}
function resolveCurrentStep(options: {
hasExpectedUid: boolean
hasRedeemResult: boolean
isRoleConfirmed: boolean
isFeifeiFlow: boolean
isUidMatched?: boolean
}) {
if (!options.hasExpectedUid) {
return 1
}
if (options.hasRedeemResult) {
return 4
}
if (!options.isFeifeiFlow && options.isRoleConfirmed) {
return 3
}
return 2
}
function resolveProgressText(options: {
feifei: ClaimKuaishouFeifeiFlowInfo | null
isFeifeiFlow: boolean
hasExpectedUid: boolean
expectedUid: string
hasRedeemResult: boolean
isRoleConfirmed: boolean
isBindingPrepared: boolean
isUidMatched: boolean
}) {
if (!options.hasExpectedUid) {
return '请填写游戏 UID'
}
if (options.isFeifeiFlow) {
return options.feifei?.rechargeStatusLabel || `UID ${options.expectedUid},请打开领取链接`
}
if (options.hasRedeemResult) {
return '兑换结果已生成'
}
if (options.isRoleConfirmed) {
return '角色已确认,等待兑换'
}
if (!options.isBindingPrepared) {
return '绑定链接准备中,请稍候'
}
if (options.isUidMatched) {
return 'UID 已匹配,请确认兑换'
}
return '请完成扫码绑定并匹配 UID'
}
function resolveResultTitle(options: {
isRedeemFailed: boolean
isCompleted: boolean
isDispatched: boolean
}) {
if (options.isRedeemFailed) {
return '兑换遇到问题'
}
if (options.isCompleted) {
return '兑换成功'
}
if (options.isDispatched) {
return '兑换请求已提交'
}
return '结果已记录'
}
function resolveResultDescription(options: {
detail: ClaimDetailData | null
flow: ClaimKuaishouCloudFlowInfo | null
feifei: ClaimKuaishouFeifeiFlowInfo | null
task: ClaimDetailData['task'] | null
isRedeemFailed: boolean
isCompleted: boolean
}) {
if (options.isRedeemFailed) {
const message = String(
options.task?.lastError ||
options.flow?.dispatch.errorMessage ||
options.feifei?.rechargeResultMessage ||
options.detail?.result?.resultMessage ||
'',
).trim()
return message || '当前兑换流程需要客服处理,后续结果请以后台任务界面为准。'
}
if (options.isCompleted) {
return '当前兑换流程已经完成。'
}
return '你的兑换请求已经提交。后续结果会由系统保存在后台任务界面。'
}
function isDateExpired(value: string | null) {
const normalized = String(value || '').trim()
if (!normalized) {
return false
}
const expiresTime = Date.parse(normalized)
return Number.isFinite(expiresTime) && expiresTime <= Date.now()
}