优化连接过期验证与戒色信息-1
This commit is contained in:
@@ -21,8 +21,10 @@ import {
|
||||
generateCloudtentaclesLoginCode,
|
||||
getCloudtentaclesBindInfo,
|
||||
getCloudtentaclesBindUrl,
|
||||
probeCloudtentaclesBindUrl,
|
||||
verifyCloudtentaclesLoginCode,
|
||||
} from '../platforms/cloudtentacles/virtual-number-service.js'
|
||||
import { resolveCloudtentaclesConfig } from '../platforms/cloudtentacles/shared.js'
|
||||
import { consumeKuaishouEticket } from '../platforms/kuaishou-eticket/consume-service.js'
|
||||
import {
|
||||
getKuaishouEticketSourceConfig,
|
||||
@@ -49,6 +51,8 @@ export function normalizeKuaishouCloudFlow(value) {
|
||||
|
||||
const roleName = String(role.name || binding.roleName || '').trim()
|
||||
const roleId = String(role.rid || binding.roleId || '').trim()
|
||||
const bindPreparedAt = binding.bindPreparedAt || null
|
||||
const bindExpiresAt = binding.bindExpiresAt || resolveKuaishouCloudBindUrlExpiresAt(bindPreparedAt)
|
||||
|
||||
return {
|
||||
...source,
|
||||
@@ -75,7 +79,11 @@ export function normalizeKuaishouCloudFlow(value) {
|
||||
vnId: Number(binding.vnId || 0) || 0,
|
||||
vnPhone: String(binding.vnPhone || '').trim(),
|
||||
bindUrl: String(binding.bindUrl || '').trim(),
|
||||
bindPreparedAt: binding.bindPreparedAt || null,
|
||||
bindPreparedAt,
|
||||
bindExpiresAt,
|
||||
bindProbeAt: binding.bindProbeAt || null,
|
||||
bindProbeStatus: String(binding.bindProbeStatus || '').trim(),
|
||||
bindProbeMessage: String(binding.bindProbeMessage || '').trim(),
|
||||
roleName,
|
||||
roleId,
|
||||
},
|
||||
@@ -123,10 +131,12 @@ export function normalizeKuaishouCloudFlow(value) {
|
||||
|
||||
export function normalizeKuaishouCloudRoleInfo(value) {
|
||||
const rawInfo = value && typeof value === 'object' ? value : null
|
||||
const nestedBindInfo = rawInfo?.sBindInfo && typeof rawInfo.sBindInfo === 'object' ? rawInfo.sBindInfo : null
|
||||
const source = nestedBindInfo || rawInfo
|
||||
|
||||
return {
|
||||
name: String(rawInfo?.name || rawInfo?.roleName || rawInfo?.nickname || '').trim(),
|
||||
rid: String(rawInfo?.rid || rawInfo?.roleId || rawInfo?.uid || '').trim(),
|
||||
name: String(source?.name || source?.roleName || source?.nickname || source?.sRoleName || '').trim(),
|
||||
rid: String(source?.rid || source?.roleId || source?.uid || source?.sRoleId || source?.sUserId || '').trim(),
|
||||
rawInfo,
|
||||
}
|
||||
}
|
||||
@@ -188,6 +198,14 @@ export async function prepareKuaishouCloudFulfillmentTask(task, options = {}) {
|
||||
const claimLinkState = await ensureTaskClaimLink(task)
|
||||
|
||||
if (!force && flow.binding.prepareStatus === 'ready' && flow.binding.vnId > 0 && flow.binding.vnPhone && flow.binding.bindUrl) {
|
||||
if (!isKuaishouCloudBindUrlFresh(flow)) {
|
||||
return refreshKuaishouCloudTaskBindUrl(task, {
|
||||
source: options.source || 'system_refresh_expired_bind_url',
|
||||
actor,
|
||||
claimLinkState,
|
||||
})
|
||||
}
|
||||
|
||||
let readyTask = task
|
||||
|
||||
if (String(task.task_status || '').trim() !== 'waiting_binding') {
|
||||
@@ -300,6 +318,10 @@ export async function prepareKuaishouCloudFulfillmentTask(task, options = {}) {
|
||||
vnPhone: preparedBinding.vnPhone,
|
||||
bindUrl: preparedBinding.bindUrl,
|
||||
bindPreparedAt: now,
|
||||
bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
|
||||
bindProbeAt: null,
|
||||
bindProbeStatus: 'pending',
|
||||
bindProbeMessage: '',
|
||||
roleName: '',
|
||||
roleId: '',
|
||||
},
|
||||
@@ -356,6 +378,180 @@ export async function prepareKuaishouCloudFulfillmentTask(task, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshKuaishouCloudTaskBindUrl(task, options = {}) {
|
||||
if (!isKuaishouCloudTask(task)) {
|
||||
throw createHttpError('当前任务不是快手 Cloud 履约任务', {
|
||||
statusCode: 409,
|
||||
errorCode: 'kuaishou_cloud_task_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const actor = normalizeActor(options.actor)
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
|
||||
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
||||
throw createHttpError('当前任务缺少可刷新绑定链接的虚拟号信息', {
|
||||
statusCode: 409,
|
||||
errorCode: 'kuaishou_cloud_missing_bind_url_context',
|
||||
})
|
||||
}
|
||||
|
||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
||||
const bindUrlResult = await getCloudtentaclesBindUrl({
|
||||
...cloudContext,
|
||||
key: flow.binding.vnKey,
|
||||
id: flow.binding.vnId,
|
||||
})
|
||||
const bindUrl = String(bindUrlResult.bindUrl || '').trim()
|
||||
|
||||
if (!bindUrl) {
|
||||
throw createHttpError('cloudtentacles 未返回新的绑定链接', {
|
||||
statusCode: 502,
|
||||
errorCode: 'kuaishou_cloud_empty_bind_url',
|
||||
})
|
||||
}
|
||||
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
binding: {
|
||||
...flow.binding,
|
||||
prepareStatus: 'ready',
|
||||
bindUrl,
|
||||
bindPreparedAt: now,
|
||||
bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
|
||||
bindProbeAt: null,
|
||||
bindProbeStatus: 'pending',
|
||||
bindProbeMessage: '',
|
||||
roleName: String(task.task_status || '').trim() === 'role_confirmed' ? flow.binding.roleName : '',
|
||||
roleId: String(task.task_status || '').trim() === 'role_confirmed' ? flow.binding.roleId : '',
|
||||
},
|
||||
role: String(task.task_status || '').trim() === 'role_confirmed'
|
||||
? flow.role
|
||||
: {
|
||||
status: 'pending',
|
||||
name: '',
|
||||
rid: '',
|
||||
refreshedAt: null,
|
||||
errorMessage: '',
|
||||
rawInfo: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const claimLinkState = options.claimLinkState || await ensureTaskClaimLink(task)
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: String(task.task_status || '').trim() === 'role_confirmed' ? task.task_status : 'waiting_binding',
|
||||
claim_token: claimLinkState.token || task.claim_token || '',
|
||||
claim_expires_at: claimLinkState.expiredAt || getTaskClaimExpiresAt(task),
|
||||
role_id: String(task.task_status || '').trim() === 'role_confirmed' ? task.role_id : '',
|
||||
role_name: String(task.task_status || '').trim() === 'role_confirmed' ? task.role_name : '',
|
||||
last_error: '',
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
await createTaskEvent(task.id, 'kuaishou_cloud_bind_url_refreshed', {
|
||||
source: String(options.source || 'system').trim() || 'system',
|
||||
vnId: flow.binding.vnId,
|
||||
vnPhoneMasked: maskPhone(flow.binding.vnPhone),
|
||||
actor,
|
||||
}, now)
|
||||
|
||||
return {
|
||||
task: updatedTask,
|
||||
claimUrl: claimLinkState.claimUrl,
|
||||
token: claimLinkState.token,
|
||||
flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
|
||||
}
|
||||
}
|
||||
|
||||
export async function probeKuaishouCloudTaskBindUrl(task, options = {}) {
|
||||
if (!isKuaishouCloudTask(task)) {
|
||||
throw createHttpError('当前任务不是快手 Cloud 履约任务', {
|
||||
statusCode: 409,
|
||||
errorCode: 'kuaishou_cloud_task_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
|
||||
if (!flow.binding.bindUrl) {
|
||||
return {
|
||||
task,
|
||||
flow,
|
||||
probe: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.force && flow.binding.bindProbeAt && Date.now() - Date.parse(flow.binding.bindProbeAt) < 15000) {
|
||||
return {
|
||||
task,
|
||||
flow,
|
||||
probe: null,
|
||||
}
|
||||
}
|
||||
|
||||
const probe = await probeCloudtentaclesBindUrl({
|
||||
bindUrl: flow.binding.bindUrl,
|
||||
})
|
||||
|
||||
if (probe.expired || !isKuaishouCloudBindUrlFresh(flow)) {
|
||||
const refreshed = await refreshKuaishouCloudTaskBindUrl(task, {
|
||||
source: options.source || 'claim_page_bind_url_expired',
|
||||
actor: options.actor || { source: 'system' },
|
||||
})
|
||||
return {
|
||||
...refreshed,
|
||||
probe,
|
||||
}
|
||||
}
|
||||
|
||||
const probeRoleInfo = normalizeKuaishouCloudRoleInfo(probe.roleInfo)
|
||||
const hasRoleInfo = Boolean(probeRoleInfo.name || probeRoleInfo.rid)
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
binding: {
|
||||
...flow.binding,
|
||||
bindProbeAt: now,
|
||||
bindProbeStatus: probe.valid ? 'valid' : 'invalid',
|
||||
bindProbeMessage: String(probe.message || probe.reason || '').trim(),
|
||||
roleName: hasRoleInfo ? probeRoleInfo.name : flow.binding.roleName,
|
||||
roleId: hasRoleInfo ? probeRoleInfo.rid : flow.binding.roleId,
|
||||
},
|
||||
role: {
|
||||
...flow.role,
|
||||
status: hasRoleInfo ? 'ready' : flow.role.status,
|
||||
name: hasRoleInfo ? probeRoleInfo.name : flow.role.name,
|
||||
rid: hasRoleInfo ? probeRoleInfo.rid : flow.role.rid,
|
||||
refreshedAt: hasRoleInfo ? now : flow.role.refreshedAt,
|
||||
errorMessage: hasRoleInfo ? '' : flow.role.errorMessage,
|
||||
rawInfo: hasRoleInfo ? probeRoleInfo.rawInfo : flow.role.rawInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
role_id: hasRoleInfo ? probeRoleInfo.rid : task.role_id,
|
||||
role_name: hasRoleInfo ? probeRoleInfo.name : task.role_name,
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
|
||||
return {
|
||||
task: updatedTask,
|
||||
flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
|
||||
probe,
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshKuaishouCloudTaskRoleInfo(task, options = {}) {
|
||||
if (!isKuaishouCloudTask(task)) {
|
||||
throw createHttpError('当前任务不是快手 Cloud 履约任务', {
|
||||
@@ -376,6 +572,34 @@ export async function refreshKuaishouCloudTaskRoleInfo(task, options = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
if (flow.binding.bindUrl) {
|
||||
const probed = await probeKuaishouCloudTaskBindUrl(task, {
|
||||
source: options.source || 'system_role_refresh_bind_url_probe',
|
||||
actor,
|
||||
force: options.forceProbe === true,
|
||||
})
|
||||
const probedFlow = normalizeKuaishouCloudFlow(parseTaskContext(probed.task).kuaishouCloudFulfillment)
|
||||
if (probed.probe?.expired) {
|
||||
return {
|
||||
task: probed.task,
|
||||
roleInfo: normalizeKuaishouCloudRoleInfo(null),
|
||||
flow: probedFlow,
|
||||
}
|
||||
}
|
||||
|
||||
if ((probed.probe === null || probed.probe?.valid) && (probedFlow.binding.roleName || probedFlow.binding.roleId)) {
|
||||
return {
|
||||
task: probed.task,
|
||||
roleInfo: {
|
||||
name: probedFlow.binding.roleName,
|
||||
rid: probedFlow.binding.roleId,
|
||||
rawInfo: probedFlow.role.rawInfo,
|
||||
},
|
||||
flow: probedFlow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
||||
const bindInfoResult = await getCloudtentaclesBindInfo({
|
||||
...cloudContext,
|
||||
@@ -701,6 +925,36 @@ export function maskCode(value) {
|
||||
return `${text.slice(0, 4)}****${text.slice(-4)}`
|
||||
}
|
||||
|
||||
export function resolveKuaishouCloudBindUrlExpiresAt(preparedAt) {
|
||||
const preparedTime = Date.parse(String(preparedAt || ''))
|
||||
if (!Number.isFinite(preparedTime)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const config = resolveCloudtentaclesConfig()
|
||||
const ttlSeconds = Number(config.bindUrlTtlSeconds || 300)
|
||||
return new Date(preparedTime + Math.max(1, ttlSeconds) * 1000).toISOString()
|
||||
}
|
||||
|
||||
export function isKuaishouCloudBindUrlFresh(flow, now = new Date()) {
|
||||
const normalizedFlow = normalizeKuaishouCloudFlow(flow)
|
||||
if (!normalizedFlow.binding.bindUrl) {
|
||||
return false
|
||||
}
|
||||
|
||||
const expiresAt = normalizedFlow.binding.bindExpiresAt
|
||||
if (!expiresAt) {
|
||||
return false
|
||||
}
|
||||
|
||||
const expiresTime = Date.parse(String(expiresAt || ''))
|
||||
if (!Number.isFinite(expiresTime)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return expiresTime > now.getTime()
|
||||
}
|
||||
|
||||
function resolveKuaishouCloudBindingResources(flow, { skuItems = [], knapsackItems = [] } = {}) {
|
||||
const normalizedSkuItems = Array.isArray(skuItems) ? skuItems.filter(isCloudSkuLikeItem) : []
|
||||
const normalizedKnapsackItems = Array.isArray(knapsackItems) ? knapsackItems.filter(isCloudSkuLikeItem) : []
|
||||
|
||||
Reference in New Issue
Block a user