优化 UID 履约闸门、运营可见性与一键兑换

lewan 发货强制 expectedUid 并取消无 UID 旧路径回落;任务详情展示 UID 匹配态;领取页匹配后可一键兑换;补充 identity 单测并收口短链说明。
This commit is contained in:
yml2213
2026-07-10 13:51:02 +08:00
parent dc89188f7a
commit 8de56b0354
13 changed files with 383 additions and 152 deletions
@@ -681,13 +681,57 @@ function FulfillmentOverviewPanel({
}) {
const executor = resolveTaskExecutorDisplay(detail.task.executorKey)
const steps = buildFulfillmentOverviewSteps(detail, claimUrl, claimLinkInvalid)
const currentBlocker = resolveTaskBlocker(detail.task, claimUrl, claimLinkInvalid)
const claimIdentity = detail.claimIdentity
const currentBlocker = resolveTaskBlocker(
detail.task,
claimUrl,
claimLinkInvalid,
claimIdentity,
)
const uidMatchTag =
claimIdentity?.uidMatched === true
? { color: 'success' as const, text: 'UID 已匹配' }
: claimIdentity?.uidMatched === false
? { color: 'error' as const, text: 'UID 不一致' }
: claimIdentity?.ready
? { color: 'processing' as const, text: '已填 UID' }
: { color: 'warning' as const, text: '未填 UID' }
return (
<Card
title="履约链路总览"
extra={<Tag color={executor.color}>{executor.label}</Tag>}
>
<Descriptions
size="small"
column={{ xs: 1, sm: 2, md: 3 }}
className="platform-section-gap"
style={{ marginBottom: 12 }}
>
<Descriptions.Item label="填写 UID">
{claimIdentity?.expectedUid || (
<Typography.Text type="warning"></Typography.Text>
)}
</Descriptions.Item>
<Descriptions.Item label="绑定角色 ID">
{claimIdentity?.boundUid || detail.task.roleId || '-'}
</Descriptions.Item>
<Descriptions.Item label="绑定角色名">
{claimIdentity?.boundRoleName || detail.task.roleName || '-'}
</Descriptions.Item>
<Descriptions.Item label="匹配状态">
<Tag color={uidMatchTag.color}>{uidMatchTag.text}</Tag>
</Descriptions.Item>
<Descriptions.Item label="兼容模式">
{claimIdentity?.compatibilityMode === 'legacy_no_uid' ? '旧单无 UID' : 'UID 闸门'}
</Descriptions.Item>
<Descriptions.Item label="说明" span={3}>
<Typography.Text type="secondary">
{claimIdentity?.note || '暂无 UID 信息'}
</Typography.Text>
</Descriptions.Item>
</Descriptions>
<div className="fulfillment-overview-grid">
{steps.map((step) => (
<div key={step.key} className="fulfillment-step-card">
@@ -791,6 +835,7 @@ function resolveTaskBlocker(
task: AdminTaskDetail['task'],
claimUrl: string,
claimLinkInvalid: boolean,
claimIdentity?: AdminTaskDetail['claimIdentity'],
): { level: 'success' | 'info' | 'warning' | 'error'; title: string; detail: string } {
if (['completed', 'redeemed', 'delivered'].includes(task.status) || task.deliveryStatus === 'delivered') {
return {
@@ -808,6 +853,29 @@ function resolveTaskBlocker(
}
}
if (
task.executorKey === 'kuaishou_ct_assisted' &&
claimIdentity &&
!claimIdentity.ready
) {
return {
level: 'warning',
title: '等待用户填写 UID',
detail: '旧单或新单均需用户打开领取页提交游戏 UID,否则禁止自动发货。',
}
}
if (
task.executorKey === 'kuaishou_ct_assisted' &&
claimIdentity?.uidMatched === false
) {
return {
level: 'error',
title: 'UID 与绑定角色不一致',
detail: `填写 ${claimIdentity.expectedUid || '-'},绑定 ${claimIdentity.boundUid || '-'},请用户换绑或更正 UID。`,
}
}
if (claimLinkInvalid) {
return {
level: 'warning',
+41 -15
View File
@@ -521,6 +521,7 @@ function createClaimSnapshot(
hasRedeemResult,
isRoleConfirmed,
isFeifeiFlow,
isUidMatched,
})
const progressText = resolveProgressText({
feifei,
@@ -613,6 +614,7 @@ function resolveCurrentStep(options: {
hasRedeemResult: boolean
isRoleConfirmed: boolean
isFeifeiFlow: boolean
isUidMatched?: boolean
}) {
if (!options.hasExpectedUid) {
return 1
@@ -620,6 +622,10 @@ function resolveCurrentStep(options: {
if (options.hasRedeemResult) {
return 4
}
// UID 匹配后仍停留在绑定步,提供一键兑换;已 ROLE_CONFIRMED 走确认页
if (!options.isFeifeiFlow && options.isRoleConfirmed && !options.isUidMatched) {
return 3
}
if (!options.isFeifeiFlow && options.isRoleConfirmed) {
return 3
}
@@ -857,10 +863,12 @@ function KuaishouCloudClaimSteps({
refreshingRole={refreshingRole}
confirmingRole={confirmingRole}
rebindingRole={rebindingRole}
redeeming={redeeming}
onOpenBindUrl={onOpenBindUrl}
onRefreshRole={onRefreshRole}
onRebindRole={onRebindRole}
onConfirmRole={onConfirmRole}
onConfirmRedeem={onConfirmRedeem}
uidInput={uidInput}
submittingUid={submittingUid}
onUidChange={onUidChange}
@@ -875,10 +883,12 @@ function ClaimBindingStep({
refreshingRole,
confirmingRole,
rebindingRole,
redeeming,
onOpenBindUrl,
onRefreshRole,
onRebindRole,
onConfirmRole,
onConfirmRedeem,
uidInput,
submittingUid,
onUidChange,
@@ -889,10 +899,12 @@ function ClaimBindingStep({
refreshingRole: boolean
confirmingRole: boolean
rebindingRole: boolean
redeeming: boolean
onOpenBindUrl: (useCurrentPage?: boolean) => void
onRefreshRole: () => void
onRebindRole: () => void
onConfirmRole: () => void
onConfirmRedeem: () => void
uidInput: string
submittingUid: boolean
onUidChange: (value: string) => void
@@ -962,25 +974,39 @@ function ClaimBindingStep({
size="large"
icon={<SwapOutlined />}
loading={rebindingRole}
disabled={confirmingRole}
disabled={confirmingRole || redeeming}
onClick={onRebindRole}
>
</Button>
<Tooltip title={snapshot.confirmRoleDisabledReason || undefined}>
<span className="claim-action-tooltip-wrap">
<Button
type="primary"
size="large"
icon={<CheckCircleOutlined />}
loading={confirmingRole}
disabled={Boolean(snapshot.confirmRoleDisabledReason)}
onClick={onConfirmRole}
>
UID
</Button>
</span>
</Tooltip>
{snapshot.isUidMatched ? (
<Button
type="primary"
size="large"
danger
icon={<CheckCircleOutlined />}
loading={redeeming}
disabled={rebindingRole}
onClick={onConfirmRedeem}
>
UID
</Button>
) : (
<Tooltip title={snapshot.confirmRoleDisabledReason || undefined}>
<span className="claim-action-tooltip-wrap">
<Button
type="primary"
size="large"
icon={<CheckCircleOutlined />}
loading={confirmingRole}
disabled={Boolean(snapshot.confirmRoleDisabledReason)}
onClick={onConfirmRole}
>
</Button>
</span>
</Tooltip>
)}
</Space>
<div className="claim-meta-footer">
+13
View File
@@ -105,6 +105,17 @@ export interface AdminTaskDetail {
resultMessage: string
}>
}
claimIdentity: null | {
expectedUid: string
submittedAt: string | null
source: string
ready: boolean
boundUid: string
boundRoleName: string
uidMatched: boolean | null
compatibilityMode: 'uid' | 'legacy_no_uid'
note: string
}
kuaishouCloudFulfillment: null | {
flowType: string
configId: string
@@ -135,6 +146,8 @@ export interface AdminTaskDetail {
bindProbeAt: string | null
bindProbeStatus: string
bindProbeMessage: string
roleName?: string
roleId?: string
}
role: {
status: string