Wallet 是這條事件流的「核定關」。本篇把 Listener 的行為逐段拆解,說清楚 BUY / SELL 的條件與凍結欄位。
CreateOrderListener.onOrderCreate(...):
@RabbitListener(queues = ORDER_CREATE_QUEUE)
public void onOrderCreate(OrderCreateEvent event) {
if (!isWalletEnough(event)) { /*...*/ }
if (!isWalletEnoughForSell(event)) { /*...*/ }
WalletEntity wallet = walletRepository.findByUserId(event.getUserId());
if ("BUY".equals(event.getOrderType())) {
int cost = event.getPrice() * event.getAmount();
wallet.setAvailableCurrency(wallet.getAvailableCurrency() - cost);
wallet.setLockedCurrency(wallet.getLockedCurrency() + cost);
} else if ("SELL".equals(event.getOrderType())) {
int quantity = event.getAmount();
wallet.setAvailableAmount(wallet.getAvailableAmount() - quantity);
wallet.setLockedAmount(wallet.getLockedAmount() + quantity);
}
walletRepository.save(wallet);
// 發出 OrderCreatedEvent
rabbitTemplate.convertAndSend(ORDER_EXCHANGE, ORDER_CREATED_KEY, toCreated(event));
}
isWalletEnough() 與 isWalletEnoughForSell() 會先查使用者錢包並計算是否足夠,避免超賣/超買。
把可用資產轉到鎖定欄位(available → locked)是交易系統的基本保護:
• 撮合前就確保資金/庫存到位
• 後續若撤單,再從 locked 還回可用
核定成功才會送這個事件,代表可以進入撮合。欄位直接沿用原事件,用同一個 exchange、不同 routing key。
Wallet 把「資產可用性」收斂成一個明確訊號(created)。這樣上游不用等、下游不用猜,整條鏈路的責任邊界就很清楚。