feat: close overseas wallet and pallet cost flow
This commit is contained in:
@@ -43,8 +43,8 @@ class FinanceSqlCommentContractTest {
|
||||
missing.add(currentTable + "." + column.group(1));
|
||||
}
|
||||
}
|
||||
assertThat(tableCount).isEqualTo(21);
|
||||
assertThat(columnCount).isEqualTo(330);
|
||||
assertThat(tableCount).isEqualTo(23);
|
||||
assertThat(columnCount).isEqualTo(350);
|
||||
assertThat(missing).as("缺少中文业务注释的Finance字段").isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.baiyue.finance;
|
||||
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.EventPayloadCanonicalizer;
|
||||
import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.common.event.WalletCommandRequestedV1;
|
||||
import com.baiyue.finance.dto.FinanceWalletDtos.WalletHold;
|
||||
import com.baiyue.finance.service.FinanceEventException;
|
||||
import com.baiyue.finance.service.FinanceWalletService;
|
||||
import com.baiyue.finance.service.WalletCommandEventService;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class WalletCommandEventServiceTest {
|
||||
private final JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
private final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
|
||||
private final FinanceWalletService wallet = mock(FinanceWalletService.class);
|
||||
private final TransactionTemplate transactions = mock(TransactionTemplate.class);
|
||||
private final WalletCommandEventService service = new WalletCommandEventService(jdbc, mapper, wallet, transactions);
|
||||
|
||||
@BeforeEach
|
||||
void executeTransactionCallbacks() {
|
||||
when(transactions.execute(any())).thenAnswer(invocation -> {
|
||||
TransactionCallback<?> callback = invocation.getArgument(0);
|
||||
return callback.doInTransaction(mock(TransactionStatus.class));
|
||||
});
|
||||
doAnswer(invocation -> {
|
||||
Consumer<TransactionStatus> callback = invocation.getArgument(0);
|
||||
callback.accept(mock(TransactionStatus.class));
|
||||
return null;
|
||||
}).when(transactions).executeWithoutResult(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void freezesWalletAndPublishesCompletedEvent() {
|
||||
EventEnvelope envelope = envelope("FREEZE", 31L, new BigDecimal("88.50"));
|
||||
when(jdbc.queryForList(startsWith("SELECT payload_hash"), eq(String.class), eq(envelope.eventId())))
|
||||
.thenReturn(List.of());
|
||||
when(jdbc.queryForObject(startsWith("SELECT COUNT(*)"), eq(Integer.class), anyString())).thenReturn(0);
|
||||
when(wallet.freeze(eq(31L), eq("freeze-procurement-1"), any())).thenReturn(
|
||||
new WalletHold(9L, 31L, "OVERSEAS_PROCUREMENT_FEE", "PO-1", new BigDecimal("88.50"),
|
||||
new BigDecimal("80.00"), new BigDecimal("8.50"), "FROZEN", 71L, null,
|
||||
null, null, null));
|
||||
|
||||
assertThat(service.consume(envelope)).isEqualTo(envelope.eventId());
|
||||
|
||||
verify(wallet).freeze(eq(31L), eq("freeze-procurement-1"), argThat(request ->
|
||||
request.amount().compareTo(new BigDecimal("88.50")) == 0
|
||||
&& request.businessNo().equals("PO-1")));
|
||||
verify(jdbc).update(startsWith("INSERT IGNORE INTO fin_wallet_event_outbox"), any(Object[].class));
|
||||
verify(jdbc).update(startsWith("UPDATE fin_wallet_command_inbox SET status="),
|
||||
eq("SUCCEEDED"), contains("\"holdStatus\":\"FROZEN\""), eq(envelope.eventId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsCustomerMismatchBeforeTouchingWallet() {
|
||||
EventEnvelope envelope = envelope("FREEZE", 31L, new BigDecimal("10.00"));
|
||||
envelope = new EventEnvelope(envelope.eventId(), envelope.eventType(), envelope.eventVersion(),
|
||||
envelope.occurredAt(), envelope.sourceRegion(), envelope.targetRegion(), "32", envelope.aggregateType(),
|
||||
envelope.aggregateId(), envelope.aggregateVersion(), envelope.handoverVersion(), envelope.traceId(),
|
||||
envelope.correlationId(), envelope.causationId(), envelope.payload(), envelope.payloadHash());
|
||||
|
||||
EventEnvelope mismatched = envelope;
|
||||
assertThatThrownBy(() -> service.consume(mismatched))
|
||||
.isInstanceOf(FinanceEventException.class)
|
||||
.extracting(error -> ((FinanceEventException) error).code()).isEqualTo("FIN-EVT-007");
|
||||
verifyNoInteractions(wallet);
|
||||
}
|
||||
|
||||
private EventEnvelope envelope(String operation, long customerId, BigDecimal amount) {
|
||||
WalletCommandRequestedV1 command = new WalletCommandRequestedV1(operation, customerId,
|
||||
"OVERSEAS_PROCUREMENT_FEE", "PO-1", amount, "采购快递费",
|
||||
"freeze-procurement-1", Instant.parse("2026-09-05T01:00:00Z"));
|
||||
JsonNode payload = mapper.valueToTree(command);
|
||||
return new EventEnvelope(UUID.randomUUID().toString(), EventTypes.WALLET_COMMAND_REQUESTED,
|
||||
WalletCommandRequestedV1.VERSION, Instant.parse("2026-09-05T01:00:00Z"),
|
||||
RegionCode.OVERSEAS, RegionCode.CN, String.valueOf(customerId), "WALLET_HOLD", "PO-1", 1,
|
||||
null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), null, payload,
|
||||
EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))).requireValid();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user