import { TASK_STATUS, hasKuaishouCloudRedeemResultStatus, isKuaishouCloudCompletedStatus, isKuaishouCloudRoleConfirmedStatus, normalizeTaskStatus, } from '@/domain/task-status' import type { ClaimAffiliateDashFlowInfo, 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 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 roleName = flow?.role.name || flow?.binding.roleName || '' const roleId = flow?.role.rid || flow?.binding.roleId || '' const isFeifeiFlow = detail?.flowType === 'kuaishou_feifei' 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 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 || normalizedStatus === TASK_STATUS.CLOSED || String(flow?.dispatch.status || '').trim() === 'failed' || (isFeifeiFlow && [40, 50].includes(Number(feifei?.rechargeStatus || 0))) || (isAffiliateDashFlow && ['ship_failed', 'cancelled'].includes(String(affiliateDash?.orderStatus || '').trim())) 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))) || (isAffiliateDashFlow && ['delivered', 'ship_failed', 'cancelled'].includes( String(affiliateDash?.orderStatus || '').trim(), )) const currentStep = resolveCurrentStep({ hasExpectedUid, hasRedeemResult, isRoleConfirmed, isFeifeiFlow, isAffiliateDashFlow, isAffiliateDashReadyToSubmit, }) const progressText = resolveProgressText({ feifei, isFeifeiFlow, isAffiliateDashFlow, affiliateDash, hasExpectedUid, expectedUid, hasRedeemResult, isRoleConfirmed, isBindingPrepared, isUidMatched, isAffiliateDashReadyToSubmit, isAffiliateDashBindMismatch, }) const resultTitle = resolveResultTitle({ isRedeemFailed, isCompleted, isDispatched, isAffiliateDashFlow, affiliateDash, }) const resultDescription = resolveResultDescription({ detail, flow, feifei, affiliateDash, task, isRedeemFailed, isCompleted, }) const resultVariant: ResultVariant = isRedeemFailed ? 'warning' : isCompleted || isDispatched ? 'success' : 'info' return { flow, feifei, affiliateDash, order, orderItem, product, task, claimIdentity, expectedUid, hasExpectedUid, roleName, roleId, isFeifeiFlow, isAffiliateDashFlow, affiliateDashExpectedUid, affiliateDashBoundAccount, isAffiliateDashBound, isAffiliateDashUidMatched, isAffiliateDashBindMismatch, isAffiliateDashReadyToSubmit, 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 isAffiliateDashFlow: boolean isAffiliateDashReadyToSubmit: boolean }) { if (!options.hasExpectedUid) { return 1 } if (options.hasRedeemResult) { return 4 } if (options.isAffiliateDashFlow) { if (!options.isAffiliateDashReadyToSubmit) { return 2 } return 3 } if (!options.isFeifeiFlow && options.isRoleConfirmed) { return 3 } return 2 } function resolveProgressText(options: { feifei: ClaimKuaishouFeifeiFlowInfo | null affiliateDash: ClaimAffiliateDashFlowInfo | null isFeifeiFlow: boolean isAffiliateDashFlow: boolean hasExpectedUid: boolean expectedUid: string hasRedeemResult: boolean isRoleConfirmed: boolean isBindingPrepared: boolean isUidMatched: boolean isAffiliateDashReadyToSubmit: boolean isAffiliateDashBindMismatch: boolean }) { 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 '绑定账号确认中,请稍候' } return '已绑定,请提交发货' } 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 isAffiliateDashFlow: boolean affiliateDash: ClaimAffiliateDashFlowInfo | null }) { if (options.isRedeemFailed) { return '发货遇到问题' } if (options.isCompleted) { return '兑换成功' } if (options.isDispatched) { return '兑换请求已提交' } if (options.isAffiliateDashFlow && options.affiliateDash?.orderStatus === 'delivered') { return '发货已完成' } return '结果已记录' } function resolveResultDescription(options: { detail: ClaimDetailData | null flow: ClaimKuaishouCloudFlowInfo | null feifei: ClaimKuaishouFeifeiFlowInfo | null affiliateDash: ClaimAffiliateDashFlowInfo | 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.affiliateDash?.failureReason || options.detail?.result?.resultMessage || '', ).trim() return message || '当前流程需要客服处理,后续结果请以后台任务界面为准。' } if (options.isCompleted) { return '当前兑换流程已经完成。' } if (options.affiliateDash) { 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() }