feat(procurement): implement sample fulfillment and settled fee workflow

This commit is contained in:
2026-10-10 02:19:38 +08:00
parent 040f3bbd93
commit a973be2103
104 changed files with 9823 additions and 714 deletions
@@ -0,0 +1,51 @@
package com.baiyue.lastleg.wallet;
import com.baiyue.common.event.EventEnvelope;
import com.baiyue.common.event.WalletCommandRequestedV1;
import com.baiyue.lastleg.entity.LastlegEventOutbox;
import com.baiyue.lastleg.mapper.LastlegEventOutboxMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class WalletCommandOutboxServiceTest {
@Test
void replayRetainsEventIdentityButRejectsDifferentFinancialFacts() throws Exception {
LastlegEventOutboxMapper mapper = mock(LastlegEventOutboxMapper.class);
ObjectMapper json = new ObjectMapper().findAndRegisterModules();
Map<String, LastlegEventOutbox> saved = new HashMap<>();
when(mapper.findByEventKey(anyString())).thenAnswer(i -> saved.get(i.getArgument(0)));
when(mapper.insert(any(LastlegEventOutbox.class))).thenAnswer(i -> {
LastlegEventOutbox row = i.getArgument(0); saved.put(row.getEventKey(), row); return 1;
});
WalletCommandOutboxService service = new WalletCommandOutboxService(mapper, json);
String first = service.publishRefund(9, "PROCUREMENT_FEE", "RF-1", new BigDecimal("20"), "72", "correction", 1);
assertThat(service.publishRefund(9, "PROCUREMENT_FEE", "RF-1", new BigDecimal("20.00"), "72", "correction", 1))
.isEqualTo(first);
assertThatThrownBy(() -> service.publishRefund(9, "PROCUREMENT_FEE", "RF-1", new BigDecimal("21"), "72", "correction", 1))
.isInstanceOf(IllegalStateException.class);
assertThatThrownBy(() -> service.publishRefund(9, "PROCUREMENT_FEE", "RF-1", new BigDecimal("20"), "73", "correction", 1))
.isInstanceOf(IllegalStateException.class);
ArgumentCaptor<LastlegEventOutbox> row = ArgumentCaptor.forClass(LastlegEventOutbox.class);
verify(mapper).insert(row.capture());
EventEnvelope event = json.readValue(row.getValue().getPayloadJson(), EventEnvelope.class);
WalletCommandRequestedV1 command = json.treeToValue(event.payload(), WalletCommandRequestedV1.class);
assertThat(command.originalLedgerId()).isEqualTo("72");
assertThat(command.operation()).isEqualTo("REFUND");
}
@Test
void refundCannotUseUnreferencedGenericCommand() {
var mapper = mock(LastlegEventOutboxMapper.class);
var service = new WalletCommandOutboxService(mapper, new ObjectMapper().findAndRegisterModules());
assertThatThrownBy(() -> service.publish("REFUND", 9, "PROCUREMENT_FEE", "RF-1", BigDecimal.TEN, "reason", 1))
.isInstanceOf(IllegalArgumentException.class);
verifyNoInteractions(mapper);
}
}