OrderLifecycleHandler.java
package com.ecommerce.marketing.application.service;
import com.ecommerce.marketing.application.exception.MarketingError;
import com.ecommerce.marketing.application.exception.MarketingException;
import com.ecommerce.marketing.application.model.OrderLifecycleCommand;
import com.ecommerce.marketing.infrastructure.persistence.mapper.ConsumedEventMapper;
import com.ecommerce.marketing.infrastructure.persistence.mapper.PricingLockMapper;
import com.ecommerce.platform.common.idempotency.PayloadFingerprint;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderLifecycleHandler {
public static final String CONSUMER_GROUP = "marketing-order-lifecycle-v1";
private final ConsumedEventMapper consumedEventMapper;
private final PricingLockMapper pricingLockMapper;
private final MarketingService marketingService;
public OrderLifecycleHandler(
ConsumedEventMapper consumedEventMapper,
PricingLockMapper pricingLockMapper,
MarketingService marketingService) {
this.consumedEventMapper = consumedEventMapper;
this.pricingLockMapper = pricingLockMapper;
this.marketingService = marketingService;
}
@Transactional
public void handle(OrderLifecycleCommand command) {
String payloadFingerprint = PayloadFingerprint.of(command);
if (consumedEventMapper.insertIfAbsent(
command.eventId(), CONSUMER_GROUP, payloadFingerprint,
pricingLockMapper.currentTime()) != 1) {
String storedFingerprint = consumedEventMapper.selectPayloadFingerprint(
command.eventId(), CONSUMER_GROUP);
if (!PayloadFingerprint.matches(storedFingerprint, payloadFingerprint)) {
throw new MarketingException(MarketingError.IDEMPOTENCY_CONFLICT);
}
return;
}
switch (command.eventType()) {
case "OrderPaid" -> marketingService.redeemIfPresent(command.orderNo());
case "OrderCanceled", "OrderClosed" -> marketingService.releaseIfPresent(command.orderNo());
default -> throw new IllegalArgumentException("Unsupported order lifecycle event: " + command.eventType());
}
}
}