export type UnifiedOrderSource = 'goods' | 'service' export type OrderStatusSource = { source?: UnifiedOrderSource | string order_status: number payment_status: number pay_expire_at: string created_at: string cancel_reason: string } export const ORDER_STATUS_PENDING = 1 export const ORDER_STATUS_PAID_OR_SHIPPING = 2 export const ORDER_STATUS_RECEIVING = 3 export const ORDER_STATUS_COMPLETED = 4 export const ORDER_STATUS_CANCELLED = 5 export const ORDER_STATUS_REFUNDING = 6 export const ORDER_STATUS_REFUNDED = 7 export const ORDER_STATUS_TIMEOUT_LEGACY = 8 export const PAYMENT_STATUS_UNPAID = 1 export const PAYMENT_STATUS_PAID = 2 export const PAYMENT_STATUS_PARTIAL_REFUND = 3 export const PAYMENT_STATUS_REFUNDED = 4 export const PAYMENT_STATUS_TIMEOUT = 5 export const ORDER_PAY_TIMEOUT_SECONDS = 10 * 60 export const ORDER_TIMEOUT_CANCEL_REASON = '支付超时自动取消' function parseDateMs(value: string): number { if (value == '') { return 0 } const date = new Date(value) if (isNaN(date.getTime())) { return 0 } return date.getTime() } function containsTimeoutReason(cancelReason: string): boolean { return cancelReason.indexOf('超时') >= 0 } function getNormalizedSource(order: OrderStatusSource): UnifiedOrderSource { if (order.source == 'service') { return 'service' } return 'goods' } function isPendingSourceOrder(order: OrderStatusSource): boolean { return order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID } export function getOrderDeadlineMs(order: OrderStatusSource): number { const payExpireAtMs = parseDateMs(order.pay_expire_at) if (payExpireAtMs > 0) { return payExpireAtMs } const createdAtMs = parseDateMs(order.created_at) if (createdAtMs > 0) { return createdAtMs + ORDER_PAY_TIMEOUT_SECONDS * 1000 } return 0 } export function getRemainingSeconds(order: OrderStatusSource): number { const deadlineMs = getOrderDeadlineMs(order) if (deadlineMs <= 0) { return 0 } const diff = Math.floor((deadlineMs - Date.now()) / 1000) return diff > 0 ? diff : 0 } export function getUnifiedDisplayState(order: OrderStatusSource): string { const source = getNormalizedSource(order) const deadlineMs = getOrderDeadlineMs(order) const hasExpiredByDeadline = order.order_status == ORDER_STATUS_PENDING && deadlineMs > 0 && deadlineMs <= Date.now() if (source == 'service') { if (order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID && !hasExpiredByDeadline && !containsTimeoutReason(order.cancel_reason)) { return 'pending_pay' } if (order.order_status == ORDER_STATUS_TIMEOUT_LEGACY || order.payment_status == PAYMENT_STATUS_TIMEOUT || containsTimeoutReason(order.cancel_reason)) { return 'cancelled' } if (hasExpiredByDeadline) { return 'expired' } if (order.order_status == 2 || order.order_status == 3 || order.order_status == 4) { return 'processing' } if (order.order_status == ORDER_STATUS_COMPLETED) { return 'completed' } if (order.order_status == ORDER_STATUS_REFUNDING || order.order_status == ORDER_STATUS_REFUNDED) { return 'refund' } return 'unknown' } if (order.order_status == ORDER_STATUS_PENDING && order.payment_status == PAYMENT_STATUS_UNPAID && !hasExpiredByDeadline && !containsTimeoutReason(order.cancel_reason)) { return 'pending_pay' } if (order.order_status == ORDER_STATUS_CANCELLED || order.order_status == ORDER_STATUS_TIMEOUT_LEGACY || order.payment_status == PAYMENT_STATUS_TIMEOUT || containsTimeoutReason(order.cancel_reason)) { return 'cancelled' } if (hasExpiredByDeadline) { return 'expired' } if (order.order_status == ORDER_STATUS_PAID_OR_SHIPPING || order.order_status == ORDER_STATUS_RECEIVING) { return 'processing' } if (order.order_status == ORDER_STATUS_COMPLETED) { return 'completed' } if (order.order_status == ORDER_STATUS_REFUNDING || order.order_status == ORDER_STATUS_REFUNDED) { return 'refund' } return 'unknown' } export function isOrderPayExpired(order: OrderStatusSource): boolean { const displayState = getUnifiedDisplayState(order) return displayState == 'cancelled' || displayState == 'expired' } export function isPendingPayOrder(order: OrderStatusSource): boolean { return getUnifiedDisplayState(order) == 'pending_pay' && isPendingSourceOrder(order) } export function getOrderDisplayStatus(order: OrderStatusSource): string { const displayState = getUnifiedDisplayState(order) if (displayState == 'pending_pay') { return 'pending' } if (displayState == 'cancelled' || displayState == 'expired') { return 'cancelled' } if (displayState == 'processing') { return 'shipping' } if (displayState == 'completed') { return 'completed' } if (displayState == 'refund') { return 'refund' } return 'unknown' } function pad2(value: number): string { return value < 10 ? '0' + value : '' + value } export function formatCountdownHM(seconds: number): string { const safeSeconds = seconds > 0 ? seconds : 0 const totalMinutes = Math.floor(safeSeconds / 60) const hours = Math.floor(totalMinutes / 60) const minutes = totalMinutes % 60 return pad2(hours) + '时' + pad2(minutes) + '分钟' } export function formatCountdownHMS(seconds: number): string { const safeSeconds = seconds > 0 ? seconds : 0 const hours = Math.floor(safeSeconds / 3600) const minutes = Math.floor((safeSeconds % 3600) / 60) const remainSeconds = safeSeconds % 60 return pad2(hours) + ':' + pad2(minutes) + ':' + pad2(remainSeconds) }