尾程暂扣回传
This commit is contained in:
+2
-1
@@ -101,7 +101,8 @@ public class LastlegOrderController {
|
||||
@RequestParam(required = false) String customerId,
|
||||
@PathVariable long id,
|
||||
@Valid @RequestBody LastlegOrderRequests.SetDetained request) {
|
||||
orderService.setDetained(platformScope(user, customerId), id, request, operatorName(user));
|
||||
orderService.setDetained(platformScope(user, customerId), id, request,
|
||||
String.valueOf(user.userId()), operatorName(user));
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/** 至少一次投递海外卸柜完成事件;国内事件桥按 eventId 幂等接收。 */
|
||||
/** 至少一次投递海外到国内业务事件;国内事件桥按 eventId 幂等接收。 */
|
||||
@Component
|
||||
@Slf4j
|
||||
public class InboundUnloadCompletedOutboxRelay {
|
||||
@@ -44,7 +44,7 @@ public class InboundUnloadCompletedOutboxRelay {
|
||||
} catch (Exception exception) {
|
||||
repository.retry(row.getId(), workerId, LocalDateTime.now().plusSeconds(Math.max(retrySeconds, 1)),
|
||||
exception.getMessage());
|
||||
log.warn("海外卸柜完成事件投递失败: eventKey={}, reason={}", row.getEventKey(), exception.getMessage());
|
||||
log.warn("海外到国内事件投递失败: eventKey={}, reason={}", row.getEventKey(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class InboundUnloadCompletedOutboxRelay {
|
||||
com.baiyue.common.core.api.ApiResponse<EventBridgeClient.Acknowledgement> response) {
|
||||
if (response == null) throw new IllegalStateException("国内事件桥返回空响应");
|
||||
if (!response.successful()) {
|
||||
throw new IllegalStateException("国内事件桥拒绝海外卸柜完成事件: code=" + response.code()
|
||||
throw new IllegalStateException("国内事件桥拒绝海外事件: code=" + response.code()
|
||||
+ ", message=" + response.message() + ", requestId=" + response.requestId());
|
||||
}
|
||||
if (response.data() == null) throw new IllegalStateException("国内事件桥成功响应缺少确认数据");
|
||||
|
||||
+7
-4
@@ -13,11 +13,14 @@ import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class UnloadCompletedOutboxRepository {
|
||||
private static final List<String> OVERSEAS_TO_CN_TYPES = List.of(
|
||||
EventTypes.OVERSEAS_CONTAINER_UNLOADED,
|
||||
EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED);
|
||||
@Resource private LastlegEventOutboxMapper mapper;
|
||||
|
||||
public List<LastlegEventOutbox> pending(int limit, LocalDateTime now, LocalDateTime expiredLeaseBefore) {
|
||||
return mapper.selectList(new LambdaQueryWrapper<LastlegEventOutbox>()
|
||||
.eq(LastlegEventOutbox::getEventType, EventTypes.OVERSEAS_CONTAINER_UNLOADED)
|
||||
.in(LastlegEventOutbox::getEventType, OVERSEAS_TO_CN_TYPES)
|
||||
.and(status -> status.eq(LastlegEventOutbox::getStatus, "PENDING")
|
||||
.and(retry -> retry.isNull(LastlegEventOutbox::getNextRetryAt)
|
||||
.or().le(LastlegEventOutbox::getNextRetryAt, now))
|
||||
@@ -30,7 +33,7 @@ public class UnloadCompletedOutboxRepository {
|
||||
public boolean claim(long id, String worker, LocalDateTime now, LocalDateTime expiredLeaseBefore) {
|
||||
return mapper.update(null, new LambdaUpdateWrapper<LastlegEventOutbox>()
|
||||
.eq(LastlegEventOutbox::getId, id)
|
||||
.eq(LastlegEventOutbox::getEventType, EventTypes.OVERSEAS_CONTAINER_UNLOADED)
|
||||
.in(LastlegEventOutbox::getEventType, OVERSEAS_TO_CN_TYPES)
|
||||
.and(status -> status.eq(LastlegEventOutbox::getStatus, "PENDING")
|
||||
.and(retry -> retry.isNull(LastlegEventOutbox::getNextRetryAt)
|
||||
.or().le(LastlegEventOutbox::getNextRetryAt, now))
|
||||
@@ -44,7 +47,7 @@ public class UnloadCompletedOutboxRepository {
|
||||
public LastlegEventOutbox claimed(long id, String worker) {
|
||||
return mapper.selectOne(new LambdaQueryWrapper<LastlegEventOutbox>()
|
||||
.eq(LastlegEventOutbox::getId, id)
|
||||
.eq(LastlegEventOutbox::getEventType, EventTypes.OVERSEAS_CONTAINER_UNLOADED)
|
||||
.in(LastlegEventOutbox::getEventType, OVERSEAS_TO_CN_TYPES)
|
||||
.eq(LastlegEventOutbox::getStatus, "PROCESSING")
|
||||
.eq(LastlegEventOutbox::getLockedBy, worker));
|
||||
}
|
||||
@@ -72,7 +75,7 @@ public class UnloadCompletedOutboxRepository {
|
||||
private LambdaUpdateWrapper<LastlegEventOutbox> claimedBase(long id, String worker) {
|
||||
return new LambdaUpdateWrapper<LastlegEventOutbox>()
|
||||
.eq(LastlegEventOutbox::getId, id)
|
||||
.eq(LastlegEventOutbox::getEventType, EventTypes.OVERSEAS_CONTAINER_UNLOADED)
|
||||
.in(LastlegEventOutbox::getEventType, OVERSEAS_TO_CN_TYPES)
|
||||
.eq(LastlegEventOutbox::getStatus, "PROCESSING")
|
||||
.eq(LastlegEventOutbox::getLockedBy, worker);
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.EventIdentifiers;
|
||||
import com.baiyue.common.event.EventPayloadCanonicalizer;
|
||||
import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.OverseasInboundOrderDetainedChangedV1;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.lastleg.entity.LastlegEventOutbox;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.mapper.LastlegEventOutboxMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
/** 在尾程业务事务内冻结按来源入仓单回传国内的暂扣状态事件。 */
|
||||
@Service
|
||||
public class InboundOrderDetainedOutboxService {
|
||||
@Resource private LastlegEventOutboxMapper mapper;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
public void publish(LastlegOrder order, boolean detained, String reason, String operatorId,
|
||||
String operatorName, Instant changedAt, long changeVersion) {
|
||||
requireText(order.getSourceInboundId(), "sourceInboundId");
|
||||
requireText(order.getInboundCode(), "inboundCode");
|
||||
requireText(order.getCustomerId(), "customerId");
|
||||
var payload = new OverseasInboundOrderDetainedChangedV1(order.getSourceInboundId(),
|
||||
order.getInboundCode(), String.valueOf(order.getId()), order.getOrderNo(), detained,
|
||||
reason, operatorId, operatorName, changedAt);
|
||||
JsonNode payloadNode = objectMapper.valueToTree(payload);
|
||||
String eventKey = EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED + ':'
|
||||
+ order.getSourceInboundId() + ':' + changeVersion;
|
||||
EventEnvelope envelope = new EventEnvelope(EventIdentifiers.newUuid(),
|
||||
EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED, 1, changedAt,
|
||||
RegionCode.OVERSEAS, RegionCode.CN, order.getCustomerId(), "INBOUND_ORDER",
|
||||
order.getSourceInboundId(), changeVersion, 1L, EventIdentifiers.newUuid(),
|
||||
eventKey, order.getSourceEventId(), payloadNode,
|
||||
EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payloadNode))).requireValid();
|
||||
|
||||
LastlegEventOutbox event = new LastlegEventOutbox();
|
||||
event.setId(IdWorker.getId());
|
||||
event.setCustomerId(order.getCustomerId());
|
||||
event.setAggregateType(envelope.aggregateType());
|
||||
event.setAggregateId(envelope.aggregateId());
|
||||
event.setEventType(envelope.eventType());
|
||||
event.setEventVersion(envelope.eventVersion());
|
||||
event.setEventKey(eventKey);
|
||||
event.setPayloadJson(json(envelope));
|
||||
event.setStatus("PENDING");
|
||||
event.setRetryCount(0);
|
||||
event.setCreatedAt(LocalDateTime.ofInstant(changedAt, ZoneOffset.UTC));
|
||||
mapper.insert(event);
|
||||
}
|
||||
|
||||
private static void requireText(String value, String field) {
|
||||
if (value == null || value.isBlank()) throw new IllegalStateException(field + " is required");
|
||||
}
|
||||
|
||||
private String json(Object value) {
|
||||
try { return objectMapper.writeValueAsString(value); }
|
||||
catch (JsonProcessingException exception) {
|
||||
throw new IllegalStateException("Cannot serialize inbound detained event", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -54,6 +55,7 @@ public class LastlegOrderService {
|
||||
@Resource private LastlegOrderOperationLogMapper operationLogMapper;
|
||||
@Resource private InboundPlanCartonMapper cartonMapper;
|
||||
@Resource private InventoryCommandService inventory;
|
||||
@Resource private InboundOrderDetainedOutboxService detainedOutbox;
|
||||
|
||||
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query) {
|
||||
Page<LastlegOrder> page = orderMapper.selectPage(
|
||||
@@ -123,6 +125,12 @@ public class LastlegOrderService {
|
||||
@Transactional
|
||||
public void setDetained(String customerId, long orderId, LastlegOrderRequests.SetDetained request,
|
||||
String operatorName) {
|
||||
setDetained(customerId, orderId, request, null, operatorName);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void setDetained(String customerId, long orderId, LastlegOrderRequests.SetDetained request,
|
||||
String operatorId, String operatorName) {
|
||||
LastlegOrder current = findOwned(customerId, orderId);
|
||||
LastlegOrder order = orderMapper.lockById(current.getCustomerId(), orderId);
|
||||
if (order == null) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Tail-leg order not found");
|
||||
@@ -149,6 +157,10 @@ public class LastlegOrderService {
|
||||
operation.setResult(request.remark().trim());
|
||||
operation.setCreatedAt(now);
|
||||
operationLogMapper.insert(operation);
|
||||
if (StringUtils.hasText(order.getSourceInboundId())) {
|
||||
detainedOutbox.publish(order, detained, request.remark().trim(), operatorId, operatorName,
|
||||
now.toInstant(ZoneOffset.UTC), operation.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
||||
Reference in New Issue
Block a user