訂單逾時自動取消功能主要是電商線上服務平台中,用來確定庫存資源能夠自動釋放,而且為了保證使用者體驗的一種優化實作方式。主要解決的問題就是使用者在訂單設定的時間内如果沒有完成支付操作,那麼系統就會将該訂單設定為已取消或者是支付過期的狀态。
訂單逾時邏輯處理細節
首先在訂單建立之初,訂單是處于一個待支付的狀态,如果訂單按照正常流程支付成功之後,那麼訂單的狀态就會被更新為已支付,而如果出現了逾時未支付的情況,那麼訂單狀态就會變為已經取消的狀态。
也就是說在訂單建立的時候需要有一個時間字段來記錄訂單建立的時間,并且還需要設定另一個字段來記錄訂單逾時的時間點。
在訂單建立的時候,所有商品庫存将會被暫時的鎖定,也就是說庫存會進行減扣,但是最終是否真實的減扣還需要等待訂單支付成功,如果訂單支付成功那麼庫存就會正常進行減扣,如果訂單被取消或者是訂單自動取消那麼庫存則會被自動釋放。
如何解決訂單逾時自動取消的問題呢?下面我們就來看看具體的幾種實作方式。
使用定時任務 (Scheduled Task)
在Spring Boot中提供了定時任務操作,開發者可以使用@Scheduled注解來定時執行取消逾時訂單的邏輯。如下所示。
建立一個定時任務類,編寫檢查并取消逾時訂單的邏輯。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class OrderTimeoutTask {
@Autowired
private OrderService orderService;
// 每隔1小時執行一次
@Scheduled(fixedRate = 3600000)
public void cancelTimeoutOrders() {
LocalDateTime now = LocalDateTime.now();
// 假設訂單逾時時間為2小時
LocalDateTime timeoutThreshold = now.minusHours(2);
orderService.cancelOrdersBefore(timeoutThreshold);
}
}
在OrderService中實作取消逾時訂單的方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public void cancelOrdersBefore(LocalDateTime timeoutThreshold) {
List<Order> timeoutOrders = orderRepository.findByStatusAndCreateTimeBefore("PENDING", timeoutThreshold);
for (Order order : timeoutOrders) {
order.setStatus("CANCELLED");
orderRepository.save(order);
}
}
}
這種方式的優勢就在于,實作相對來說比較簡單,直接使用SpringBoot定時任務就可以了,不需要額外的系統部署消耗,但是更具上面的實作我們也可以看到,在一些訂單量較大的情況下,如果按照上面的方式執行定時任務,對于系統資源的消耗本身就比較大,另外上面的這個操作,我們也看到了定時任務執行的時間是固定的,可能會出現延遲操作的問題。
是以這種方式就适合一些中小型的項目,訂單量不是太大,業務邏輯處理不是太複雜的場景中,對于訂單逾時的處理實時性不是太高的場景中使用。
使用消息隊列 (Message Queue)
使用消息隊列可以更為靈活和高效地處理訂單逾時問題,比如使用RabbitMQ、Kafka等。如下所示,通過RabbitMQ來進行訂單的逾時處理。
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 儲存訂單
orderRepository.save(order);
// 發送延時消息
rabbitTemplate.convertAndSend("order.exchange", "order.timeout", order.getId(), message -> {
message.getMessageProperties().setDelay(7200000); // 設定延時2小時
return message;
});
}
}
服務消費者代碼
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderTimeoutConsumer {
@Autowired
private OrderService orderService;
@RabbitListener(queues = "order.timeout.queue")
public void handleOrderTimeout(Long orderId) {
orderService.cancelOrderById(orderId);
}
}
這種方式的優勢就在于靈活性較強,比較适合一些高并發的場景,而且對于資料處理的實時性也控制的很好,訂單逾時之後立即就可以處理,避免了高峰流量對系統的沖擊。
但是缺點也是顯而易見的,就是需要額外的維護消息隊列,這樣無形中就增加了系統的複雜性,這種情況下無法保證系統消息的可靠性,可能由于網絡問題,出現重複處理的問題。
這種方式适合于一些大型的商城應用,畢竟是大型系統,複雜度已經不是問題了,在一些大流量高并發的場景中,通過消息隊列還可以進行流量削峰,同時使用消息隊列還可以使得系統具有更高的可擴充性。
使用資料庫的時間排程器 (Database Scheduler)
如果使用的是MySQL,可以利用MySQL的Event Scheduler來定時執行SQL語句。如下所示。
啟用MySQL Event Scheduler
SET GLOBAL event_scheduler = ON;
編寫SQL事件定時檢查并取消逾時訂單
CREATE EVENT cancel_timeout_orders
ON SCHEDULE EVERY 1 HOUR
DO
UPDATE orders
SET status = 'CANCELLED'
WHERE status = 'PENDING' AND create_time < NOW() - INTERVAL 2 HOUR;
這種方式相當于是資料庫中的定時任務,相對來說實作比較簡單,不需要邏輯層的處理,開發成本較低。
但是由于是資料庫的操作是以可能會導緻資料庫壓力較大,性能有所下降,而對于不同的資料庫性能上的差異也是很大的,在進行資料庫遷移的時候,需要将這些任務進行重寫,浪費時間。
這種操作就是适合一些小型的應用程式,對于資料庫依賴比較強,訂單量比較小,并且資料庫能夠支援這樣的操作用的場景。
總結
- 定時任務 (Scheduled Task):适用于小型和中型應用,訂單量适中且對實時性要求不高。實作簡單,但處理大量訂單時可能導緻資料庫負載問題。
- 消息隊列 (Message Queue):适用于大型、高并發應用,對實時性要求高。具有良好的擴充性和實時性,但需要額外的配置和維護。
- 資料庫時間排程器 (Database Scheduler):适用于小型應用,資料庫支援排程功能且訂單量較小的場景。實作簡單直接,但可擴充性差。
我們可以根據項目的規模、訂單量的大小、實時性的要求等多種場景的考慮來選擇合适的實作方式。