From 2a0eea40e9d20e470eb0c7e6e86c112a4cd585fb Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 6 Oct 2026 11:06:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(claim):=20=E9=85=8D=E5=90=88=E6=96=B9?= =?UTF-8?q?=E6=A1=88B=E6=94=AF=E6=8C=81=E4=B9=B0=E5=AE=B6=E7=BA=A0?= =?UTF-8?q?=E9=94=99=E6=89=AB=E7=A0=81UID=E8=87=AA=E6=84=88=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=EF=BC=8C=E8=87=AA=E5=BB=BA=E9=A1=B5=E9=9D=A2=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=A1=AE=E8=AE=A4=E6=9B=B4=E6=8D=A2=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=B8=8E=E5=BF=AB=E6=8D=B7=E5=A1=AB=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../claim/affiliate-dash-bind-state.test.ts | 77 +++++++++++++++++++ .../claim/kuaishou-cloud-claim-service.ts | 53 +++++++++++++ .../pages/claim/ClaimAffiliateDashSteps.tsx | 58 ++++++++++++-- 3 files changed, 181 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/services/claim/affiliate-dash-bind-state.test.ts b/apps/backend/src/services/claim/affiliate-dash-bind-state.test.ts index b549e291..79305e7f 100644 --- a/apps/backend/src/services/claim/affiliate-dash-bind-state.test.ts +++ b/apps/backend/src/services/claim/affiliate-dash-bind-state.test.ts @@ -7,6 +7,8 @@ import { canReuseAffiliateDashBindSnapshot, isAffiliateDashBindSnapshotStale, isAffiliateDashSnapshotFresh, + isRockAffiliateDashSku, + shouldRefuseRockRebind, shouldRefreshAffiliateDashBindStateForSnapshot, } from './kuaishou-cloud-claim-service.js' @@ -123,6 +125,81 @@ test('canReuseAffiliateDashBindSnapshot reuses verified matching bound account', ) }) +test('rock rebind is refused after mismatch or after a successful bind changes uid', () => { + assert.equal(isRockAffiliateDashSku('rock_black_tshirt'), true) + assert.equal(isRockAffiliateDashSku('top_black_elite_trainer'), false) + assert.equal( + shouldRefuseRockRebind({ + sku: 'rock_black_tshirt', + bindUuid: 'bind-1', + bindMismatch: true, + }), + true, + ) + // 方案 B:买家改回扫码真实账号时放行自愈,改第三个账号时拒绝 + assert.equal( + shouldRefuseRockRebind( + { + sku: 'rock_black_tshirt', + bindUuid: 'bind-1', + boundAccount: '3454648872', + bindMismatch: true, + }, + '3454648872', + ), + false, + ) + assert.equal( + shouldRefuseRockRebind( + { + sku: 'rock_black_tshirt', + bindUuid: 'bind-1', + boundAccount: '3454648872', + bindMismatch: true, + }, + '9999999999', + ), + true, + ) + assert.equal( + shouldRefuseRockRebind( + { + sku: 'rock_black_tshirt', + bindUuid: 'bind-1', + bound: true, + boundAccount: '111', + expectedGameAccount: '111', + }, + '222', + ), + true, + ) + assert.equal( + shouldRefuseRockRebind( + { + sku: 'rock_black_tshirt', + bindUuid: 'bind-1', + bound: false, + bindMismatch: false, + expectedGameAccount: '111', + }, + '222', + ), + false, + ) + assert.equal( + shouldRefuseRockRebind( + { + sku: 'skin_other', + bindUuid: 'bind-1', + bindMismatch: true, + }, + '222', + ), + false, + ) +}) + test('canReuseAffiliateDashBindSnapshot refuses mismatched bound account', () => { assert.equal( canReuseAffiliateDashBindSnapshot( diff --git a/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts b/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts index 738e9c61..0477f4c2 100644 --- a/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts +++ b/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts @@ -203,6 +203,13 @@ async function submitAffiliateDashClaimUid( updated_at: now, } + if (shouldRefuseRockRebind(flow, expectedUid)) { + throw createHttpError(ROCK_CLAIM_TOKEN_LOCKED_MESSAGE, { + statusCode: 409, + errorCode: 'affiliate_dash_rock_token_locked', + }) + } + if (!flow.orderNo) { await updateTask(context.task.id, { ...baseTaskPatch, @@ -472,6 +479,40 @@ function resolveAffiliateDashRecordedBindUid(flow: AffiliateDashFlowSnapshot) { return normalizeUid(flow.expectedGameAccount || flow.gameAccount) } +export const ROCK_CLAIM_TOKEN_LOCKED_MESSAGE = + '该订单提货链接已绑定失败,不能更换新的提货号,请联系客服处理' + +export function isRockAffiliateDashSku(sku: unknown) { + return String(sku || '') + .trim() + .toLowerCase() + .startsWith('rock_') +} + +/** Rock 一单一号:绑定失败禁止换第三方新号;但如果买家纠正为当前扫码角色的 UID,允许放行自愈。 */ +export function shouldRefuseRockRebind(flowLike: unknown, nextUid?: unknown) { + const flow = normalizeAffiliateDashFlow(flowLike) + if (!isRockAffiliateDashSku(flow.sku)) { + return false + } + const next = normalizeUid(nextUid) + const boundAccount = normalizeUid(flow.boundAccount) + + // 方案 B:如果买家确认修改为当前扫码反查到的角色 UID,放行自愈 + if (flow.bindMismatch) { + if (next && boundAccount && next === boundAccount) { + return false + } + return true + } + + if (!flow.bound) { + return false + } + const current = normalizeUid(flow.boundAccount || flow.expectedGameAccount || flow.gameAccount) + return Boolean(next && current && next !== current) +} + export function isAffiliateDashBindSnapshotStale(flowLike: unknown, expectedUid: unknown): boolean { const flow = normalizeAffiliateDashFlow(flowLike) const expected = normalizeUid(expectedUid) @@ -577,6 +618,18 @@ async function refreshAffiliateDashBindState(task: TaskRow): Promise @@ -92,7 +100,39 @@ export function AffiliateDashClaimPanel({ type="warning" showIcon message="绑定账号与填写 UID 不一致" - description={`当前识别到 ${displayBoundAccount},请使用你填写的 UID(${expectedAccount || '-'})对应账号重新扫码绑定。`} + description={ +
+

+ 当前微信/QQ扫码识别到角色: + + {roleName ? `「${roleName}」` : ''} ({displayBoundAccount}) + + , 与您填写的 UID({expectedAccount || '-'})不一致。 +

+ {boundAccount ? ( +
+ + 如确系本人账号,请修改上方 UID 为扫码账号({boundAccount})并点击更新确认绑定: + + +
+ ) : ( +

+ 如需更换微信号请联系客服处理。 +

+ )} +
+ } style={{ marginBottom: 16 }} /> ) : null} @@ -186,7 +226,11 @@ export function AffiliateDashClaimPanel({ {hasBindQr ? (
- 如需换号可重新扫码绑定 + {String(affiliateDash.sku || '') + .toLowerCase() + .startsWith('rock_') + ? '请使用填写的 UID 对应账号扫码。绑错后不能更换提货号,需联系客服' + : '如需换号可重新扫码绑定'}