再跳回第 1 步
This commit is contained in:
@@ -26,6 +26,7 @@ TENCENT_BROWSER_KEEP_ALIVE=true
|
|||||||
TENCENT_BROWSER_SLOW_MO=0
|
TENCENT_BROWSER_SLOW_MO=0
|
||||||
TENCENT_SESSION_DEBUG=false
|
TENCENT_SESSION_DEBUG=false
|
||||||
CLOUDTENTACLES_BIND_URL_TTL_SECONDS=300
|
CLOUDTENTACLES_BIND_URL_TTL_SECONDS=300
|
||||||
|
CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS=30
|
||||||
CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS=5000
|
CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS=5000
|
||||||
CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT=https://comm.ams.game.qq.com/ide/
|
CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT=https://comm.ams.game.qq.com/ide/
|
||||||
CLOUDTENTACLES_BIND_URL_PROBE_CHART_ID=323794
|
CLOUDTENTACLES_BIND_URL_PROBE_CHART_ID=323794
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ module.exports = {
|
|||||||
vnBindInfoPath: '/vn/bind_info',
|
vnBindInfoPath: '/vn/bind_info',
|
||||||
vnBackPath: '/vn/back',
|
vnBackPath: '/vn/back',
|
||||||
bindUrlTtlSeconds: 300,
|
bindUrlTtlSeconds: 300,
|
||||||
|
bindUrlProbeIntervalSeconds: 30,
|
||||||
bindUrlProbeTimeoutMs: 5000,
|
bindUrlProbeTimeoutMs: 5000,
|
||||||
bindUrlProbeUserAgent: 'Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/98.0.4758.102 MQQBrowser/6.2 TBS/046141 Mobile Safari/537.36 V1_AND_SQ_8.8.85_2674_YYB_D QQ/8.8.85.7685 NetType/WIFI WebP/0.3.0 Pixel/1080 StatusBarHeight/108 SimpleUISwitch/0 QQTheme/1000',
|
bindUrlProbeUserAgent: 'Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/98.0.4758.102 MQQBrowser/6.2 TBS/046141 Mobile Safari/537.36 V1_AND_SQ_8.8.85_2674_YYB_D QQ/8.8.85.7685 NetType/WIFI WebP/0.3.0 Pixel/1080 StatusBarHeight/108 SimpleUISwitch/0 QQTheme/1000',
|
||||||
bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/',
|
bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/',
|
||||||
|
|||||||
@@ -370,6 +370,11 @@ function applyEnvOverrides(baseConfig) {
|
|||||||
nextConfig.platforms.cloudtentacles.bindUrlTtlSeconds = cloudtentaclesBindUrlTtlSeconds
|
nextConfig.platforms.cloudtentacles.bindUrlTtlSeconds = cloudtentaclesBindUrlTtlSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cloudtentaclesBindUrlProbeIntervalSeconds = parseInteger(process.env.CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS)
|
||||||
|
if (cloudtentaclesBindUrlProbeIntervalSeconds !== null) {
|
||||||
|
nextConfig.platforms.cloudtentacles.bindUrlProbeIntervalSeconds = cloudtentaclesBindUrlProbeIntervalSeconds
|
||||||
|
}
|
||||||
|
|
||||||
const cloudtentaclesBindUrlProbeTimeoutMs = parseInteger(process.env.CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS)
|
const cloudtentaclesBindUrlProbeTimeoutMs = parseInteger(process.env.CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS)
|
||||||
if (cloudtentaclesBindUrlProbeTimeoutMs !== null) {
|
if (cloudtentaclesBindUrlProbeTimeoutMs !== null) {
|
||||||
nextConfig.platforms.cloudtentacles.bindUrlProbeTimeoutMs = cloudtentaclesBindUrlProbeTimeoutMs
|
nextConfig.platforms.cloudtentacles.bindUrlProbeTimeoutMs = cloudtentaclesBindUrlProbeTimeoutMs
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
normalizeKuaishouCloudFlow,
|
normalizeKuaishouCloudFlow,
|
||||||
|
prepareKuaishouCloudFulfillmentTask,
|
||||||
probeKuaishouCloudTaskBindUrl,
|
probeKuaishouCloudTaskBindUrl,
|
||||||
refreshKuaishouCloudTaskRoleInfo,
|
refreshKuaishouCloudTaskRoleInfo,
|
||||||
} from '../fulfillment/kuaishou-cloud-task-service.js'
|
} from '../fulfillment/kuaishou-cloud-task-service.js'
|
||||||
@@ -8,6 +9,18 @@ export async function syncKuaishouCloudRoleInfo(task) {
|
|||||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
|
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
|
||||||
|
|
||||||
if (!flow.binding.vnId || !flow.binding.vnKey || !flow.binding.vnPhone) {
|
if (!flow.binding.vnId || !flow.binding.vnKey || !flow.binding.vnPhone) {
|
||||||
|
if (flow.ticket.status === 'verified' && flow.binding.prepareStatus !== 'ready') {
|
||||||
|
try {
|
||||||
|
const prepared = await prepareKuaishouCloudFulfillmentTask(task, {
|
||||||
|
source: 'claim_page_retry_binding_prepare',
|
||||||
|
actor: { source: 'system' },
|
||||||
|
})
|
||||||
|
return prepared.task
|
||||||
|
} catch {
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -399,18 +399,55 @@ export async function refreshKuaishouCloudTaskBindUrl(task, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
const cloudContext = resolvePersistedCloudtentaclesContext()
|
||||||
const bindUrlResult = await getCloudtentaclesBindUrl({
|
const oldVnKey = flow.binding.vnKey
|
||||||
...cloudContext,
|
const oldVnId = flow.binding.vnId
|
||||||
key: flow.binding.vnKey,
|
const oldVnPhone = flow.binding.vnPhone
|
||||||
id: flow.binding.vnId,
|
|
||||||
})
|
|
||||||
const bindUrl = String(bindUrlResult.bindUrl || '').trim()
|
|
||||||
|
|
||||||
if (!bindUrl) {
|
await backCloudtentaclesVirtualNumber({
|
||||||
throw createHttpError('cloudtentacles 未返回新的绑定链接', {
|
...cloudContext,
|
||||||
statusCode: 502,
|
key: oldVnKey,
|
||||||
errorCode: 'kuaishou_cloud_empty_bind_url',
|
id: oldVnId,
|
||||||
|
})
|
||||||
|
|
||||||
|
await createTaskEvent(task.id, 'kuaishou_cloud_expired_bind_number_returned', {
|
||||||
|
source: String(options.source || 'system').trim() || 'system',
|
||||||
|
vnKey: oldVnKey,
|
||||||
|
vnId: oldVnId,
|
||||||
|
vnPhoneMasked: maskPhone(oldVnPhone),
|
||||||
|
actor,
|
||||||
|
}, now)
|
||||||
|
|
||||||
|
let preparedBinding
|
||||||
|
try {
|
||||||
|
preparedBinding = await prepareKuaishouCloudBindResourceWithFallback({
|
||||||
|
cloudContext,
|
||||||
|
vnKeyCandidates: resolveKuaishouCloudVnKeyCandidates({
|
||||||
|
flow,
|
||||||
|
binding: flow.binding,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!String(preparedBinding.bindUrl || '').trim()) {
|
||||||
|
throw createHttpError('cloudtentacles 未返回新的绑定链接', {
|
||||||
|
statusCode: 502,
|
||||||
|
errorCode: 'kuaishou_cloud_empty_bind_url',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const failedTask = await markKuaishouCloudBindUrlRefreshFailed(task, {
|
||||||
|
taskContext,
|
||||||
|
flow,
|
||||||
|
now,
|
||||||
|
actor,
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
task: failedTask,
|
||||||
|
claimUrl: buildClaimUrl(String(task.primary_claim_token || task.claim_token || '')),
|
||||||
|
token: String(task.primary_claim_token || task.claim_token || ''),
|
||||||
|
flow: normalizeKuaishouCloudFlow(parseTaskContext(failedTask).kuaishouCloudFulfillment),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
@@ -420,7 +457,10 @@ export async function refreshKuaishouCloudTaskBindUrl(task, options = {}) {
|
|||||||
binding: {
|
binding: {
|
||||||
...flow.binding,
|
...flow.binding,
|
||||||
prepareStatus: 'ready',
|
prepareStatus: 'ready',
|
||||||
bindUrl,
|
vnKey: preparedBinding.vnKey,
|
||||||
|
vnId: preparedBinding.vnId,
|
||||||
|
vnPhone: preparedBinding.vnPhone,
|
||||||
|
bindUrl: preparedBinding.bindUrl,
|
||||||
bindPreparedAt: now,
|
bindPreparedAt: now,
|
||||||
bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
|
bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
|
||||||
bindProbeAt: null,
|
bindProbeAt: null,
|
||||||
@@ -456,8 +496,11 @@ export async function refreshKuaishouCloudTaskBindUrl(task, options = {}) {
|
|||||||
|
|
||||||
await createTaskEvent(task.id, 'kuaishou_cloud_bind_url_refreshed', {
|
await createTaskEvent(task.id, 'kuaishou_cloud_bind_url_refreshed', {
|
||||||
source: String(options.source || 'system').trim() || 'system',
|
source: String(options.source || 'system').trim() || 'system',
|
||||||
vnId: flow.binding.vnId,
|
oldVnId,
|
||||||
vnPhoneMasked: maskPhone(flow.binding.vnPhone),
|
oldVnPhoneMasked: maskPhone(oldVnPhone),
|
||||||
|
vnKey: preparedBinding.vnKey,
|
||||||
|
vnId: preparedBinding.vnId,
|
||||||
|
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
|
||||||
actor,
|
actor,
|
||||||
}, now)
|
}, now)
|
||||||
|
|
||||||
@@ -489,7 +532,8 @@ export async function probeKuaishouCloudTaskBindUrl(task, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.force && flow.binding.bindProbeAt && Date.now() - Date.parse(flow.binding.bindProbeAt) < 15000) {
|
const probeIntervalMs = Number(resolveCloudtentaclesConfig().bindUrlProbeIntervalSeconds || 30) * 1000
|
||||||
|
if (!options.force && flow.binding.bindProbeAt && Date.now() - Date.parse(flow.binding.bindProbeAt) < probeIntervalMs) {
|
||||||
return {
|
return {
|
||||||
task,
|
task,
|
||||||
flow,
|
flow,
|
||||||
@@ -552,6 +596,65 @@ export async function probeKuaishouCloudTaskBindUrl(task, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} task
|
||||||
|
* @param {{ taskContext?: any, flow?: any, now?: string, actor?: any, error?: unknown }} [input]
|
||||||
|
*/
|
||||||
|
async function markKuaishouCloudBindUrlRefreshFailed(task, {
|
||||||
|
taskContext,
|
||||||
|
flow,
|
||||||
|
now,
|
||||||
|
actor,
|
||||||
|
error,
|
||||||
|
} = {}) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error || '新绑定链接准备失败')
|
||||||
|
const nextContext = {
|
||||||
|
...taskContext,
|
||||||
|
kuaishouCloudFulfillment: {
|
||||||
|
...flow,
|
||||||
|
binding: {
|
||||||
|
...flow.binding,
|
||||||
|
prepareStatus: 'pending',
|
||||||
|
vnId: 0,
|
||||||
|
vnPhone: '',
|
||||||
|
bindUrl: '',
|
||||||
|
bindPreparedAt: null,
|
||||||
|
bindExpiresAt: null,
|
||||||
|
bindProbeAt: now,
|
||||||
|
bindProbeStatus: 'refresh_failed',
|
||||||
|
bindProbeMessage: errorMessage,
|
||||||
|
roleName: '',
|
||||||
|
roleId: '',
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
status: 'pending',
|
||||||
|
name: '',
|
||||||
|
rid: '',
|
||||||
|
refreshedAt: now,
|
||||||
|
errorMessage: '绑定链接已过期,旧号码已退还,新链接准备失败,请稍后刷新或联系客服处理',
|
||||||
|
rawInfo: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedTask = await updateTask(task.id, {
|
||||||
|
task_status: 'pending_binding_prepare',
|
||||||
|
role_id: '',
|
||||||
|
role_name: '',
|
||||||
|
last_error: `绑定链接过期,旧号码已退还,新链接准备失败:${errorMessage}`,
|
||||||
|
context_json: JSON.stringify(nextContext),
|
||||||
|
updated_at: now,
|
||||||
|
})
|
||||||
|
|
||||||
|
await createTaskEvent(task.id, 'kuaishou_cloud_bind_url_refresh_failed', {
|
||||||
|
source: 'system_refresh_expired_bind_url',
|
||||||
|
errorMessage,
|
||||||
|
actor,
|
||||||
|
}, now)
|
||||||
|
|
||||||
|
return updatedTask
|
||||||
|
}
|
||||||
|
|
||||||
export async function refreshKuaishouCloudTaskRoleInfo(task, options = {}) {
|
export async function refreshKuaishouCloudTaskRoleInfo(task, options = {}) {
|
||||||
if (!isKuaishouCloudTask(task)) {
|
if (!isKuaishouCloudTask(task)) {
|
||||||
throw createHttpError('当前任务不是快手 Cloud 履约任务', {
|
throw createHttpError('当前任务不是快手 Cloud 履约任务', {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export function resolveCloudtentaclesConfig(overrides = {}) {
|
|||||||
vnBindInfoPath: '/vn/bind_info',
|
vnBindInfoPath: '/vn/bind_info',
|
||||||
vnBackPath: '/vn/back',
|
vnBackPath: '/vn/back',
|
||||||
bindUrlTtlSeconds: 300,
|
bindUrlTtlSeconds: 300,
|
||||||
|
bindUrlProbeIntervalSeconds: 30,
|
||||||
bindUrlProbeTimeoutMs: 5000,
|
bindUrlProbeTimeoutMs: 5000,
|
||||||
bindUrlProbeUserAgent: '',
|
bindUrlProbeUserAgent: '',
|
||||||
bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/',
|
bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/',
|
||||||
@@ -71,6 +72,10 @@ export function resolveCloudtentaclesConfig(overrides = {}) {
|
|||||||
vnBindInfoPath: normalizePath(overrides.vnBindInfoPath || baseConfig.vnBindInfoPath, '/vn/bind_info'),
|
vnBindInfoPath: normalizePath(overrides.vnBindInfoPath || baseConfig.vnBindInfoPath, '/vn/bind_info'),
|
||||||
vnBackPath: normalizePath(overrides.vnBackPath || baseConfig.vnBackPath, '/vn/back'),
|
vnBackPath: normalizePath(overrides.vnBackPath || baseConfig.vnBackPath, '/vn/back'),
|
||||||
bindUrlTtlSeconds: normalizePositiveInteger(overrides.bindUrlTtlSeconds || baseConfig.bindUrlTtlSeconds, 300),
|
bindUrlTtlSeconds: normalizePositiveInteger(overrides.bindUrlTtlSeconds || baseConfig.bindUrlTtlSeconds, 300),
|
||||||
|
bindUrlProbeIntervalSeconds: normalizePositiveInteger(
|
||||||
|
overrides.bindUrlProbeIntervalSeconds || baseConfig.bindUrlProbeIntervalSeconds,
|
||||||
|
30,
|
||||||
|
),
|
||||||
bindUrlProbeTimeoutMs: normalizePositiveInteger(overrides.bindUrlProbeTimeoutMs || baseConfig.bindUrlProbeTimeoutMs, 5000),
|
bindUrlProbeTimeoutMs: normalizePositiveInteger(overrides.bindUrlProbeTimeoutMs || baseConfig.bindUrlProbeTimeoutMs, 5000),
|
||||||
bindUrlProbeUserAgent: String(overrides.bindUrlProbeUserAgent || baseConfig.bindUrlProbeUserAgent || '').trim(),
|
bindUrlProbeUserAgent: String(overrides.bindUrlProbeUserAgent || baseConfig.bindUrlProbeUserAgent || '').trim(),
|
||||||
bindUrlProbeEndpoint: String(overrides.bindUrlProbeEndpoint || baseConfig.bindUrlProbeEndpoint || 'https://comm.ams.game.qq.com/ide/').trim(),
|
bindUrlProbeEndpoint: String(overrides.bindUrlProbeEndpoint || baseConfig.bindUrlProbeEndpoint || 'https://comm.ams.game.qq.com/ide/').trim(),
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ export {}
|
|||||||
* vnBindInfoPath: string
|
* vnBindInfoPath: string
|
||||||
* vnBackPath: string
|
* vnBackPath: string
|
||||||
* bindUrlTtlSeconds: number
|
* bindUrlTtlSeconds: number
|
||||||
|
* bindUrlProbeIntervalSeconds: number
|
||||||
* bindUrlProbeTimeoutMs: number
|
* bindUrlProbeTimeoutMs: number
|
||||||
* bindUrlProbeUserAgent: string
|
* bindUrlProbeUserAgent: string
|
||||||
* bindUrlProbeEndpoint: string
|
* bindUrlProbeEndpoint: string
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const isBindingPrepared = computed(() => (
|
|||||||
&& !isBindUrlExpired.value
|
&& !isBindUrlExpired.value
|
||||||
))
|
))
|
||||||
const isBindingPreparing = computed(() => flow.value?.binding.prepareStatus === 'pending')
|
const isBindingPreparing = computed(() => flow.value?.binding.prepareStatus === 'pending')
|
||||||
const canEnterBindingStep = computed(() => isTicketVerified.value && isBindingPrepared.value)
|
const canEnterBindingStep = computed(() => isTicketVerified.value)
|
||||||
const isRoleReady = computed(() => Boolean(roleName.value || roleId.value))
|
const isRoleReady = computed(() => Boolean(roleName.value || roleId.value))
|
||||||
const isRoleConfirmed = computed(() => String(task.value?.status || '').trim() === 'role_confirmed')
|
const isRoleConfirmed = computed(() => String(task.value?.status || '').trim() === 'role_confirmed')
|
||||||
const isDispatched = computed(() => String(flow.value?.dispatch.status || '').trim() === 'success')
|
const isDispatched = computed(() => String(flow.value?.dispatch.status || '').trim() === 'success')
|
||||||
@@ -85,7 +85,7 @@ const progressText = computed(() => {
|
|||||||
return '角色已确认,等待兑换'
|
return '角色已确认,等待兑换'
|
||||||
}
|
}
|
||||||
if (isTicketVerified.value) {
|
if (isTicketVerified.value) {
|
||||||
return isBindingPrepared.value ? '请完成扫码绑定' : '绑定资源未准备完成,请重新验证核销码'
|
return isBindingPrepared.value ? '请完成扫码绑定' : '绑定链接刷新中,请稍候'
|
||||||
}
|
}
|
||||||
return '等待提交核销码'
|
return '等待提交核销码'
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user