优化领取页角色绑定校验
This commit is contained in:
@@ -248,6 +248,16 @@ export function mapClaimKuaishouCloudFulfillment(task: TaskRow, order: OrderRow)
|
|||||||
const dispatch = source.dispatch && typeof source.dispatch === 'object' ? source.dispatch : {}
|
const dispatch = source.dispatch && typeof source.dispatch === 'object' ? source.dispatch : {}
|
||||||
const returnNumber = source.returnNumber && typeof source.returnNumber === 'object' ? source.returnNumber : {}
|
const returnNumber = source.returnNumber && typeof source.returnNumber === 'object' ? source.returnNumber : {}
|
||||||
const consume = source.consume && typeof source.consume === 'object' ? source.consume : {}
|
const consume = source.consume && typeof source.consume === 'object' ? source.consume : {}
|
||||||
|
const roleName = String(role.name || binding.roleName || '').trim()
|
||||||
|
const roleId = String(role.rid || binding.roleId || '').trim()
|
||||||
|
const defaultRoleName = String(role.defaultName || role.defaultRoleName || '').trim()
|
||||||
|
const defaultRoleId = String(role.defaultRid || role.defaultRoleId || '').trim()
|
||||||
|
const isDefaultRole =
|
||||||
|
Boolean(roleName || roleId) &&
|
||||||
|
isSameClaimKuaishouCloudRole(
|
||||||
|
{ name: roleName, rid: roleId },
|
||||||
|
{ name: defaultRoleName, rid: defaultRoleId },
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flowType: 'kuaishou_cloud',
|
flowType: 'kuaishou_cloud',
|
||||||
@@ -288,10 +298,16 @@ export function mapClaimKuaishouCloudFulfillment(task: TaskRow, order: OrderRow)
|
|||||||
},
|
},
|
||||||
role: {
|
role: {
|
||||||
status: String(role.status || 'pending').trim() || 'pending',
|
status: String(role.status || 'pending').trim() || 'pending',
|
||||||
name: String(role.name || binding.roleName || '').trim(),
|
name: roleName,
|
||||||
rid: String(role.rid || binding.roleId || '').trim(),
|
rid: roleId,
|
||||||
refreshedAt: role.refreshedAt || null,
|
refreshedAt: role.refreshedAt || null,
|
||||||
errorMessage: String(role.errorMessage || '').trim(),
|
errorMessage: String(role.errorMessage || '').trim(),
|
||||||
|
defaultName: defaultRoleName,
|
||||||
|
defaultRid: defaultRoleId,
|
||||||
|
defaultCapturedAt: role.defaultCapturedAt || null,
|
||||||
|
defaultCaptureStatus: String(role.defaultCaptureStatus || '').trim(),
|
||||||
|
defaultErrorMessage: String(role.defaultErrorMessage || '').trim(),
|
||||||
|
isDefaultRole,
|
||||||
},
|
},
|
||||||
purchase: {
|
purchase: {
|
||||||
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
||||||
@@ -351,6 +367,28 @@ function isPlainObject(value: unknown): value is JsonObject {
|
|||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSameClaimKuaishouCloudRole(
|
||||||
|
left: { name?: unknown; rid?: unknown },
|
||||||
|
right: { name?: unknown; rid?: unknown },
|
||||||
|
) {
|
||||||
|
const leftRid = String(left.rid || '').trim()
|
||||||
|
const rightRid = String(right.rid || '').trim()
|
||||||
|
if (leftRid && rightRid) {
|
||||||
|
return leftRid === rightRid
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftName = normalizeClaimRoleNameForCompare(left.name)
|
||||||
|
const rightName = normalizeClaimRoleNameForCompare(right.name)
|
||||||
|
return Boolean(leftName && rightName && leftName === rightName)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeClaimRoleNameForCompare(value: unknown) {
|
||||||
|
return String(value || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeClaimDeliveryItems(value: unknown, binding: JsonObject) {
|
function normalizeClaimDeliveryItems(value: unknown, binding: JsonObject) {
|
||||||
const rawItems = Array.isArray(value) ? value : []
|
const rawItems = Array.isArray(value) ? value : []
|
||||||
const items = rawItems
|
const items = rawItems
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
import { isKuaishouEticketMockTicketCode } from '../platforms/kuaishou-eticket/mock-ticket-service.js'
|
import { isKuaishouEticketMockTicketCode } from '../platforms/kuaishou-eticket/mock-ticket-service.js'
|
||||||
import {
|
import {
|
||||||
dispatchKuaishouCloudFulfillmentTask,
|
dispatchKuaishouCloudFulfillmentTask,
|
||||||
|
hasKuaishouCloudCustomerRole,
|
||||||
|
hasKuaishouCloudDefaultRoleSnapshot,
|
||||||
maskCode,
|
maskCode,
|
||||||
normalizeKuaishouCloudFlow,
|
normalizeKuaishouCloudFlow,
|
||||||
prepareKuaishouCloudFulfillmentTask,
|
prepareKuaishouCloudFulfillmentTask,
|
||||||
@@ -478,6 +480,19 @@ export async function confirmKuaishouCloudClaimRole(token: unknown) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasDefaultRoleSnapshot = hasKuaishouCloudDefaultRoleSnapshot(flow)
|
||||||
|
if (!mockMode && !hasKuaishouCloudCustomerRole(flow)) {
|
||||||
|
const message = hasDefaultRoleSnapshot
|
||||||
|
? '当前仍是虚拟机默认角色,请先重新绑定自己的角色信息'
|
||||||
|
: '系统还未获取到虚拟机默认角色信息,请稍后刷新角色信息后再试'
|
||||||
|
throw createHttpError(message, {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: hasDefaultRoleSnapshot
|
||||||
|
? 'claim_kuaishou_cloud_default_role_not_rebound'
|
||||||
|
: 'claim_kuaishou_cloud_default_role_not_ready',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
await updateTask(context.task.id, {
|
await updateTask(context.task.id, {
|
||||||
task_status: TASK_STATUS.ROLE_CONFIRMED,
|
task_status: TASK_STATUS.ROLE_CONFIRMED,
|
||||||
role_id: flow.binding.roleId,
|
role_id: flow.binding.roleId,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export async function syncKuaishouCloudRoleInfo(task: TaskRow) {
|
|||||||
source: 'claim_page_polling',
|
source: 'claim_page_polling',
|
||||||
actor: { source: 'system' },
|
actor: { source: 'system' },
|
||||||
recordEvent: false,
|
recordEvent: false,
|
||||||
|
forceProbe: flow.role.isDefaultRole === true,
|
||||||
})
|
})
|
||||||
return result.task || task
|
return result.task || task
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import test from 'node:test'
|
||||||
|
|
||||||
|
import {
|
||||||
|
hasKuaishouCloudCustomerRole,
|
||||||
|
normalizeKuaishouCloudFlow,
|
||||||
|
} from './domain.js'
|
||||||
|
|
||||||
|
test('hasKuaishouCloudCustomerRole 要求当前角色不同于虚拟机默认角色', () => {
|
||||||
|
const defaultRoleFlow = normalizeKuaishouCloudFlow({
|
||||||
|
binding: {
|
||||||
|
vnPhone: '13800000000',
|
||||||
|
roleName: '默认角色',
|
||||||
|
roleId: '10001',
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
name: '默认角色',
|
||||||
|
rid: '10001',
|
||||||
|
defaultName: '默认角色',
|
||||||
|
defaultRid: '10001',
|
||||||
|
defaultCapturedAt: '2026-05-31T00:00:00.000Z',
|
||||||
|
isDefaultRole: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(defaultRoleFlow.role.isDefaultRole, true)
|
||||||
|
assert.equal(hasKuaishouCloudCustomerRole(defaultRoleFlow), false)
|
||||||
|
|
||||||
|
const customerRoleFlow = normalizeKuaishouCloudFlow({
|
||||||
|
...defaultRoleFlow,
|
||||||
|
binding: {
|
||||||
|
...defaultRoleFlow.binding,
|
||||||
|
roleName: '用户角色',
|
||||||
|
roleId: '20002',
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
...defaultRoleFlow.role,
|
||||||
|
name: '用户角色',
|
||||||
|
rid: '20002',
|
||||||
|
isDefaultRole: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(customerRoleFlow.role.isDefaultRole, false)
|
||||||
|
assert.equal(hasKuaishouCloudCustomerRole(customerRoleFlow), true)
|
||||||
|
})
|
||||||
@@ -25,6 +25,16 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
|
|
||||||
const roleName = String(role.name || binding.roleName || '').trim()
|
const roleName = String(role.name || binding.roleName || '').trim()
|
||||||
const roleId = String(role.rid || binding.roleId || '').trim()
|
const roleId = String(role.rid || binding.roleId || '').trim()
|
||||||
|
const defaultRoleName = String(role.defaultName || role.defaultRoleName || '').trim()
|
||||||
|
const defaultRoleId = String(role.defaultRid || role.defaultRoleId || '').trim()
|
||||||
|
const hasDefaultRole = Boolean(defaultRoleName || defaultRoleId)
|
||||||
|
const isDefaultRole =
|
||||||
|
Boolean(roleName || roleId) &&
|
||||||
|
hasDefaultRole &&
|
||||||
|
isSameKuaishouCloudRoleIdentity(
|
||||||
|
{ name: roleName, rid: roleId },
|
||||||
|
{ name: defaultRoleName, rid: defaultRoleId },
|
||||||
|
)
|
||||||
const bindPreparedAt = binding.bindPreparedAt || null
|
const bindPreparedAt = binding.bindPreparedAt || null
|
||||||
const bindExpiresAt =
|
const bindExpiresAt =
|
||||||
binding.bindExpiresAt || resolveKuaishouCloudBindUrlExpiresAt(bindPreparedAt)
|
binding.bindExpiresAt || resolveKuaishouCloudBindUrlExpiresAt(bindPreparedAt)
|
||||||
@@ -72,6 +82,12 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
refreshedAt: role.refreshedAt || null,
|
refreshedAt: role.refreshedAt || null,
|
||||||
errorMessage: String(role.errorMessage || '').trim(),
|
errorMessage: String(role.errorMessage || '').trim(),
|
||||||
rawInfo: role.rawInfo && typeof role.rawInfo === 'object' ? role.rawInfo : null,
|
rawInfo: role.rawInfo && typeof role.rawInfo === 'object' ? role.rawInfo : null,
|
||||||
|
defaultName: defaultRoleName,
|
||||||
|
defaultRid: defaultRoleId,
|
||||||
|
defaultCapturedAt: role.defaultCapturedAt || null,
|
||||||
|
defaultCaptureStatus: String(role.defaultCaptureStatus || '').trim(),
|
||||||
|
defaultErrorMessage: String(role.defaultErrorMessage || '').trim(),
|
||||||
|
isDefaultRole,
|
||||||
},
|
},
|
||||||
purchase: {
|
purchase: {
|
||||||
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
||||||
@@ -113,6 +129,49 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasKuaishouCloudDefaultRoleSnapshot(flowLike: unknown) {
|
||||||
|
const flow = normalizeKuaishouCloudFlow(flowLike)
|
||||||
|
return Boolean(flow.role.defaultName || flow.role.defaultRid)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasKuaishouCloudCustomerRole(flowLike: unknown) {
|
||||||
|
const flow = normalizeKuaishouCloudFlow(flowLike)
|
||||||
|
if (!flow.binding.vnPhone || !flow.binding.roleName || !flow.binding.roleId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasKuaishouCloudDefaultRoleSnapshot(flow)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isSameKuaishouCloudRoleIdentity(
|
||||||
|
{ name: flow.binding.roleName, rid: flow.binding.roleId },
|
||||||
|
{ name: flow.role.defaultName, rid: flow.role.defaultRid },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSameKuaishouCloudRoleIdentity(
|
||||||
|
left: { name?: unknown; rid?: unknown },
|
||||||
|
right: { name?: unknown; rid?: unknown },
|
||||||
|
) {
|
||||||
|
const leftRid = String(left.rid || '').trim()
|
||||||
|
const rightRid = String(right.rid || '').trim()
|
||||||
|
if (leftRid && rightRid) {
|
||||||
|
return leftRid === rightRid
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftName = normalizeRoleNameForCompare(left.name)
|
||||||
|
const rightName = normalizeRoleNameForCompare(right.name)
|
||||||
|
return Boolean(leftName && rightName && leftName === rightName)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRoleNameForCompare(value: unknown) {
|
||||||
|
return String(value || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeKuaishouCloudDeliveryItems(value: unknown) {
|
export function normalizeKuaishouCloudDeliveryItems(value: unknown) {
|
||||||
const source: JsonObject = value && typeof value === 'object' ? (value as JsonObject) : {}
|
const source: JsonObject = value && typeof value === 'object' ? (value as JsonObject) : {}
|
||||||
const binding = source.binding && typeof source.binding === 'object' ? source.binding : {}
|
const binding = source.binding && typeof source.binding === 'object' ? source.binding : {}
|
||||||
|
|||||||
@@ -15,8 +15,11 @@ import { nowIso } from '../../../utils/time.js'
|
|||||||
import { TASK_STATUS, normalizeTaskStatus } from '../../../domain/task-status.js'
|
import { TASK_STATUS, normalizeTaskStatus } from '../../../domain/task-status.js'
|
||||||
import {
|
import {
|
||||||
KUAISHOU_CLOUD_FIXED_VN_KEY,
|
KUAISHOU_CLOUD_FIXED_VN_KEY,
|
||||||
|
hasKuaishouCloudCustomerRole,
|
||||||
|
hasKuaishouCloudDefaultRoleSnapshot,
|
||||||
isKuaishouCloudBindUrlFresh,
|
isKuaishouCloudBindUrlFresh,
|
||||||
isKuaishouCloudTask,
|
isKuaishouCloudTask,
|
||||||
|
isSameKuaishouCloudRoleIdentity,
|
||||||
maskPhone,
|
maskPhone,
|
||||||
normalizeKuaishouCloudFlow,
|
normalizeKuaishouCloudFlow,
|
||||||
normalizeKuaishouCloudRoleInfo,
|
normalizeKuaishouCloudRoleInfo,
|
||||||
@@ -51,17 +54,125 @@ type CloudDeliveryPlanItem = {
|
|||||||
missingCount: number
|
missingCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KuaishouCloudDefaultRoleSnapshot = {
|
||||||
|
defaultName: string
|
||||||
|
defaultRid: string
|
||||||
|
defaultCapturedAt: string | null
|
||||||
|
defaultCaptureStatus: string
|
||||||
|
defaultErrorMessage: string
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isKuaishouCloudBindUrlFresh,
|
isKuaishouCloudBindUrlFresh,
|
||||||
isKuaishouCloudTask,
|
isKuaishouCloudTask,
|
||||||
|
hasKuaishouCloudCustomerRole,
|
||||||
|
hasKuaishouCloudDefaultRoleSnapshot,
|
||||||
maskCode,
|
maskCode,
|
||||||
maskPhone,
|
maskPhone,
|
||||||
normalizeKuaishouCloudFlow,
|
normalizeKuaishouCloudFlow,
|
||||||
normalizeKuaishouCloudRoleInfo,
|
normalizeKuaishouCloudRoleInfo,
|
||||||
|
isSameKuaishouCloudRoleIdentity,
|
||||||
resolveKuaishouCloudBindUrlExpiresAt,
|
resolveKuaishouCloudBindUrlExpiresAt,
|
||||||
} from './domain.js'
|
} from './domain.js'
|
||||||
export { resolvePersistedCloudtentaclesContextBySourceKeys } from './cloudtentacles-context.js'
|
export { resolvePersistedCloudtentaclesContextBySourceKeys } from './cloudtentacles-context.js'
|
||||||
|
|
||||||
|
async function captureKuaishouCloudDefaultRoleSnapshot({
|
||||||
|
cloudContext,
|
||||||
|
preparedBinding,
|
||||||
|
now,
|
||||||
|
}: {
|
||||||
|
cloudContext: JsonObject
|
||||||
|
preparedBinding: JsonObject
|
||||||
|
now: string
|
||||||
|
}): Promise<KuaishouCloudDefaultRoleSnapshot> {
|
||||||
|
try {
|
||||||
|
const bindInfoResult = await getCloudtentaclesBindInfo({
|
||||||
|
...cloudContext,
|
||||||
|
key: preparedBinding.vnKey,
|
||||||
|
id: preparedBinding.vnId,
|
||||||
|
})
|
||||||
|
const roleInfo = normalizeKuaishouCloudRoleInfo(bindInfoResult.bindInfo)
|
||||||
|
const hasRole = Boolean(roleInfo.name || roleInfo.rid)
|
||||||
|
|
||||||
|
return {
|
||||||
|
defaultName: roleInfo.name,
|
||||||
|
defaultRid: roleInfo.rid,
|
||||||
|
defaultCapturedAt: hasRole ? now : null,
|
||||||
|
defaultCaptureStatus: hasRole ? 'captured' : 'empty',
|
||||||
|
defaultErrorMessage: hasRole ? '' : '未获取到虚拟机默认角色信息,请稍后刷新',
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
defaultName: '',
|
||||||
|
defaultRid: '',
|
||||||
|
defaultCapturedAt: null,
|
||||||
|
defaultCaptureStatus: 'failed',
|
||||||
|
defaultErrorMessage:
|
||||||
|
error instanceof Error ? error.message : '获取虚拟机默认角色信息失败,请稍后刷新',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPendingRoleWithDefaultSnapshot(snapshot: KuaishouCloudDefaultRoleSnapshot) {
|
||||||
|
return {
|
||||||
|
status: 'pending',
|
||||||
|
name: '',
|
||||||
|
rid: '',
|
||||||
|
refreshedAt: null as null,
|
||||||
|
errorMessage: snapshot.defaultErrorMessage || '',
|
||||||
|
rawInfo: null as null,
|
||||||
|
defaultName: snapshot.defaultName,
|
||||||
|
defaultRid: snapshot.defaultRid,
|
||||||
|
defaultCapturedAt: snapshot.defaultCapturedAt,
|
||||||
|
defaultCaptureStatus: snapshot.defaultCaptureStatus,
|
||||||
|
defaultErrorMessage: snapshot.defaultErrorMessage,
|
||||||
|
isDefaultRole: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRoleStateFromCurrentInfo({
|
||||||
|
flow,
|
||||||
|
roleInfo,
|
||||||
|
now,
|
||||||
|
emptyMessage,
|
||||||
|
}: {
|
||||||
|
flow: ReturnType<typeof normalizeKuaishouCloudFlow>
|
||||||
|
roleInfo: { name: string; rid: string; rawInfo?: unknown }
|
||||||
|
now: string
|
||||||
|
emptyMessage: string
|
||||||
|
}) {
|
||||||
|
const hasRoleInfo = Boolean(roleInfo.name || roleInfo.rid)
|
||||||
|
const hasDefaultRole = hasKuaishouCloudDefaultRoleSnapshot(flow)
|
||||||
|
const isDefaultRole =
|
||||||
|
hasRoleInfo &&
|
||||||
|
hasDefaultRole &&
|
||||||
|
isSameKuaishouCloudRoleIdentity(
|
||||||
|
{ name: roleInfo.name, rid: roleInfo.rid },
|
||||||
|
{ name: flow.role.defaultName, rid: flow.role.defaultRid },
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasRoleInfo,
|
||||||
|
isDefaultRole,
|
||||||
|
bindingRoleName: hasRoleInfo ? roleInfo.name : '',
|
||||||
|
bindingRoleId: hasRoleInfo ? roleInfo.rid : '',
|
||||||
|
role: {
|
||||||
|
...flow.role,
|
||||||
|
status: hasRoleInfo && !isDefaultRole ? 'ready' : 'pending',
|
||||||
|
name: hasRoleInfo ? roleInfo.name : '',
|
||||||
|
rid: hasRoleInfo ? roleInfo.rid : '',
|
||||||
|
refreshedAt: now,
|
||||||
|
errorMessage: hasRoleInfo
|
||||||
|
? isDefaultRole
|
||||||
|
? '当前仍是虚拟机默认角色,请重新绑定自己的角色信息'
|
||||||
|
: ''
|
||||||
|
: emptyMessage,
|
||||||
|
rawInfo: roleInfo.rawInfo || null,
|
||||||
|
isDefaultRole,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function ensureTaskClaimLink(task: TaskRow) {
|
export async function ensureTaskClaimLink(task: TaskRow) {
|
||||||
const tokenStatus = String(task?.primary_claim_token_status || '').trim()
|
const tokenStatus = String(task?.primary_claim_token_status || '').trim()
|
||||||
const token = String(task?.primary_claim_token || task?.claim_token || '').trim()
|
const token = String(task?.primary_claim_token || task?.claim_token || '').trim()
|
||||||
@@ -201,6 +312,11 @@ export async function prepareKuaishouCloudFulfillmentTask(task: TaskRow, options
|
|||||||
cloudContext,
|
cloudContext,
|
||||||
vnKeyCandidates,
|
vnKeyCandidates,
|
||||||
})
|
})
|
||||||
|
const defaultRoleSnapshot = await captureKuaishouCloudDefaultRoleSnapshot({
|
||||||
|
cloudContext,
|
||||||
|
preparedBinding,
|
||||||
|
now,
|
||||||
|
})
|
||||||
|
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
@@ -222,14 +338,7 @@ export async function prepareKuaishouCloudFulfillmentTask(task: TaskRow, options
|
|||||||
roleName: '',
|
roleName: '',
|
||||||
roleId: '',
|
roleId: '',
|
||||||
},
|
},
|
||||||
role: {
|
role: buildPendingRoleWithDefaultSnapshot(defaultRoleSnapshot),
|
||||||
status: 'pending',
|
|
||||||
name: '',
|
|
||||||
rid: '',
|
|
||||||
refreshedAt: null as null,
|
|
||||||
errorMessage: '',
|
|
||||||
rawInfo: null as null,
|
|
||||||
},
|
|
||||||
purchase: {
|
purchase: {
|
||||||
...flowWithResolvedBinding.purchase,
|
...flowWithResolvedBinding.purchase,
|
||||||
usedKnapsack,
|
usedKnapsack,
|
||||||
@@ -281,6 +390,9 @@ export async function prepareKuaishouCloudFulfillmentTask(task: TaskRow, options
|
|||||||
})),
|
})),
|
||||||
resolvedByName: resolvedBinding.resolvedByName,
|
resolvedByName: resolvedBinding.resolvedByName,
|
||||||
accountSelection: buildCloudtentaclesSourceSelectionEventDetail(selectedCloud),
|
accountSelection: buildCloudtentaclesSourceSelectionEventDetail(selectedCloud),
|
||||||
|
defaultRoleName: defaultRoleSnapshot.defaultName,
|
||||||
|
defaultRoleId: defaultRoleSnapshot.defaultRid,
|
||||||
|
defaultRoleCaptureStatus: defaultRoleSnapshot.defaultCaptureStatus,
|
||||||
actor,
|
actor,
|
||||||
},
|
},
|
||||||
now,
|
now,
|
||||||
@@ -377,6 +489,14 @@ export async function refreshKuaishouCloudTaskBindUrl(task: TaskRow, options: Js
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultRoleSnapshot =
|
||||||
|
normalizeTaskStatus(task.task_status) === TASK_STATUS.ROLE_CONFIRMED
|
||||||
|
? null
|
||||||
|
: await captureKuaishouCloudDefaultRoleSnapshot({
|
||||||
|
cloudContext,
|
||||||
|
preparedBinding,
|
||||||
|
now,
|
||||||
|
})
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
kuaishouCloudFulfillment: {
|
kuaishouCloudFulfillment: {
|
||||||
@@ -405,14 +525,7 @@ export async function refreshKuaishouCloudTaskBindUrl(task: TaskRow, options: Js
|
|||||||
role:
|
role:
|
||||||
normalizeTaskStatus(task.task_status) === TASK_STATUS.ROLE_CONFIRMED
|
normalizeTaskStatus(task.task_status) === TASK_STATUS.ROLE_CONFIRMED
|
||||||
? flow.role
|
? flow.role
|
||||||
: {
|
: buildPendingRoleWithDefaultSnapshot(defaultRoleSnapshot!),
|
||||||
status: 'pending',
|
|
||||||
name: '',
|
|
||||||
rid: '',
|
|
||||||
refreshedAt: null,
|
|
||||||
errorMessage: '',
|
|
||||||
rawInfo: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +761,11 @@ export async function rebindKuaishouCloudTaskRole(task: TaskRow, options: JsonOb
|
|||||||
|
|
||||||
const claimLinkState = await ensureTaskClaimLink(task)
|
const claimLinkState = await ensureTaskClaimLink(task)
|
||||||
const nextBindExpiresAt = resolveKuaishouCloudBindUrlExpiresAt(now)
|
const nextBindExpiresAt = resolveKuaishouCloudBindUrlExpiresAt(now)
|
||||||
|
const defaultRoleSnapshot = await captureKuaishouCloudDefaultRoleSnapshot({
|
||||||
|
cloudContext,
|
||||||
|
preparedBinding,
|
||||||
|
now,
|
||||||
|
})
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
kuaishouCloudFulfillment: {
|
kuaishouCloudFulfillment: {
|
||||||
@@ -667,14 +785,7 @@ export async function rebindKuaishouCloudTaskRole(task: TaskRow, options: JsonOb
|
|||||||
roleName: '',
|
roleName: '',
|
||||||
roleId: '',
|
roleId: '',
|
||||||
},
|
},
|
||||||
role: {
|
role: buildPendingRoleWithDefaultSnapshot(defaultRoleSnapshot),
|
||||||
status: 'pending',
|
|
||||||
name: '',
|
|
||||||
rid: '',
|
|
||||||
refreshedAt: null,
|
|
||||||
errorMessage: '',
|
|
||||||
rawInfo: null,
|
|
||||||
},
|
|
||||||
rebind: {
|
rebind: {
|
||||||
...previousRebind,
|
...previousRebind,
|
||||||
currentAttempt: attempt,
|
currentAttempt: attempt,
|
||||||
@@ -723,6 +834,9 @@ export async function rebindKuaishouCloudTaskRole(task: TaskRow, options: JsonOb
|
|||||||
vnId: preparedBinding.vnId,
|
vnId: preparedBinding.vnId,
|
||||||
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
|
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
|
||||||
bindUrl: preparedBinding.bindUrl,
|
bindUrl: preparedBinding.bindUrl,
|
||||||
|
defaultRoleName: defaultRoleSnapshot.defaultName,
|
||||||
|
defaultRoleId: defaultRoleSnapshot.defaultRid,
|
||||||
|
defaultRoleCaptureStatus: defaultRoleSnapshot.defaultCaptureStatus,
|
||||||
actor,
|
actor,
|
||||||
},
|
},
|
||||||
now,
|
now,
|
||||||
@@ -787,6 +901,14 @@ export async function probeKuaishouCloudTaskBindUrl(task: TaskRow, options: Json
|
|||||||
|
|
||||||
const probeRoleInfo = normalizeKuaishouCloudRoleInfo(probe.roleInfo)
|
const probeRoleInfo = normalizeKuaishouCloudRoleInfo(probe.roleInfo)
|
||||||
const hasRoleInfo = Boolean(probeRoleInfo.name || probeRoleInfo.rid)
|
const hasRoleInfo = Boolean(probeRoleInfo.name || probeRoleInfo.rid)
|
||||||
|
const roleState = hasRoleInfo
|
||||||
|
? buildRoleStateFromCurrentInfo({
|
||||||
|
flow,
|
||||||
|
roleInfo: probeRoleInfo,
|
||||||
|
now,
|
||||||
|
emptyMessage: '当前还没有查询到角色信息,请完成绑定后稍等片刻再试',
|
||||||
|
})
|
||||||
|
: null
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
kuaishouCloudFulfillment: {
|
kuaishouCloudFulfillment: {
|
||||||
@@ -796,24 +918,16 @@ export async function probeKuaishouCloudTaskBindUrl(task: TaskRow, options: Json
|
|||||||
bindProbeAt: now,
|
bindProbeAt: now,
|
||||||
bindProbeStatus: probe.valid ? 'valid' : 'invalid',
|
bindProbeStatus: probe.valid ? 'valid' : 'invalid',
|
||||||
bindProbeMessage: String(probe.message || probe.reason || '').trim(),
|
bindProbeMessage: String(probe.message || probe.reason || '').trim(),
|
||||||
roleName: hasRoleInfo ? probeRoleInfo.name : flow.binding.roleName,
|
roleName: roleState ? roleState.bindingRoleName : flow.binding.roleName,
|
||||||
roleId: hasRoleInfo ? probeRoleInfo.rid : flow.binding.roleId,
|
roleId: roleState ? roleState.bindingRoleId : 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,
|
|
||||||
},
|
},
|
||||||
|
role: roleState ? roleState.role : flow.role,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedTask = await updateTask(task.id, {
|
const updatedTask = await updateTask(task.id, {
|
||||||
role_id: hasRoleInfo ? probeRoleInfo.rid : task.role_id,
|
role_id: roleState && !roleState.isDefaultRole ? probeRoleInfo.rid : task.role_id,
|
||||||
role_name: hasRoleInfo ? probeRoleInfo.name : task.role_name,
|
role_name: roleState && !roleState.isDefaultRole ? probeRoleInfo.name : task.role_name,
|
||||||
context_json: JSON.stringify(nextContext),
|
context_json: JSON.stringify(nextContext),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
})
|
})
|
||||||
@@ -956,30 +1070,28 @@ export async function refreshKuaishouCloudTaskRoleInfo(task: TaskRow, options: J
|
|||||||
})
|
})
|
||||||
|
|
||||||
const bindInfo = normalizeKuaishouCloudRoleInfo(bindInfoResult.bindInfo)
|
const bindInfo = normalizeKuaishouCloudRoleInfo(bindInfoResult.bindInfo)
|
||||||
const hasRoleInfo = Boolean(bindInfo.name || bindInfo.rid)
|
const roleState = buildRoleStateFromCurrentInfo({
|
||||||
|
flow,
|
||||||
|
roleInfo: bindInfo,
|
||||||
|
now,
|
||||||
|
emptyMessage: '当前还没有查询到角色信息,请完成绑定后稍等片刻再试',
|
||||||
|
})
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...taskContext,
|
...taskContext,
|
||||||
kuaishouCloudFulfillment: {
|
kuaishouCloudFulfillment: {
|
||||||
...flow,
|
...flow,
|
||||||
binding: {
|
binding: {
|
||||||
...flow.binding,
|
...flow.binding,
|
||||||
roleName: hasRoleInfo ? bindInfo.name : '',
|
roleName: roleState.bindingRoleName,
|
||||||
roleId: hasRoleInfo ? bindInfo.rid : '',
|
roleId: roleState.bindingRoleId,
|
||||||
},
|
|
||||||
role: {
|
|
||||||
status: hasRoleInfo ? 'ready' : 'pending',
|
|
||||||
name: hasRoleInfo ? bindInfo.name : '',
|
|
||||||
rid: hasRoleInfo ? bindInfo.rid : '',
|
|
||||||
refreshedAt: now,
|
|
||||||
errorMessage: hasRoleInfo ? '' : '当前还没有查询到角色信息,请完成绑定后稍等片刻再试',
|
|
||||||
rawInfo: bindInfo.rawInfo,
|
|
||||||
},
|
},
|
||||||
|
role: roleState.role,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedTask = await updateTask(task.id, {
|
const updatedTask = await updateTask(task.id, {
|
||||||
role_id: hasRoleInfo ? bindInfo.rid : '',
|
role_id: roleState.hasRoleInfo && !roleState.isDefaultRole ? bindInfo.rid : '',
|
||||||
role_name: hasRoleInfo ? bindInfo.name : '',
|
role_name: roleState.hasRoleInfo && !roleState.isDefaultRole ? bindInfo.name : '',
|
||||||
context_json: JSON.stringify(nextContext),
|
context_json: JSON.stringify(nextContext),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -93,6 +93,12 @@ export interface ClaimKuaishouCloudFlowInfo {
|
|||||||
rid: string
|
rid: string
|
||||||
refreshedAt: string | null
|
refreshedAt: string | null
|
||||||
errorMessage: string
|
errorMessage: string
|
||||||
|
defaultName: string
|
||||||
|
defaultRid: string
|
||||||
|
defaultCapturedAt: string | null
|
||||||
|
defaultCaptureStatus: string
|
||||||
|
defaultErrorMessage: string
|
||||||
|
isDefaultRole: boolean
|
||||||
}
|
}
|
||||||
purchase: {
|
purchase: {
|
||||||
autoBuyEnabled: boolean
|
autoBuyEnabled: boolean
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ const claim = useKuaishouCloudClaim(() => props.token)
|
|||||||
:qr-code-data-url="claim.qrCodeDataUrl.value"
|
:qr-code-data-url="claim.qrCodeDataUrl.value"
|
||||||
:is-binding-prepared="claim.isBindingPrepared.value"
|
:is-binding-prepared="claim.isBindingPrepared.value"
|
||||||
:is-binding-preparing="claim.isBindingPreparing.value"
|
:is-binding-preparing="claim.isBindingPreparing.value"
|
||||||
:is-role-ready="claim.isRoleReady.value"
|
:is-customer-role-ready="claim.isCustomerRoleReady.value"
|
||||||
|
:confirm-role-disabled-reason="claim.confirmRoleDisabledReason.value"
|
||||||
:role-name="claim.roleName.value"
|
:role-name="claim.roleName.value"
|
||||||
:role-id="claim.roleId.value"
|
:role-id="claim.roleId.value"
|
||||||
:refreshing-role="claim.refreshingRole.value"
|
:refreshing-role="claim.refreshingRole.value"
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ defineProps<{
|
|||||||
qrCodeDataUrl: string
|
qrCodeDataUrl: string
|
||||||
isBindingPrepared: boolean
|
isBindingPrepared: boolean
|
||||||
isBindingPreparing: boolean
|
isBindingPreparing: boolean
|
||||||
isRoleReady: boolean
|
isCustomerRoleReady: boolean
|
||||||
|
confirmRoleDisabledReason: string
|
||||||
roleName: string
|
roleName: string
|
||||||
roleId: string
|
roleId: string
|
||||||
refreshingRole: boolean
|
refreshingRole: boolean
|
||||||
@@ -41,7 +42,7 @@ defineEmits<{
|
|||||||
<span>系统正在准备绑定资源,请稍后自动刷新。</span>
|
<span>系统正在准备绑定资源,请稍后自动刷新。</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="role-panel" :class="{ pending: !isRoleReady }">
|
<div class="role-panel" :class="{ pending: !isCustomerRoleReady }">
|
||||||
<div class="role-panel__item">
|
<div class="role-panel__item">
|
||||||
<span>当前角色</span>
|
<span>当前角色</span>
|
||||||
<strong>{{ roleName || '待识别' }}</strong>
|
<strong>{{ roleName || '待识别' }}</strong>
|
||||||
@@ -52,6 +53,10 @@ defineEmits<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="flow.role.isDefaultRole" class="status-banner warning">
|
||||||
|
<span>当前仍是虚拟机默认角色,请重新绑定自己的角色信息。</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="action-row">
|
<div class="action-row">
|
||||||
<el-button size="large" plain :loading="refreshingRole" @click="$emit('refreshRole')">
|
<el-button size="large" plain :loading="refreshingRole" @click="$emit('refreshRole')">
|
||||||
<el-icon><RefreshRight /></el-icon>
|
<el-icon><RefreshRight /></el-icon>
|
||||||
@@ -66,15 +71,23 @@ defineEmits<{
|
|||||||
>
|
>
|
||||||
换绑角色
|
换绑角色
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-tooltip
|
||||||
type="primary"
|
:disabled="!confirmRoleDisabledReason"
|
||||||
size="large"
|
:content="confirmRoleDisabledReason"
|
||||||
:loading="confirmingRole"
|
placement="top"
|
||||||
:disabled="!isBindingPrepared || !isRoleReady || rebindingRole"
|
|
||||||
@click="$emit('confirmRole')"
|
|
||||||
>
|
>
|
||||||
我已完成绑定,下一步
|
<span class="action-tooltip-wrap">
|
||||||
</el-button>
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:loading="confirmingRole"
|
||||||
|
:disabled="Boolean(confirmRoleDisabledReason)"
|
||||||
|
@click="$emit('confirmRole')"
|
||||||
|
>
|
||||||
|
我已完成绑定,下一步
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="meta-footer">
|
<div class="meta-footer">
|
||||||
@@ -127,6 +140,11 @@ defineEmits<{
|
|||||||
color: #1d4ed8;
|
color: #1d4ed8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-banner.warning {
|
||||||
|
background: #fff7ed;
|
||||||
|
color: #c2410c;
|
||||||
|
}
|
||||||
|
|
||||||
.qr-block {
|
.qr-block {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -239,6 +257,10 @@ defineEmits<{
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-tooltip-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
.action-row :deep(.el-button) {
|
.action-row :deep(.el-button) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,31 @@ export function useKuaishouCloudClaim(token: () => string) {
|
|||||||
const isBindingPreparing = computed(() => flow.value?.binding.prepareStatus === 'pending')
|
const isBindingPreparing = computed(() => flow.value?.binding.prepareStatus === 'pending')
|
||||||
const canEnterBindingStep = computed(() => isTicketVerified.value)
|
const canEnterBindingStep = computed(() => isTicketVerified.value)
|
||||||
const isRoleReady = computed(() => Boolean(roleName.value || roleId.value))
|
const isRoleReady = computed(() => Boolean(roleName.value || roleId.value))
|
||||||
|
const hasDefaultRoleSnapshot = computed(() =>
|
||||||
|
Boolean(flow.value?.role.defaultName || flow.value?.role.defaultRid),
|
||||||
|
)
|
||||||
|
const isDefaultRole = computed(() => flow.value?.role.isDefaultRole === true)
|
||||||
|
const isCustomerRoleReady = computed(
|
||||||
|
() => isRoleReady.value && hasDefaultRoleSnapshot.value && !isDefaultRole.value,
|
||||||
|
)
|
||||||
|
const confirmRoleDisabledReason = computed(() => {
|
||||||
|
if (!isBindingPrepared.value) {
|
||||||
|
return '绑定二维码还在准备中,请稍后自动刷新'
|
||||||
|
}
|
||||||
|
if (!hasDefaultRoleSnapshot.value) {
|
||||||
|
return flow.value?.role.defaultErrorMessage || '系统还未获取到虚拟机默认角色信息,请稍后刷新'
|
||||||
|
}
|
||||||
|
if (!isRoleReady.value) {
|
||||||
|
return '请先扫码绑定自己的角色,并刷新角色信息'
|
||||||
|
}
|
||||||
|
if (isDefaultRole.value) {
|
||||||
|
return '当前仍是虚拟机默认角色,请重新绑定自己的角色信息'
|
||||||
|
}
|
||||||
|
if (rebindingRole.value) {
|
||||||
|
return '正在换绑角色,请稍候'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
const isRoleConfirmed = computed(() => isKuaishouCloudRoleConfirmedStatus(task.value?.status))
|
const isRoleConfirmed = computed(() => isKuaishouCloudRoleConfirmedStatus(task.value?.status))
|
||||||
const isDispatched = computed(
|
const isDispatched = computed(
|
||||||
() => String(flow.value?.dispatch.status || '').trim() === 'success',
|
() => String(flow.value?.dispatch.status || '').trim() === 'success',
|
||||||
@@ -239,11 +264,15 @@ export function useKuaishouCloudClaim(token: () => string) {
|
|||||||
refreshingRole.value = true
|
refreshingRole.value = true
|
||||||
try {
|
try {
|
||||||
await loadDetail({ silent: true })
|
await loadDetail({ silent: true })
|
||||||
if (isRoleReady.value) {
|
if (isCustomerRoleReady.value) {
|
||||||
showSuccess('角色信息已刷新')
|
showSuccess('角色信息已刷新')
|
||||||
|
} else if (isDefaultRole.value) {
|
||||||
|
showError('当前仍是虚拟机默认角色,请重新绑定自己的角色信息')
|
||||||
} else {
|
} else {
|
||||||
showError(
|
showError(
|
||||||
flow.value?.role.errorMessage || '暂时还没有识别到角色信息,请完成绑定后稍等片刻再试',
|
flow.value?.role.errorMessage ||
|
||||||
|
flow.value?.role.defaultErrorMessage ||
|
||||||
|
'暂时还没有识别到角色信息,请完成绑定后稍等片刻再试',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -441,6 +470,10 @@ export function useKuaishouCloudClaim(token: () => string) {
|
|||||||
isBindingPreparing,
|
isBindingPreparing,
|
||||||
canEnterBindingStep,
|
canEnterBindingStep,
|
||||||
isRoleReady,
|
isRoleReady,
|
||||||
|
hasDefaultRoleSnapshot,
|
||||||
|
isDefaultRole,
|
||||||
|
isCustomerRoleReady,
|
||||||
|
confirmRoleDisabledReason,
|
||||||
isRoleConfirmed,
|
isRoleConfirmed,
|
||||||
isDispatched,
|
isDispatched,
|
||||||
isRedeemFailed,
|
isRedeemFailed,
|
||||||
|
|||||||
Reference in New Issue
Block a user