优化 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
@@ -91,7 +91,7 @@ export function resolveBoundRoleUid(flowLike: unknown): string {
export function assertClaimExpectedUidReady(task: Partial<TaskRow> | null | undefined): string {
const expectedUid = getClaimIdentityFromTask(task).expectedUid
if (!expectedUid) {
throw createHttpError('请先填写游戏 UID', {
throw createHttpError('请先通过领取页填写游戏 UID(旧单无 UID 不可自动履约)', {
statusCode: 409,
errorCode: 'claim_uid_not_submitted',
})
@@ -99,6 +99,72 @@ export function assertClaimExpectedUidReady(task: Partial<TaskRow> | null | unde
return expectedUid
}
/**
* lewan 自动发货前:必须已有 expectedUid(不再回落「仅确认角色」旧路径)。
* mock 任务可跳过。
*/
export function assertLewanAutoFulfillmentUidReady(
task: Partial<TaskRow> | null | undefined,
options: { allowMockSkip?: boolean; errorCodePrefix?: string } = {},
): string {
const context = parseTaskContext(task)
const flow =
context.kuaishouCloudFulfillment && typeof context.kuaishouCloudFulfillment === 'object'
? (context.kuaishouCloudFulfillment as JsonObject)
: {}
const mock = flow.mock && typeof flow.mock === 'object' ? (flow.mock as JsonObject) : null
if (options.allowMockSkip !== false && mock?.enabled === true) {
return getClaimIdentityFromTask(task).expectedUid
}
const expectedUid = getClaimIdentityFromTask(task).expectedUid
if (!expectedUid) {
const prefix = String(options.errorCodePrefix || 'kuaishou_cloud').trim() || 'kuaishou_cloud'
throw createHttpError(
'该 lewan 任务尚未填写游戏 UID,请用户打开领取页提交 UID 后再发货(旧单不再支持无 UID 自动发货)',
{
statusCode: 409,
errorCode: `${prefix}_expected_uid_required`,
},
)
}
return expectedUid
}
export function buildClaimIdentityAdminSummary(
context: unknown,
options: { flowLike?: unknown; taskRoleId?: unknown; taskRoleName?: unknown } = {},
) {
const identity = getClaimIdentityFromContext(context)
const boundUid = resolveBoundRoleUid(options.flowLike) || normalizeClaimUid(options.taskRoleId)
const flow = isPlainObject(options.flowLike) ? options.flowLike : {}
const binding = isPlainObject(flow.binding) ? flow.binding : {}
const role = isPlainObject(flow.role) ? flow.role : {}
const boundRoleName = String(
role.name || binding.roleName || options.taskRoleName || '',
).trim()
const ready = Boolean(identity.expectedUid)
const uidMatched = ready && boundUid ? isClaimUidMatched(identity.expectedUid, boundUid) : null
return {
expectedUid: identity.expectedUid,
submittedAt: identity.submittedAt,
source: identity.source,
ready,
boundUid,
boundRoleName,
uidMatched,
compatibilityMode: ready ? ('uid' as const) : ('legacy_no_uid' as const),
note: ready
? uidMatched === true
? 'UID 已匹配,可继续履约'
: uidMatched === false
? '绑定角色 ID 与填写 UID 不一致'
: '已填 UID,等待绑定识别'
: '旧单或未提交 UID:用户须先打开领取页填写 UID,否则禁止自动发货',
}
}
export function assertBoundUidMatchesExpected(
task: Partial<TaskRow> | null | undefined,
flowLike: unknown,