优化快手 Cloud 主链路状态机

This commit is contained in:
yml
2026-05-28 10:03:23 +08:00
parent 0876e3ff5b
commit 9709643583
14 changed files with 513 additions and 122 deletions
@@ -0,0 +1,27 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import {
TASK_STATUS,
canRedeemKuaishouCloudClaimStatus,
canTaskTransition,
isClaimInactiveTaskStatus,
isOpen91FailedTaskStatus,
shouldEnsureKuaishouCloudClaimLink,
} from './task-status.js'
test('任务状态机声明快手 Cloud 主流程跳转', () => {
assert.equal(canTaskTransition(TASK_STATUS.PENDING_BINDING_PREPARE, TASK_STATUS.WAITING_BINDING), true)
assert.equal(canTaskTransition(TASK_STATUS.WAITING_BINDING, TASK_STATUS.ROLE_CONFIRMED), true)
assert.equal(canTaskTransition(TASK_STATUS.ROLE_CONFIRMED, TASK_STATUS.REDEEMING), true)
assert.equal(canTaskTransition(TASK_STATUS.REDEEMING, TASK_STATUS.DISPATCHED_PENDING_RETURN), true)
assert.equal(canTaskTransition(TASK_STATUS.DISPATCHED_PENDING_RETURN, TASK_STATUS.COMPLETED), true)
})
test('任务状态机收敛领取和 91 查询的业务判断', () => {
assert.equal(shouldEnsureKuaishouCloudClaimLink(TASK_STATUS.PENDING_BINDING_PREPARE), true)
assert.equal(canRedeemKuaishouCloudClaimStatus(TASK_STATUS.ROLE_CONFIRMED), true)
assert.equal(canRedeemKuaishouCloudClaimStatus(TASK_STATUS.WAITING_BINDING), false)
assert.equal(isClaimInactiveTaskStatus(TASK_STATUS.MANUAL_REVIEW), true)
assert.equal(isOpen91FailedTaskStatus(TASK_STATUS.CLOSED), true)
})
+313
View File
@@ -0,0 +1,313 @@
export const TASK_STATUS = {
PENDING_PAYMENT: 'pending_payment',
PAID: 'paid',
LINK_GENERATED: 'link_generated',
CLAIMED: 'claimed',
PENDING_BINDING_PREPARE: 'pending_binding_prepare',
WAITING_BINDING: 'waiting_binding',
ROLE_CONFIRMED: 'role_confirmed',
REDEEMING: 'redeeming',
DISPATCHED_PENDING_RETURN: 'dispatched_pending_return',
COMPLETED: 'completed',
REDEEMED: 'redeemed',
RETRY_PENDING: 'retry_pending',
MANUAL_REVIEW: 'manual_review',
FAILED: 'failed',
EXPIRED: 'expired',
CLOSED: 'closed',
} as const
export type KnownTaskStatus = typeof TASK_STATUS[keyof typeof TASK_STATUS]
export type TaskStatus = KnownTaskStatus | (string & {})
const TASK_TRANSITIONS: Record<KnownTaskStatus, readonly KnownTaskStatus[]> = {
[TASK_STATUS.PENDING_PAYMENT]: [
TASK_STATUS.PAID,
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.PAID]: [
TASK_STATUS.LINK_GENERATED,
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.LINK_GENERATED]: [
TASK_STATUS.CLAIMED,
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.CLAIMED]: [
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.PENDING_BINDING_PREPARE]: [
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.WAITING_BINDING]: [
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.REDEEMING,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.ROLE_CONFIRMED]: [
TASK_STATUS.REDEEMING,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.REDEEMING]: [
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
],
[TASK_STATUS.DISPATCHED_PENDING_RETURN]: [
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.MANUAL_REVIEW,
],
[TASK_STATUS.RETRY_PENDING]: [
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.REDEEMING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.MANUAL_REVIEW]: [
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.REDEEMING,
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.FAILED]: [
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.CLOSED,
],
[TASK_STATUS.COMPLETED]: [],
[TASK_STATUS.REDEEMED]: [],
[TASK_STATUS.EXPIRED]: [],
[TASK_STATUS.CLOSED]: [],
}
const FINAL_STATUSES = new Set<TaskStatus>([
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
])
const OPEN_91_FAILED_STATUSES = new Set<TaskStatus>([
TASK_STATUS.FAILED,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.CLOSED,
])
const CLAIM_INACTIVE_STATUSES = new Set<TaskStatus>([
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.FAILED,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
])
const PAID_PREPARATION_STABLE_STATUSES = new Set<TaskStatus>([
TASK_STATUS.LINK_GENERATED,
TASK_STATUS.CLAIMED,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.REDEEMING,
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.EXPIRED,
TASK_STATUS.CLOSED,
])
const KUAISHOU_CLOUD_CLAIM_LINK_STATUSES = new Set<TaskStatus>([
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.FAILED,
])
const KUAISHOU_CLOUD_ROLE_CONFIRM_SETTLED_STATUSES = new Set<TaskStatus>([
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.REDEEMING,
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.MANUAL_REVIEW,
])
const KUAISHOU_CLOUD_REDEEM_SETTLED_STATUSES = new Set<TaskStatus>([
TASK_STATUS.REDEEMING,
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.MANUAL_REVIEW,
])
const CLAIM_LINK_REGENERATABLE_STATUSES = new Set<TaskStatus>([
TASK_STATUS.LINK_GENERATED,
TASK_STATUS.CLAIMED,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
])
const KUAISHOU_CLOUD_RESULT_STATUSES = new Set<TaskStatus>([
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.FAILED,
])
const FULFILLMENT_COMPLETED_STATUSES = new Set<TaskStatus>([
TASK_STATUS.DISPATCHED_PENDING_RETURN,
TASK_STATUS.COMPLETED,
TASK_STATUS.REDEEMED,
])
const RETRYABLE_TASK_STATUSES = new Set<TaskStatus>([
TASK_STATUS.RETRY_PENDING,
TASK_STATUS.MANUAL_REVIEW,
])
const KUAISHOU_CLOUD_PREPARABLE_STATUSES = new Set<TaskStatus>([
TASK_STATUS.PENDING_BINDING_PREPARE,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.FAILED,
])
export function normalizeTaskStatus(value: unknown): TaskStatus {
return String(value || '').trim() as TaskStatus
}
export function canTaskTransition(from: unknown, to: unknown): boolean {
const current = normalizeTaskStatus(from) as KnownTaskStatus
const next = normalizeTaskStatus(to) as KnownTaskStatus
if (!current || !next || current === next) {
return Boolean(current && next)
}
return Boolean(TASK_TRANSITIONS[current]?.includes(next))
}
export function getTaskNextStatuses(status: unknown): readonly KnownTaskStatus[] {
return TASK_TRANSITIONS[normalizeTaskStatus(status) as KnownTaskStatus] || []
}
export function isTaskFinalStatus(status: unknown): boolean {
return FINAL_STATUSES.has(normalizeTaskStatus(status))
}
export function isOpen91FailedTaskStatus(status: unknown): boolean {
return OPEN_91_FAILED_STATUSES.has(normalizeTaskStatus(status))
}
export function isClaimInactiveTaskStatus(status: unknown): boolean {
return CLAIM_INACTIVE_STATUSES.has(normalizeTaskStatus(status))
}
export function isPaidPreparationStableStatus(status: unknown): boolean {
return PAID_PREPARATION_STABLE_STATUSES.has(normalizeTaskStatus(status))
}
export function shouldEnsureKuaishouCloudClaimLink(status: unknown): boolean {
return KUAISHOU_CLOUD_CLAIM_LINK_STATUSES.has(normalizeTaskStatus(status))
}
export function isKuaishouCloudRoleConfirmSettledStatus(status: unknown): boolean {
return KUAISHOU_CLOUD_ROLE_CONFIRM_SETTLED_STATUSES.has(normalizeTaskStatus(status))
}
export function isKuaishouCloudRedeemSettledStatus(status: unknown): boolean {
return KUAISHOU_CLOUD_REDEEM_SETTLED_STATUSES.has(normalizeTaskStatus(status))
}
export function canRedeemKuaishouCloudClaimStatus(status: unknown): boolean {
return normalizeTaskStatus(status) === TASK_STATUS.ROLE_CONFIRMED
}
export function canRegenerateClaimLinkStatus(status: unknown): boolean {
return CLAIM_LINK_REGENERATABLE_STATUSES.has(normalizeTaskStatus(status))
}
export function hasKuaishouCloudRedeemResultStatus(status: unknown): boolean {
return KUAISHOU_CLOUD_RESULT_STATUSES.has(normalizeTaskStatus(status))
}
export function isTaskFulfillmentCompletedStatus(status: unknown): boolean {
return FULFILLMENT_COMPLETED_STATUSES.has(normalizeTaskStatus(status))
}
export function canRetryTaskStatus(status: unknown): boolean {
return RETRYABLE_TASK_STATUSES.has(normalizeTaskStatus(status))
}
export function canMarkManualReviewTaskStatus(status: unknown): boolean {
const normalized = normalizeTaskStatus(status)
return normalized !== TASK_STATUS.MANUAL_REVIEW && !isTaskFinalStatus(normalized)
}
export function canCompleteManualDispatchTaskStatus(status: unknown): boolean {
return !isTaskFinalStatus(status)
}
export function canPrepareKuaishouCloudFulfillmentStatus(status: unknown): boolean {
return KUAISHOU_CLOUD_PREPARABLE_STATUSES.has(normalizeTaskStatus(status))
}
export function canDispatchKuaishouCloudFulfillmentStatus(status: unknown): boolean {
return normalizeTaskStatus(status) === TASK_STATUS.WAITING_BINDING
}
export function canReturnKuaishouCloudFulfillmentStatus(status: unknown): boolean {
return normalizeTaskStatus(status) === TASK_STATUS.DISPATCHED_PENDING_RETURN
}
export function resolveInitialPaidTaskStatus(profile: {
requires_claim?: boolean
executor_key?: unknown
} | null | undefined): TaskStatus {
if (Boolean(profile?.requires_claim)) {
return TASK_STATUS.PAID
}
if (String(profile?.executor_key || '').trim() === 'kuaishou_ct_assisted') {
return TASK_STATUS.PENDING_BINDING_PREPARE
}
if (String(profile?.executor_key || '').trim() === 'manual_dispatch') {
return TASK_STATUS.MANUAL_REVIEW
}
return TASK_STATUS.PAID
}