优化
This commit is contained in:
@@ -40,6 +40,9 @@ public class BusinessFeeSourceEventService {
|
||||
|| envelope.targetRegion() != RegionCode.CN) {
|
||||
throw error("FIN-EVT-001", "跨区费用事件类型、版本或区域无效");
|
||||
}
|
||||
if (!EventPayloadHash.matches(EventPayloadCanonicalizer.canonicalize(envelope.payload()), envelope.payloadHash())) {
|
||||
throw new FinanceEventException("FIN-EVT-002", "事件信封摘要与实际载荷不一致");
|
||||
}
|
||||
BusinessFeeSourceChangedV1 source = payload(envelope);
|
||||
validate(envelope, source);
|
||||
String sourceSystem = source.sourceSystem().trim().toUpperCase();
|
||||
|
||||
@@ -112,6 +112,9 @@ public class WalletCommandEventService {
|
||||
|| envelope.sourceRegion() != RegionCode.OVERSEAS || envelope.targetRegion() != RegionCode.CN) {
|
||||
throw new FinanceEventException("FIN-EVT-001", "资金指令类型、版本或区域无效");
|
||||
}
|
||||
if (!EventPayloadHash.matches(EventPayloadCanonicalizer.canonicalize(envelope.payload()), envelope.payloadHash())) {
|
||||
throw new FinanceEventException("FIN-EVT-002", "事件信封摘要与实际载荷不一致");
|
||||
}
|
||||
try {
|
||||
WalletCommandRequestedV1 value = objectMapper.treeToValue(envelope.payload(), WalletCommandRequestedV1.class);
|
||||
String operation = value.operation() == null ? "" : value.operation().trim().toUpperCase(Locale.ROOT);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.baiyue.finance.service;
|
||||
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.web.EventDeliveryFailure;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.baiyue.common.core.retry.BoundedRetryPolicy;
|
||||
import com.baiyue.finance.client.WalletResultLastlegClient;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -50,21 +52,25 @@ public class WalletResultOutboxRelay {
|
||||
"自动投递次数已耗尽(含租约超时)", eventId, worker);
|
||||
return;
|
||||
}
|
||||
boolean requestStarted = false;
|
||||
try {
|
||||
EventEnvelope envelope = objectMapper.readValue(String.valueOf(row.get("payload_json")), EventEnvelope.class).requireValid();
|
||||
requestStarted = true;
|
||||
var response = client.receive(internalToken, envelope);
|
||||
if (response == null || !response.successful() || response.data() == null)
|
||||
throw new IllegalStateException("Lastleg 未确认资金结果事件");
|
||||
EventDeliveryFailure.requireSuccessful(response);
|
||||
response.data().requireMatches(envelope);
|
||||
jdbc.update("UPDATE fin_wallet_event_outbox SET status='PUBLISHED',published_at=NOW(3),locked_by=NULL,locked_at=NULL,last_error=NULL,updated_at=NOW(3) WHERE event_id=? AND status='SENDING' AND locked_by=?",
|
||||
eventId, worker);
|
||||
} catch (Exception exception) {
|
||||
String error = EventDeliveryFailure.diagnostic(exception, internalToken);
|
||||
boolean permanent = EventDeliveryFailure.isPermanent(exception)
|
||||
|| (!requestStarted && (exception instanceof JsonProcessingException || exception instanceof IllegalArgumentException));
|
||||
int retries = failures + 1;
|
||||
String status = policy.exhausted(retries) ? "DEAD" : "PENDING";
|
||||
LocalDateTime next = policy.exhausted(retries) ? LocalDateTime.now() : LocalDateTime.now().plus(policy.delayAfterFailure(retries));
|
||||
String status = permanent || policy.exhausted(retries) ? "DEAD" : "PENDING";
|
||||
LocalDateTime next = permanent || policy.exhausted(retries) ? LocalDateTime.now() : LocalDateTime.now().plus(policy.delayAfterFailure(retries));
|
||||
jdbc.update("UPDATE fin_wallet_event_outbox SET status=?,retry_count=?,next_retry_at=?,locked_by=NULL,locked_at=NULL,last_error=?,updated_at=NOW(3) WHERE event_id=? AND status='SENDING' AND locked_by=?",
|
||||
status, retries, next, safe(exception.getMessage()), eventId, worker);
|
||||
log.warn("资金结果事件投递失败 eventId={}, reason={}", eventId, exception.getMessage());
|
||||
status, retries, next, safe(error), eventId, worker);
|
||||
log.warn("资金结果事件投递失败 eventId={}, reason={}", eventId, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,18 @@ class WalletCommandEventServiceTest {
|
||||
when(transactions.getTransaction(any())).thenAnswer(invocation -> mock(TransactionStatus.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsTamperedEnvelopeBeforeOpeningWalletTransaction() {
|
||||
EventEnvelope valid = envelope("FREEZE", 1L, BigDecimal.TEN);
|
||||
EventEnvelope tampered = new EventEnvelope(valid.eventId(), valid.eventType(), valid.eventVersion(),
|
||||
valid.occurredAt(), valid.sourceRegion(), valid.targetRegion(), valid.tenantId(), valid.aggregateType(),
|
||||
valid.aggregateId(), valid.aggregateVersion(), valid.handoverVersion(), valid.traceId(),
|
||||
valid.correlationId(), valid.causationId(), valid.payload(), "sha256:" + "0".repeat(64));
|
||||
assertThatThrownBy(() -> service.consume(tampered)).isInstanceOf(FinanceEventException.class)
|
||||
.hasMessageContaining("摘要与实际载荷不一致");
|
||||
verifyNoInteractions(jdbc, wallet);
|
||||
}
|
||||
|
||||
@Test
|
||||
void freezesWalletAndPublishesCompletedEvent() {
|
||||
EventEnvelope envelope = envelope("FREEZE", 31L, new BigDecimal("88.50"));
|
||||
|
||||
Reference in New Issue
Block a user