Files
order_site/apps/backend/src/domain/task-status.ts
T
yml2213 8de56b0354 优化 UID 履约闸门、运营可见性与一键兑换
lewan 发货强制 expectedUid 并取消无 UID 旧路径回落;任务详情展示 UID 匹配态;领取页匹配后可一键兑换;补充 identity 单测并收口短链说明。
2026-07-10 13:51:02 +08:00

332 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.WAITING_BINDING,
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,
])
const KUAISHOU_CLOUD_REBINDABLE_STATUSES = new Set<TaskStatus>([
TASK_STATUS.WAITING_BINDING,
TASK_STATUS.ROLE_CONFIRMED,
TASK_STATUS.MANUAL_REVIEW,
TASK_STATUS.RETRY_PENDING,
])
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 {
const normalized = normalizeTaskStatus(status)
// 允许 WAITING_BINDING:UID 匹配后可一键确认+兑换
return (
normalized === TASK_STATUS.ROLE_CONFIRMED ||
normalized === TASK_STATUS.WAITING_BINDING
)
}
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 canRebindKuaishouCloudRoleStatus(status: unknown): boolean {
return KUAISHOU_CLOUD_REBINDABLE_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
}