暂存提交

This commit is contained in:
2026-08-19 16:24:02 +08:00
parent f5c4bf130a
commit 6acb6178da
354 changed files with 25000 additions and 198 deletions
@@ -0,0 +1,25 @@
package com.baiyue.lastleg.procurement;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class OverseasProcurementInventoryBoundaryTest {
@Test
void procurementRemainsABusinessDocumentAndDoesNotMoveAvailableInventory() throws Exception {
String source = Files.readString(Path.of(
"src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java"),
StandardCharsets.UTF_8);
assertTrue(source.contains("PENDING_REVIEW"));
assertFalse(source.contains("InventoryCommandService"));
assertFalse(source.contains("stockIn("));
assertFalse(source.contains("setAvailablePackages"));
assertFalse(source.contains("availablePackages"));
}
}
@@ -0,0 +1,21 @@
package com.baiyue.lastleg.procurement;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertTrue;
class OverseasProcurementSchemaContractTest {
@Test
void baselineContainsOrderOutboundAndReviewHistoryTables() throws Exception {
String sql;
try (var input = getClass().getResourceAsStream("/db/lastleg.sql")) {
sql = new String(input.readAllBytes(), StandardCharsets.UTF_8);
}
assertTrue(sql.contains("CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order"));
assertTrue(sql.contains("CREATE TABLE IF NOT EXISTS ll_overseas_procurement_outbound"));
assertTrue(sql.contains("CREATE TABLE IF NOT EXISTS ll_overseas_procurement_status_history"));
assertTrue(sql.contains("UNIQUE KEY uk_ll_overseas_procurement_outbound_no(customer_id,order_id,outbound_no)"));
}
}
@@ -0,0 +1,68 @@
package com.baiyue.lastleg.procurement.controller;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.lastleg.procurement.dto.OverseasProcurementRequests;
import com.baiyue.lastleg.procurement.service.OverseasProcurementService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class OverseasProcurementControllerTest {
@Mock OverseasProcurementService service;
private MerchantOverseasProcurementController merchant;
private PlatformOverseasProcurementController platform;
@BeforeEach
void setUp() {
merchant = new MerchantOverseasProcurementController();
platform = new PlatformOverseasProcurementController();
ReflectionTestUtils.setField(merchant, "service", service);
ReflectionTestUtils.setField(platform, "service", service);
}
@Test
void merchantAlwaysUsesTenantFromTrustedIdentity() {
var query = new OverseasProcurementRequests.Query(1, 20, null, null, null, null, null, null, null, null);
merchant.page(user(100L, "MERCHANT", Set.of()), query);
verify(service).page("100", query);
}
@Test
void reviewRequiresExplicitPermission() {
var request = new OverseasProcurementRequests.Review("APPROVE", null);
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> platform.review(user(0L, "PLATFORM", Set.of()), "100", "HC1", request));
assertEquals(403, error.getStatusCode().value());
}
@Test
void merchantEndpointRejectsPlatformIdentity() {
var query = new OverseasProcurementRequests.Query(1, 20, null, null, null, null, null, null, null, null);
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> merchant.page(user(0L, "PLATFORM", Set.of()), query));
assertEquals(403, error.getStatusCode().value());
}
@Test
void platformReviewUsesExplicitTargetTenant() {
var request = new OverseasProcurementRequests.Review("APPROVE", "ok");
platform.review(user(0L, "PLATFORM", Set.of(OverseasProcurementService.REVIEW_PERMISSION)),
"100", "HC1", request);
verify(service).review("100", "HC1", "7", request);
}
private AuthenticatedUser user(long tenantId, String appCode, Set<String> permissions) {
return new AuthenticatedUser(7L, tenantId, tenantId, 8L, "operator", appCode, "WEB", permissions);
}
}
@@ -0,0 +1,103 @@
package com.baiyue.lastleg.procurement.service;
import com.baiyue.lastleg.procurement.dto.OverseasProcurementRequests;
import com.baiyue.lastleg.procurement.entity.OverseasProcurementOrder;
import com.baiyue.lastleg.procurement.mapper.OverseasProcurementOrderMapper;
import com.baiyue.lastleg.procurement.mapper.OverseasProcurementOutboundMapper;
import com.baiyue.lastleg.procurement.mapper.OverseasProcurementStatusHistoryMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class OverseasProcurementServiceTest {
@Mock OverseasProcurementOrderMapper orders;
@Mock OverseasProcurementOutboundMapper outbounds;
@Mock OverseasProcurementStatusHistoryMapper histories;
private OverseasProcurementService service;
@BeforeEach
void setUp() {
service = new OverseasProcurementService();
ReflectionTestUtils.setField(service, "orders", orders);
ReflectionTestUtils.setField(service, "outbounds", outbounds);
ReflectionTestUtils.setField(service, "histories", histories);
ReflectionTestUtils.setField(service, "objectMapper", new ObjectMapper());
}
@Test
void customerOrderAlwaysStartsPendingReviewWithZeroInventory() {
AtomicReference<OverseasProcurementOrder> saved = new AtomicReference<>();
when(orders.insert(any(OverseasProcurementOrder.class))).thenAnswer(invocation -> {
saved.set(invocation.getArgument(0));
return 1;
});
when(orders.findNo(anyString(), anyString())).thenAnswer(ignored -> saved.get());
when(outbounds.listOrder(anyString(), any())).thenReturn(List.of());
when(histories.listOrder(anyString(), any())).thenReturn(List.of());
var detail = service.create("100", "7", new OverseasProcurementRequests.Create(
"Acme", "Office", "Labels", "100x150", "https://example.test/product", 500L,
new BigDecimal("26.50"), "USD", "Ningbo", "Buyer", "13800000000", null, "key-1"));
assertEquals(OverseasProcurementService.PENDING_REVIEW, detail.summary().status());
assertEquals(0, detail.summary().inboundQuantity());
assertEquals(0, detail.summary().inventoryQuantity());
}
@Test
void pendingOrderCannotReceiveInventory() {
OverseasProcurementOrder order = order(OverseasProcurementService.PENDING_REVIEW, 0, 0);
when(orders.lockNo("100", "HC1")).thenReturn(order);
var request = new OverseasProcurementRequests.Update(0L, null, null, null, null,
100L, null, null, null, null, null, null, null, null);
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> service.update("100", "HC1", request));
assertEquals(409, error.getStatusCode().value());
}
@Test
void outboundCannotExceedDerivedInventory() {
OverseasProcurementOrder order = order(OverseasProcurementService.APPROVED, 100, 80);
when(orders.lockNo("100", "HC1")).thenReturn(order);
when(orders.addOutbound(order.getId(), "100", 30)).thenReturn(0);
var request = new OverseasProcurementRequests.AddOutbound(30L, "OUT-1", null, null,
LocalDateTime.now(), null);
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> service.addOutbound("100", "HC1", "7", request));
assertEquals(409, error.getStatusCode().value());
}
private OverseasProcurementOrder order(String status, long inbound, long outbound) {
OverseasProcurementOrder order = new OverseasProcurementOrder();
order.setId(1L);
order.setCustomerId("100");
order.setOrderNo("HC1");
order.setStatus(status);
order.setInboundQuantity(inbound);
order.setOutboundQuantity(outbound);
order.setPurchaseQuantity(500L);
order.setVersion(0L);
order.setCreatedAt(LocalDateTime.now());
order.setUpdatedAt(order.getCreatedAt());
return order;
}
}