172 lines
5.5 KiB
Plaintext
172 lines
5.5 KiB
Plaintext
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)
|
|
} |