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 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() }