修复feifei领取链接直返
This commit is contained in:
@@ -5,6 +5,7 @@ import { getOrderById, listOrders } from '../../repositories/order-repo.js'
|
|||||||
import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js'
|
import { getTaskById, listTasks, listTasksByOrderId } from '../../repositories/task-repo.js'
|
||||||
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
|
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
|
||||||
import { listCloudtentaclesSources } from '../platforms/cloudtentacles/source-config-service.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 { createHttpError } from '../../utils/http.js'
|
||||||
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
|
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
|
||||||
import {
|
import {
|
||||||
@@ -189,7 +190,10 @@ export async function getAdminTaskDetail(
|
|||||||
const taskContext = parseTaskContext(task)
|
const taskContext = parseTaskContext(task)
|
||||||
const taskState = parseTaskState(task)
|
const taskState = parseTaskState(task)
|
||||||
const viewerContext = createAdminViewerContext(session)
|
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 screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext)
|
||||||
const cloudSourceLabelMap = buildCloudSourceLabelMap()
|
const cloudSourceLabelMap = buildCloudSourceLabelMap()
|
||||||
const kuaishouCloudFulfillment =
|
const kuaishouCloudFulfillment =
|
||||||
@@ -227,12 +231,12 @@ export async function getAdminTaskDetail(
|
|||||||
quantity: orderItem.quantity,
|
quantity: orderItem.quantity,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
claimToken: claimToken
|
claimToken: claimToken || directClaimUrl
|
||||||
? {
|
? {
|
||||||
primaryClaimTokenId: claimToken.id,
|
primaryClaimTokenId: claimToken?.id || 0,
|
||||||
token: viewerContext.canViewSensitiveTaskData ? claimToken.token : '',
|
token: viewerContext.canViewSensitiveTaskData ? (claimToken?.token || '') : '',
|
||||||
status: claimToken.status,
|
status: claimToken?.status || 'external',
|
||||||
expiredAt: claimToken.expired_at,
|
expiredAt: claimToken?.expired_at || '',
|
||||||
claimUrl,
|
claimUrl,
|
||||||
}
|
}
|
||||||
: null,
|
: 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 { parseTaskContext } from '../../../utils/task-json.js'
|
||||||
import { nowIso } from '../../../utils/time.js'
|
import { nowIso } from '../../../utils/time.js'
|
||||||
import { TASK_STATUS } from '../../../domain/task-status.js'
|
import { TASK_STATUS } from '../../../domain/task-status.js'
|
||||||
import { buildClaimUrl, createTaskClaimToken } from '../../claim/claim-service.js'
|
|
||||||
import {
|
import {
|
||||||
createKuaishouFeifeiOrder,
|
createKuaishouFeifeiOrder,
|
||||||
queryKuaishouFeifeiOrder,
|
queryKuaishouFeifeiOrder,
|
||||||
@@ -29,20 +28,18 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
|
|||||||
const now = nowIso()
|
const now = nowIso()
|
||||||
const taskContext = parseTaskContext(task)
|
const taskContext = parseTaskContext(task)
|
||||||
const flow = normalizeKuaishouFeifeiFlow(taskContext.kuaishouFeifei)
|
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 = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
kuaishouFeifei: {
|
kuaishouFeifei: {
|
||||||
...flow,
|
...flow,
|
||||||
claimUrl: claimLinkState.claimUrl,
|
claimUrl: existingClaimUrl,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return updateTask(task.id, {
|
return updateTask(task.id, {
|
||||||
task_status: TASK_STATUS.LINK_GENERATED,
|
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',
|
user_action_status: 'pending_claim',
|
||||||
context_json: JSON.stringify(nextContext),
|
context_json: JSON.stringify(nextContext),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
@@ -62,15 +59,21 @@ export async function prepareKuaishouFeifeiTask(task: TaskRow) {
|
|||||||
productCode: flow.productCode,
|
productCode: flow.productCode,
|
||||||
platformBuyNum: 1,
|
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, {
|
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
|
||||||
platformOrderNo,
|
platformOrderNo,
|
||||||
claimUrl: claimLinkState.claimUrl,
|
claimUrl: feifeiClaimUrl,
|
||||||
syncedAt: now,
|
syncedAt: now,
|
||||||
})
|
})
|
||||||
const updatedTask = await updateTask(task.id, {
|
const updatedTask = await updateTask(task.id, {
|
||||||
task_status: TASK_STATUS.LINK_GENERATED,
|
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',
|
user_action_status: 'pending_claim',
|
||||||
result_code: 'kuaishou_feifei_order_created',
|
result_code: 'kuaishou_feifei_order_created',
|
||||||
result_message: order.rechargeStatusLabel || 'kuaishou-feifei 订单已创建',
|
result_message: order.rechargeStatusLabel || 'kuaishou-feifei 订单已创建',
|
||||||
@@ -122,7 +125,7 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) {
|
|||||||
let lastError = task.last_error
|
let lastError = task.last_error
|
||||||
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
|
const nextFlow = mergeKuaishouFeifeiOrder(flow, order, {
|
||||||
syncedAt: now,
|
syncedAt: now,
|
||||||
claimUrl: flow.claimUrl,
|
claimUrl: resolveKuaishouFeifeiOrderClaimUrl(order) || resolveKuaishouFeifeiClaimUrl(flow),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (order.rechargeStatus === 30) {
|
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(
|
function mergeKuaishouFeifeiOrder(
|
||||||
flow: ReturnType<typeof normalizeKuaishouFeifeiFlow>,
|
flow: ReturnType<typeof normalizeKuaishouFeifeiFlow>,
|
||||||
order: Awaited<ReturnType<typeof createKuaishouFeifeiOrder>>,
|
order: Awaited<ReturnType<typeof createKuaishouFeifeiOrder>>,
|
||||||
@@ -225,25 +238,13 @@ function mergeKuaishouFeifeiOrder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureTaskClaimLink(task: TaskRow) {
|
function resolveKuaishouFeifeiOrderClaimUrl(order: Awaited<ReturnType<typeof createKuaishouFeifeiOrder>>) {
|
||||||
const tokenStatus = String(task?.primary_claim_token_status || '').trim()
|
return String(order.h5.rechargeUrl || order.h5.entryUrl || '').trim()
|
||||||
const token = String(task?.primary_claim_token || task?.claim_token || '').trim()
|
|
||||||
const expiredAt = task?.primary_claim_expires_at || task?.claim_expires_at || null
|
|
||||||
|
|
||||||
if (tokenStatus === 'active' && token && expiredAt && new Date(expiredAt).getTime() > Date.now()) {
|
|
||||||
return {
|
|
||||||
token,
|
|
||||||
expiredAt,
|
|
||||||
claimUrl: buildClaimUrl(token),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const claimToken = await createTaskClaimToken(task.id)
|
function isLocalClaimUrl(value: unknown) {
|
||||||
return {
|
const url = String(value || '').trim()
|
||||||
token: claimToken.token,
|
return !/^https?:\/\//i.test(url) || url.includes('/#/claim/')
|
||||||
expiredAt: claimToken.expired_at,
|
|
||||||
claimUrl: claimToken.claimUrl,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFeifeiPlatformOrderNo(task: TaskRow) {
|
function buildFeifeiPlatformOrderNo(task: TaskRow) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { findLatestOrderByPlatformOrderId } from '../../repositories/order-repo.
|
|||||||
import { listTasksByOrderId } from '../../repositories/task-repo.js'
|
import { listTasksByOrderId } from '../../repositories/task-repo.js'
|
||||||
import { buildClaimUrl } from '../claim/claim-service.js'
|
import { buildClaimUrl } from '../claim/claim-service.js'
|
||||||
import { ensureTaskClaimLink } from '../fulfillment/kuaishou-cloud/index.js'
|
import { ensureTaskClaimLink } from '../fulfillment/kuaishou-cloud/index.js'
|
||||||
|
import { resolveKuaishouFeifeiClaimUrl } from '../fulfillment/kuaishou-feifei/index.js'
|
||||||
import { logIntegration } from '../../utils/logger.js'
|
import { logIntegration } from '../../utils/logger.js'
|
||||||
import {
|
import {
|
||||||
OPEN_91_MANUAL_FAILED_STATUS,
|
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 || ''
|
let expireTime: unknown = task.primary_claim_expires_at || task.claim_expires_at || ''
|
||||||
const primaryToken = String(task.primary_claim_token || task.claim_token || '').trim()
|
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)
|
claimUrl = buildClaimUrl(primaryToken)
|
||||||
} else if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
|
} else if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user