统一领取流:Step1 填 UID,按平台分叉履约

领取页改为统一 UID 入口;lewan 强制角色 ID 匹配后才发货核销,feifei 将 uid 拼入 H5 并去掉 claim 短链,对外仍返回本站 claimUrl。
This commit is contained in:
yml2213
2026-07-10 13:30:22 +08:00
parent 1155b3c608
commit bb060e81a4
15 changed files with 898 additions and 285 deletions
@@ -4,6 +4,7 @@ import assert from 'node:assert/strict'
import {
buildFeifeiPlatformOrderNo,
resolveKuaishouFeifeiClaimUrl,
resolveKuaishouFeifeiH5UrlWithUid,
resolveKuaishouFeifeiPlatformAmount,
} from './index.js'
import type { TaskRow } from '../../../types/repository/rows.js'
@@ -31,7 +32,7 @@ test('resolveKuaishouFeifeiClaimUrl does not expose local claim token fallback',
)
})
test('resolveKuaishouFeifeiClaimUrl prefers internal short link', () => {
test('resolveKuaishouFeifeiClaimUrl prefers direct h5 over historical short link url', () => {
assert.equal(
resolveKuaishouFeifeiClaimUrl({
shortLink: {
@@ -43,10 +44,24 @@ test('resolveKuaishouFeifeiClaimUrl prefers internal short link', () => {
rechargeUrl: 'https://feifei.example.com/h5/bind?code=very-long',
},
}),
'https://ks.khhao.com/s/AbCd1234',
'https://feifei.example.com/h5/bind?code=very-long',
)
})
test('resolveKuaishouFeifeiH5UrlWithUid appends uid query', () => {
const next = resolveKuaishouFeifeiH5UrlWithUid(
{
h5: {
rechargeUrl: 'http://skin-exchange.yiquyou.icu/h5/bind?code=eyJpdi&product_name=demo',
},
},
'166909256',
)
const url = new URL(next)
assert.equal(url.searchParams.get('uid'), '166909256')
assert.equal(url.searchParams.get('code'), 'eyJpdi')
})
test('buildFeifeiPlatformOrderNo prefers source order number', () => {
assert.equal(
buildFeifeiPlatformOrderNo(createTask({
@@ -7,17 +7,14 @@ import { logIntegration } from '../../../utils/logger.js'
import { parseTaskContext } from '../../../utils/task-json.js'
import { nowIso } from '../../../utils/time.js'
import { TASK_STATUS } from '../../../domain/task-status.js'
import { appendUidToUrl, getClaimIdentityFromContext } from '../../claim/claim-identity.js'
import {
createKuaishouFeifeiOrder,
queryKuaishouFeifeiOrder,
} from '../../platforms/kuaishou-feifei/order-service.js'
import { consumeKuaishouIndustryVouchersForTask } from '../../platforms/kuaishou-industry/voucher-service.js'
import { buildKuaishouIndustryVoucherContext } from '../../platforms/kuaishou-industry/voucher-binding-service.js'
import {
SHORT_LINK_SOURCE_KUAISHOU_FEIFEI_CLAIM,
ensureShortLinkForTarget,
normalizeShortLinkPayload,
} from '../../short-links/short-link-service.js'
import { normalizeShortLinkPayload } from '../../short-links/short-link-service.js'
import type { TaskRow } from '../../../types/repository/rows.js'
type JsonObject = Record<string, any>
@@ -40,15 +37,13 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
const existingClaimUrl = resolveKuaishouFeifeiClaimUrl(flow)
if (flow.orderNo && existingClaimUrl) {
const nextFlow = await ensureKuaishouFeifeiFlowShortLink(flow, task)
const nextContext = {
...taskContext,
kuaishouFeifei: nextFlow,
}
return updateTask(task.id, {
task_status: TASK_STATUS.LINK_GENERATED,
user_action_status: 'pending_claim',
context_json: JSON.stringify(nextContext),
context_json: JSON.stringify({
...taskContext,
kuaishouFeifei: flow,
}),
updated_at: now,
})
}
@@ -80,11 +75,11 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
})
}
const nextFlow = await ensureKuaishouFeifeiFlowShortLink(mergeKuaishouFeifeiOrder(flow, order, {
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
platformOrderNo,
claimUrl: feifeiClaimUrl,
syncedAt: now,
}), task)
})
const updatedTask = await updateTask(task.id, {
task_status: TASK_STATUS.LINK_GENERATED,
user_action_status: 'pending_claim',
@@ -137,10 +132,10 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) {
let resultCode = task.result_code
let resultMessage = task.result_message
let lastError = task.last_error
const nextFlow = await ensureKuaishouFeifeiFlowShortLink(mergeKuaishouFeifeiOrder(flow, order, {
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
syncedAt: now,
claimUrl: resolveKuaishouFeifeiOrderClaimUrl(order) || resolveKuaishouFeifeiClaimUrl(flow),
}), task)
})
let nextIndustryVoucher = taskContext.kuaishouIndustryVoucher
if (order.rechargeStatus === 30) {
@@ -230,6 +225,7 @@ export function normalizeKuaishouFeifeiFlow(value: unknown) {
rechargeResultMessage: String(source.rechargeResultMessage || '').trim(),
claimUrl: String(source.claimUrl || '').trim(),
consumeStatus: String(source.consumeStatus || 'pending').trim(),
// 历史字段保留读取,新流程不再生成 shortLink
shortLink: normalizeShortLinkPayload(source.shortLink),
h5: {
entryUrl: String(h5.entryUrl || '').trim(),
@@ -240,38 +236,38 @@ export function normalizeKuaishouFeifeiFlow(value: unknown) {
}
}
/** 平台原始 H5 链接(不拼 uid、不用短链)。 */
export function resolveKuaishouFeifeiClaimUrl(value: unknown) {
const flow = normalizeKuaishouFeifeiFlow(value)
if (flow.shortLink?.url) {
return flow.shortLink.url
}
return resolveKuaishouFeifeiDirectClaimUrl(flow)
}
export async function ensureKuaishouFeifeiClaimShortLink(task: TaskRow) {
if (!isKuaishouFeifeiTask(task)) {
/** 在已有 expectedUid 时返回拼好 uid 的 H5 链接。 */
export function resolveKuaishouFeifeiH5UrlWithUid(
value: unknown,
expectedUid?: unknown,
) {
const directUrl = resolveKuaishouFeifeiClaimUrl(value)
if (!directUrl) {
return ''
}
const taskContext = parseTaskContext(task)
const flow = normalizeKuaishouFeifeiFlow(taskContext.kuaishouFeifei)
const nextFlow = await ensureKuaishouFeifeiFlowShortLink(flow, task)
const claimUrl = resolveKuaishouFeifeiClaimUrl(nextFlow)
if (JSON.stringify(flow.shortLink || null) === JSON.stringify(nextFlow.shortLink || null)) {
return claimUrl
const uid = String(expectedUid || '').trim()
if (!uid) {
return directUrl
}
await updateTask(task.id, {
context_json: JSON.stringify({
...taskContext,
kuaishouFeifei: nextFlow,
}),
updated_at: nowIso(),
})
return appendUidToUrl(directUrl, uid)
}
return claimUrl
export function resolveKuaishouFeifeiH5UrlWithTaskUid(
task: Partial<TaskRow> | null | undefined,
flowValue?: unknown,
) {
const context = parseTaskContext(task)
const identity = getClaimIdentityFromContext(context)
const flow = flowValue === undefined ? context.kuaishouFeifei : flowValue
return resolveKuaishouFeifeiH5UrlWithUid(flow, identity.expectedUid)
}
function mergeKuaishouFeifeiOrder(
@@ -303,30 +299,6 @@ function mergeKuaishouFeifeiOrder(
}
}
async function ensureKuaishouFeifeiFlowShortLink(
flow: ReturnType<typeof normalizeKuaishouFeifeiFlow>,
task: TaskRow,
) {
const targetUrl = resolveKuaishouFeifeiDirectClaimUrl(flow)
if (!targetUrl) {
return flow
}
if (flow.shortLink?.url && flow.shortLink.targetUrl === targetUrl) {
return flow
}
const shortLink = await ensureShortLinkForTarget(targetUrl, {
source: SHORT_LINK_SOURCE_KUAISHOU_FEIFEI_CLAIM,
taskId: task.id,
})
return {
...flow,
shortLink,
}
}
function resolveKuaishouFeifeiDirectClaimUrl(
flow: ReturnType<typeof normalizeKuaishouFeifeiFlow>,
) {
@@ -335,6 +307,7 @@ function resolveKuaishouFeifeiDirectClaimUrl(
return h5ClaimUrl
}
// 兼容历史 shortLink 中的原始 target
if (flow.shortLink?.targetUrl) {
return flow.shortLink.targetUrl
}