修复feifei领取链接直返

This commit is contained in:
yml2213
2026-07-07 15:46:08 +08:00
parent 177f72dd2c
commit affba7e924
4 changed files with 72 additions and 35 deletions
@@ -5,6 +5,7 @@ import { getOrderById, listOrders } from '../../repositories/order-repo.js'
import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js'
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
import { listCloudtentaclesSources } from '../platforms/cloudtentacles/source-config-service.js'
import { resolveKuaishouFeifeiClaimUrl } from '../fulfillment/kuaishou-feifei/index.js'
import { createHttpError } from '../../utils/http.js'
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import {
@@ -189,7 +190,10 @@ export async function getAdminTaskDetail(
const taskContext = parseTaskContext(task)
const taskState = parseTaskState(task)
const viewerContext = createAdminViewerContext(session)
const claimUrl = claimToken ? buildClaimUrl(claimToken.token) : ''
const directClaimUrl = String(task.executor_key || '').trim() === 'kuaishou_feifei'
? resolveKuaishouFeifeiClaimUrl(taskContext.kuaishouFeifei)
: ''
const claimUrl = directClaimUrl || (claimToken ? buildClaimUrl(claimToken.token) : '')
const screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext)
const cloudSourceLabelMap = buildCloudSourceLabelMap()
const kuaishouCloudFulfillment =
@@ -227,12 +231,12 @@ export async function getAdminTaskDetail(
quantity: orderItem.quantity,
}
: null,
claimToken: claimToken
claimToken: claimToken || directClaimUrl
? {
primaryClaimTokenId: claimToken.id,
token: viewerContext.canViewSensitiveTaskData ? claimToken.token : '',
status: claimToken.status,
expiredAt: claimToken.expired_at,
primaryClaimTokenId: claimToken?.id || 0,
token: viewerContext.canViewSensitiveTaskData ? (claimToken?.token || '') : '',
status: claimToken?.status || 'external',
expiredAt: claimToken?.expired_at || '',
claimUrl,
}
: null,
@@ -0,0 +1,27 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { resolveKuaishouFeifeiClaimUrl } from './index.js'
test('resolveKuaishouFeifeiClaimUrl prefers feifei h5 url over local claim url', () => {
assert.equal(
resolveKuaishouFeifeiClaimUrl({
claimUrl: 'https://ks.khhao.com/#/claim/local-token',
h5: {
entryUrl: 'https://feifei.example.com/h5/exchange',
rechargeUrl: 'https://feifei.example.com/h5/bind?code=abc',
},
}),
'https://feifei.example.com/h5/bind?code=abc',
)
})
test('resolveKuaishouFeifeiClaimUrl does not expose local claim token fallback', () => {
assert.equal(
resolveKuaishouFeifeiClaimUrl({
claimUrl: 'local-token',
h5: {},
}),
'',
)
})
@@ -4,7 +4,6 @@ import { createHttpError } from '../../../utils/http.js'
import { parseTaskContext } from '../../../utils/task-json.js'
import { nowIso } from '../../../utils/time.js'
import { TASK_STATUS } from '../../../domain/task-status.js'
import { buildClaimUrl, createTaskClaimToken } from '../../claim/claim-service.js'
import {
createKuaishouFeifeiOrder,
queryKuaishouFeifeiOrder,
@@ -29,20 +28,18 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
const now = nowIso()
const taskContext = parseTaskContext(task)
const flow = normalizeKuaishouFeifeiFlow(taskContext.kuaishouFeifei)
const claimLinkState = await ensureTaskClaimLink(task)
if (flow.orderNo && flow.h5.rechargeUrl) {
const existingClaimUrl = resolveKuaishouFeifeiClaimUrl(flow)
if (flow.orderNo && existingClaimUrl) {
const nextContext = {
...taskContext,
kuaishouFeifei: {
...flow,
claimUrl: claimLinkState.claimUrl,
claimUrl: existingClaimUrl,
},
}
return updateTask(task.id, {
task_status: TASK_STATUS.LINK_GENERATED,
claim_token: claimLinkState.token || task.claim_token || '',
claim_expires_at: claimLinkState.expiredAt || task.claim_expires_at || null,
user_action_status: 'pending_claim',
context_json: JSON.stringify(nextContext),
updated_at: now,
@@ -62,15 +59,21 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
productCode: flow.productCode,
platformBuyNum: 1,
})
const feifeiClaimUrl = resolveKuaishouFeifeiOrderClaimUrl(order)
if (!feifeiClaimUrl) {
throw createHttpError('kuaishou-feifei 未返回领取链接', {
statusCode: 502,
errorCode: 'kuaishou_feifei_missing_claim_url',
})
}
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
platformOrderNo,
claimUrl: claimLinkState.claimUrl,
claimUrl: feifeiClaimUrl,
syncedAt: now,
})
const updatedTask = await updateTask(task.id, {
task_status: TASK_STATUS.LINK_GENERATED,
claim_token: claimLinkState.token || task.claim_token || '',
claim_expires_at: claimLinkState.expiredAt || task.claim_expires_at || null,
user_action_status: 'pending_claim',
result_code: 'kuaishou_feifei_order_created',
result_message: order.rechargeStatusLabel || 'kuaishou-feifei 订单已创建',
@@ -122,7 +125,7 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) {
let lastError = task.last_error
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
syncedAt: now,
claimUrl: flow.claimUrl,
claimUrl: resolveKuaishouFeifeiOrderClaimUrl(order) || resolveKuaishouFeifeiClaimUrl(flow),
})
if (order.rechargeStatus === 30) {
@@ -197,6 +200,16 @@ export function normalizeKuaishouFeifeiFlow(value: unknown) {
}
}
export function resolveKuaishouFeifeiClaimUrl(value: unknown) {
const flow = normalizeKuaishouFeifeiFlow(value)
const h5ClaimUrl = flow.h5.rechargeUrl || flow.h5.entryUrl
if (h5ClaimUrl) {
return h5ClaimUrl
}
return isLocalClaimUrl(flow.claimUrl) ? '' : flow.claimUrl
}
function mergeKuaishouFeifeiOrder(
flow: ReturnType<typeof normalizeKuaishouFeifeiFlow>,
order: Awaited<ReturnType<typeof createKuaishouFeifeiOrder>>,
@@ -225,25 +238,13 @@ function mergeKuaishouFeifeiOrder(
}
}
async function ensureTaskClaimLink(task: TaskRow) {
const tokenStatus = String(task?.primary_claim_token_status || '').trim()
const token = String(task?.primary_claim_token || task?.claim_token || '').trim()
const expiredAt = task?.primary_claim_expires_at || task?.claim_expires_at || null
function resolveKuaishouFeifeiOrderClaimUrl(order: Awaited<ReturnType<typeof createKuaishouFeifeiOrder>>) {
return String(order.h5.rechargeUrl || order.h5.entryUrl || '').trim()
}
if (tokenStatus === 'active' && token && expiredAt && new Date(expiredAt).getTime() > Date.now()) {
return {
token,
expiredAt,
claimUrl: buildClaimUrl(token),
}
}
const claimToken = await createTaskClaimToken(task.id)
return {
token: claimToken.token,
expiredAt: claimToken.expired_at,
claimUrl: claimToken.claimUrl,
}
function isLocalClaimUrl(value: unknown) {
const url = String(value || '').trim()
return !/^https?:\/\//i.test(url) || url.includes('/#/claim/')
}
function buildFeifeiPlatformOrderNo(task: TaskRow) {
@@ -2,6 +2,7 @@ import { findLatestOrderByPlatformOrderId } from '../../repositories/order-repo.
import { listTasksByOrderId } from '../../repositories/task-repo.js'
import { buildClaimUrl } from '../claim/claim-service.js'
import { ensureTaskClaimLink } from '../fulfillment/kuaishou-cloud/index.js'
import { resolveKuaishouFeifeiClaimUrl } from '../fulfillment/kuaishou-feifei/index.js'
import { logIntegration } from '../../utils/logger.js'
import {
OPEN_91_MANUAL_FAILED_STATUS,
@@ -88,7 +89,11 @@ export async function queryOpen91Order(payload: JsonObject = {}, { requestId = '
let expireTime: unknown = task.primary_claim_expires_at || task.claim_expires_at || ''
const primaryToken = String(task.primary_claim_token || task.claim_token || '').trim()
if (primaryToken) {
if (String(task.executor_key || '').trim() === 'kuaishou_feifei') {
const context = parseJsonObject(task.context_json)
claimUrl = resolveKuaishouFeifeiClaimUrl(context.kuaishouFeifei)
expireTime = ''
} else if (primaryToken) {
claimUrl = buildClaimUrl(primaryToken)
} else if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
try {