为船期节点记录并返回操作人快照
This commit is contained in:
+2
-1
@@ -103,7 +103,8 @@ public final class CarrierScheduleQueryDtos {
|
||||
String providerNodeCode, String standardNode,
|
||||
int nodeSequence, LocalDateTime observedAt, LocalDateTime estimatedAt,
|
||||
LocalDateTime actualAt, String locationCode, String locationName,
|
||||
String sourceReference, String rawPayloadHash, LocalDateTime createdAt) {}
|
||||
String sourceReference, String rawPayloadHash, LocalDateTime createdAt,
|
||||
String operatorName) {}
|
||||
|
||||
public record ContainerNodeDetail(Long containerBookingId, String currentNode,
|
||||
LocalDateTime currentNodeAt, List<NodeFact> nodes) {}
|
||||
|
||||
+2
@@ -30,5 +30,7 @@ public class CarrierScheduleNodeFactEntity {
|
||||
private String locationName;
|
||||
private String sourceReference;
|
||||
private String rawPayloadHash;
|
||||
private Long operatorId;
|
||||
private String operatorName;
|
||||
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt;
|
||||
}
|
||||
|
||||
+3
-2
@@ -11,14 +11,15 @@ import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CarrierScheduleNodeFactMapper extends BaseMapper<CarrierScheduleNodeFactEntity> {
|
||||
/** 保存不可变节点事实及执行该操作的账号快照。 */
|
||||
@Insert("""
|
||||
INSERT INTO fl_carrier_schedule_node_fact
|
||||
(id,query_id,container_booking_id,event_key,provider_code,provider_event_id,provider_node_code,standard_node,node_sequence,observed_at,
|
||||
estimated_at,actual_at,location_code,location_name,source_reference,raw_payload_hash,created_at)
|
||||
estimated_at,actual_at,location_code,location_name,source_reference,raw_payload_hash,operator_id,operator_name,created_at)
|
||||
VALUES
|
||||
(#{value.id},#{value.queryId},#{value.containerBookingId},#{value.eventKey},#{value.providerCode},#{value.providerEventId},#{value.providerNodeCode},
|
||||
#{value.standardNode},#{value.nodeSequence},#{value.observedAt},#{value.estimatedAt},#{value.actualAt},
|
||||
#{value.locationCode},#{value.locationName},#{value.sourceReference},#{value.rawPayloadHash},
|
||||
#{value.locationCode},#{value.locationName},#{value.sourceReference},#{value.rawPayloadHash},#{value.operatorId},#{value.operatorName},
|
||||
CURRENT_TIMESTAMP(3))
|
||||
""")
|
||||
int insertStrict(@Param("value") CarrierScheduleNodeFactEntity value);
|
||||
|
||||
+17
-9
@@ -111,7 +111,7 @@ public class CarrierScheduleQueryService {
|
||||
|
||||
public long customerId(long id) { return required(id).getCustomerId(); }
|
||||
|
||||
/** Idempotently starts the canonical tracking task after domestic outbound confirmation. */
|
||||
/** 国内确认出仓后幂等建立跟踪任务,并保存确认人的账号快照。 */
|
||||
@Transactional
|
||||
public CarrierScheduleQueryEntity ensureFromOutboundConfirm(Long customerId, long oceanBookingId,
|
||||
long containerBookingId,
|
||||
@@ -161,7 +161,7 @@ public class CarrierScheduleQueryService {
|
||||
persistenceStage = "NODE_FACT";
|
||||
CarrierScheduleNodeFactEntity departed = addNode(value, "FIRSTLEG", "OUTBOUND_CONFIRM", "OUTBOUND_CONFIRM",
|
||||
CarrierScheduleNode.DEPARTED_WAREHOUSE, now, null, now, null, null, "OUTBOUND_CONFIRM",
|
||||
sha256("OUTBOUND_CONFIRM|" + containerBookingId));
|
||||
sha256("OUTBOUND_CONFIRM|" + containerBookingId), operatorId, operatorName);
|
||||
if (departed != null) {
|
||||
persistenceStage = "CROSS_REGION_OUTBOX";
|
||||
nodeOutbox.publish(value, departed, requireContainerNo(containerBookingId));
|
||||
@@ -185,6 +185,7 @@ public class CarrierScheduleQueryService {
|
||||
+ ";shippingCompanyName=" + Objects.toString(booking.getShippingCompanyName(), "");
|
||||
}
|
||||
|
||||
/** 保存船司自动采集事实,操作人留空以区别人工补录。 */
|
||||
@Transactional
|
||||
public Detail applyProviderObservation(long id, ProviderObservationRequest request) {
|
||||
CarrierScheduleQueryEntity value = requiredLocked(id);
|
||||
@@ -193,7 +194,7 @@ public class CarrierScheduleQueryService {
|
||||
CarrierScheduleNodeFactEntity fact = addNode(value, request.providerCode(), request.providerEventId(),
|
||||
request.providerNodeCode(), node,
|
||||
request.observedAt(), request.estimatedAt(), request.actualAt(), request.locationCode(),
|
||||
request.locationName(), request.sourceReference(), request.rawPayloadHash());
|
||||
request.locationName(), request.sourceReference(), request.rawPayloadHash(), null, null);
|
||||
if (fact == null) {
|
||||
return detail(value);
|
||||
}
|
||||
@@ -220,6 +221,7 @@ public class CarrierScheduleQueryService {
|
||||
return detail(value);
|
||||
}
|
||||
|
||||
/** 人工补录节点与操作账号一并留存,重放不覆盖原有事实。 */
|
||||
@Transactional
|
||||
public Detail addManualNode(long id, long operatorId, String operatorName, String key,
|
||||
ManualNodeRequest request) {
|
||||
@@ -238,7 +240,7 @@ public class CarrierScheduleQueryService {
|
||||
CarrierScheduleNodeFactEntity fact = addNode(value, "MANUAL", providerEventId, node.name(), node,
|
||||
request.observedAt(),
|
||||
request.estimatedAt(), request.actualAt(), request.locationCode(), request.locationName(),
|
||||
"MANUAL:" + operatorId, hash(operation, id, request));
|
||||
"MANUAL:" + operatorId, hash(operation, id, request), operatorId, operatorName);
|
||||
if (fact == null) {
|
||||
return complete(id, operatorId, key, detail(value));
|
||||
}
|
||||
@@ -261,6 +263,7 @@ public class CarrierScheduleQueryService {
|
||||
return containerNodeDetail(containerBookingId, repository.nodesByContainer(containerBookingId));
|
||||
}
|
||||
|
||||
/** 按货柜补录节点并保存操作账号,未建立查询任务时同样保留署名。 */
|
||||
@Transactional
|
||||
public ContainerNodeDetail addContainerManualNode(long containerBookingId, long operatorId,
|
||||
String operatorName, String key,
|
||||
@@ -280,7 +283,8 @@ public class CarrierScheduleQueryService {
|
||||
CarrierScheduleNodeFactEntity fact = addNode(query == null ? null : query.getId(), containerBookingId,
|
||||
"MANUAL", providerEventId,
|
||||
node.name(), node, request.observedAt(), request.estimatedAt(), request.actualAt(),
|
||||
request.locationCode(), request.locationName(), "MANUAL:" + operatorId, payloadHash);
|
||||
request.locationCode(), request.locationName(), "MANUAL:" + operatorId, payloadHash,
|
||||
operatorId, operatorName);
|
||||
if (fact != null && query != null) {
|
||||
CarrierScheduleQueryEntity before = copy(query);
|
||||
requireManualActualAdvance(query, node, request.actualAt());
|
||||
@@ -546,11 +550,12 @@ public class CarrierScheduleQueryService {
|
||||
repository.histories(value.getId()).stream().map(this::history).toList());
|
||||
}
|
||||
|
||||
/** 返回节点发生时保存的用户名,避免以来源引用代替操作人。 */
|
||||
private NodeFact node(CarrierScheduleNodeFactEntity value) {
|
||||
return new NodeFact(value.getId(), value.getProviderCode(), value.getProviderEventId(), value.getProviderNodeCode(),
|
||||
value.getStandardNode(), value.getNodeSequence(), value.getObservedAt(), value.getEstimatedAt(),
|
||||
value.getActualAt(), value.getLocationCode(), value.getLocationName(), value.getSourceReference(),
|
||||
value.getRawPayloadHash(), value.getCreatedAt());
|
||||
value.getRawPayloadHash(), value.getCreatedAt(), value.getOperatorName());
|
||||
}
|
||||
|
||||
private ExceptionFact exception(CarrierScheduleExceptionFactEntity value) {
|
||||
@@ -565,21 +570,23 @@ public class CarrierScheduleQueryService {
|
||||
value.getReason(), value.getOperatorId(), value.getOperatorName(), value.getCreatedAt());
|
||||
}
|
||||
|
||||
/** 为查询任务追加节点事实并透传人工操作账号。 */
|
||||
private CarrierScheduleNodeFactEntity addNode(CarrierScheduleQueryEntity query, String providerCode, String providerEventId,
|
||||
String providerNodeCode,
|
||||
CarrierScheduleNode node, LocalDateTime observedAt, LocalDateTime estimatedAt,
|
||||
LocalDateTime actualAt, String locationCode, String locationName,
|
||||
String sourceReference, String rawPayloadHash) {
|
||||
String sourceReference, String rawPayloadHash, Long operatorId, String operatorName) {
|
||||
return addNode(query.getId(), query.getContainerBookingId(), providerCode, providerEventId,
|
||||
providerNodeCode, node, observedAt, estimatedAt, actualAt, locationCode, locationName,
|
||||
sourceReference, rawPayloadHash);
|
||||
sourceReference, rawPayloadHash, operatorId, operatorName);
|
||||
}
|
||||
|
||||
/** 节点与操作人快照同次入库,重复事件保留首次记录。 */
|
||||
private CarrierScheduleNodeFactEntity addNode(Long queryId, long containerBookingId,
|
||||
String providerCode, String providerEventId, String providerNodeCode,
|
||||
CarrierScheduleNode node, LocalDateTime observedAt, LocalDateTime estimatedAt,
|
||||
LocalDateTime actualAt, String locationCode, String locationName,
|
||||
String sourceReference, String rawPayloadHash) {
|
||||
String sourceReference, String rawPayloadHash, Long operatorId, String operatorName) {
|
||||
CarrierScheduleNodeFactEntity fact = new CarrierScheduleNodeFactEntity();
|
||||
fact.setId(IdWorker.getId()); fact.setQueryId(queryId); fact.setContainerBookingId(containerBookingId);
|
||||
fact.setEventKey(providerCode.trim().toUpperCase(Locale.ROOT) + ':' + providerEventId.trim());
|
||||
@@ -589,6 +596,7 @@ public class CarrierScheduleQueryService {
|
||||
fact.setNodeSequence(node.sequence()); fact.setObservedAt(observedAt); fact.setEstimatedAt(estimatedAt);
|
||||
fact.setActualAt(actualAt); fact.setLocationCode(trim(locationCode)); fact.setLocationName(trim(locationName));
|
||||
fact.setSourceReference(trim(sourceReference)); fact.setRawPayloadHash(rawPayloadHash);
|
||||
fact.setOperatorId(operatorId); fact.setOperatorName(trim(operatorName));
|
||||
if (repository.insertIfAbsent(fact)) return fact;
|
||||
CarrierScheduleNodeFactEntity existing = repository.nodeByContainer(containerBookingId, fact.getEventKey());
|
||||
if (existing != null && !nodeFactHash(existing).equals(nodeFactHash(fact))) {
|
||||
|
||||
Reference in New Issue
Block a user