This commit is contained in:
2026-10-16 16:53:32 +08:00
parent fee0268391
commit 6211d4ae37
18 changed files with 222 additions and 65 deletions
@@ -34,6 +34,20 @@ class MerchantRelabelApplicationControllerTest {
() -> controller.page(user(0L, "PLATFORM"), query));
}
/** 下载必须使用登录客户,且响应禁止缓存。 */
@Test
void documentDownloadUsesAuthenticatedCustomerAndRejectsPlatform() {
org.mockito.Mockito.when(service.downloadFile("100", "RL001", "SKU_TOTAL", null, 99L))
.thenReturn(new com.baiyue.lastleg.relabel.gateway.RelabelDocumentFileGateway.DownloadedFile(
"%PDF-1.7".getBytes(), "SKU标签.pdf", "application/pdf"));
var result = controller.downloadFile(user(100L, "MERCHANT"), "RL001", 99L, "SKU_TOTAL", null);
verify(service).downloadFile("100", "RL001", "SKU_TOTAL", null, 99L);
org.junit.jupiter.api.Assertions.assertEquals("no-store", result.getHeaders().getCacheControl());
org.junit.jupiter.api.Assertions.assertEquals("application/pdf", result.getHeaders().getContentType().toString());
assertThrows(ResponseStatusException.class,
() -> controller.downloadFile(user(0L, "PLATFORM"), "RL001", 99L, "SKU_TOTAL", null));
}
private static AuthenticatedUser user(long tenantId, String appCode) {
return new AuthenticatedUser(1L, tenantId, tenantId, 9L, "merchant", appCode, "WEB", Set.of());
}
@@ -61,6 +61,18 @@ class RelabelApplicationControllerTest {
org.junit.jupiter.api.Assertions.assertEquals("application/pdf", response.getHeaders().getContentType().toString());
}
/** 平台面单下载按申请解析客户,避免任意文件读取。 */
@Test
void shippingLabelDownloadUsesApplicationCustomer() {
org.mockito.Mockito.when(service.customerId("RL-1")).thenReturn("101");
org.mockito.Mockito.when(service.downloadShippingLabel("101", "RL-1", 88L))
.thenReturn(new com.baiyue.lastleg.relabel.gateway.RelabelDocumentFileGateway.DownloadedFile(
"%PDF-1.7".getBytes(), "shipping.pdf", "application/pdf"));
var response = controller.downloadShippingLabel(platform(), "RL-1", 88L);
verify(service).downloadShippingLabel("101", "RL-1", 88L);
org.junit.jupiter.api.Assertions.assertEquals("no-store", response.getHeaders().getCacheControl());
}
private RelabelRequests.Create request() {
return new RelabelRequests.Create("CUSTOMER-1", "WH-1",
LocalDateTime.of(2026, 8, 20, 10, 0), "Li", "13800000000", null, null, "key-1",
@@ -54,7 +54,7 @@ class MerchantWarehouseTransferControllerTest {
/** 补预报只使用登录客户身份,平台身份不可越权操作。 */
@Test
void forecastIncreaseUsesAuthenticatedCustomerAndRejectsPlatform() {
var request = new WarehouseTransferRequests.IncreaseForecastCartons(2,9L,2,3);
var request = new WarehouseTransferRequests.IncreaseForecastCartons(2,3);
controller.increaseForecastCartons(user(100L,"MERCHANT"),"TR001",request);
verify(forecastService).increase("100","TR001","1",request);
assertThrows(ResponseStatusException.class,()->controller.increaseForecastCartons(user(0L,"PLATFORM"),"TR001",request));
@@ -0,0 +1,28 @@
package com.baiyue.lastleg.transfer.controller;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.lastleg.transfer.dto.WarehouseTransferRequests;
import com.baiyue.lastleg.transfer.service.WarehouseTransferForecastService;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class WarehouseTransferForecastControllerTest {
/** 平台补箱明确使用选中客户和操作者,拒绝客户身份调用平台入口。 */
@Test void platformIncreaseRequiresPlatformAndExplicitCustomer() {
var service = mock(WarehouseTransferForecastService.class);
var controller = new WarehouseTransferController();
ReflectionTestUtils.setField(controller, "forecasts", service);
var platform = new AuthenticatedUser(7L, 0L, 0L, 8L, "operator", "PLATFORM", "WEB", Set.of());
var customer = new AuthenticatedUser(8L, 101L, 101L, 9L, "customer", "MERCHANT", "WEB", Set.of());
var request = new WarehouseTransferRequests.IncreaseForecastCartons(4, 6);
controller.increaseForecastCartons(platform, "101", "WT-1", request);
verify(service).increase("101", "WT-1", "7", request);
assertThrows(ResponseStatusException.class, () -> controller.increaseForecastCartons(customer, "101", "WT-1", request));
assertThrows(ResponseStatusException.class, () -> controller.increaseForecastCartons(platform, "", "WT-1", request));
verifyNoMoreInteractions(service);
}
}
@@ -55,6 +55,15 @@ class WarehouseTransferCartonCountTest {
verify(cartons,never()).updateById(any(WarehouseTransferCarton.class));
assertThrows(ResponseStatusException.class,()->service.countCartons("OP1",77L,"WT1",request("K1",input("BOX1","A",13),input("BOX2","B",7))));
}
/** 补充预报箱可无SKU归属,箱内装货按PDA实点记录。 */
@Test void acceptsUnassignedForecastAndDifferentOriginalSku() {
a.setTransferItemId(null);
service.countCartons("OP1",77L,"WT1",request("UNBOUND",input("BOX1","B",3),input("BOX2","A",4)));
assertNotNull(a.getTransferItemId());
assertTrue(a.getSkuItemsJson().contains("B"));
assertTrue(b.getSkuItemsJson().contains("A"));
}
/** 整托汇总后再保留三位,不能把每箱先舍入后累加。 */
@Test void volumeRoundsOnceAfterSummingBoxes() {
var first = new WarehouseTransferRequests.CountCarton("BOX1", null, 200L,
@@ -26,31 +26,31 @@ class WarehouseTransferForecastServiceTest {
order = new WarehouseTransferOrder(); order.setId(1L); order.setCustomerId("C"); order.setTransferNo("WT1"); order.setInboundCode("WT1"); order.setStatus("WAIT_PDA_RECEIVING"); order.setPackageCount(2);
item = new WarehouseTransferItem(); item.setId(2L); item.setPackageCount(2); item.setDeclaredQuantity(10L);
carton = new WarehouseTransferCarton(); carton.setCartonIndex(2); carton.setCartonMark("ORIGINAL"); carton.setStatus("COUNTED");
when(repository.lock("C","WT1")).thenReturn(order); when(repository.items("C",1L)).thenReturn(List.of(item));
when(repository.lock("C","WT1")).thenReturn(order);
}
/** 补箱追加编号,预报件数与原箱号保持不变,相同快照重放不重复增加。 */
@Test void appendPreservesMarksAndQuantityAndReplays() {
when(repository.cartons("C",1L)).thenReturn(List.of(carton));
var request = new WarehouseTransferRequests.IncreaseForecastCartons(2,2L,2,4);
var request = new WarehouseTransferRequests.IncreaseForecastCartons(2,4);
service.increase("C","WT1","OP",request);
var added = org.mockito.ArgumentCaptor.forClass(List.class);
verify(repository).append(eq(order),eq(item),eq(0),added.capture(),any(),eq("OP"));
verify(repository).append(eq(order),added.capture(),any(),eq("OP"));
var rows = (List<WarehouseTransferCarton>) added.getValue();
assertEquals(2,rows.size()); assertEquals(3,rows.get(0).getCartonIndex()); assertEquals(4,rows.get(1).getCartonIndex());
assertNotEquals(rows.get(0).getCartonMark(),rows.get(1).getCartonMark()); assertEquals("ORIGINAL",carton.getCartonMark());
assertEquals(10L,item.getDeclaredQuantity()); assertEquals(4,order.getPackageCount());
assertNull(rows.get(0).getTransferItemId()); assertEquals(2,item.getPackageCount()); assertEquals(10L,item.getDeclaredQuantity()); assertEquals(4,order.getPackageCount());
clearInvocations(repository); service.increase("C","WT1","OP",request);
verify(repository,never()).append(any(),any(),anyInt(),anyList(),any(),anyString());
verify(repository,never()).append(any(),anyList(),any(),anyString());
}
/** 不允许完工后、跨单SKU、旧快照或减少箱数。 */
@Test void rejectsCompletedForeignStaleAndDecreasing() {
order.setStatus("WAIT_CUSTOMER_CONFIRMATION");
assertThrows(ResponseStatusException.class,()->service.increase("C","WT1","OP",new WarehouseTransferRequests.IncreaseForecastCartons(2,2L,2,3)));
assertThrows(ResponseStatusException.class,()->service.increase("C","WT1","OP",new WarehouseTransferRequests.IncreaseForecastCartons(2,3)));
order.setStatus("WAIT_PDA_RECEIVING");
for(var request:List.of(new WarehouseTransferRequests.IncreaseForecastCartons(2,99L,2,3),new WarehouseTransferRequests.IncreaseForecastCartons(1,2L,2,3),new WarehouseTransferRequests.IncreaseForecastCartons(2,2L,2,1)))
for(var request:List.of(new WarehouseTransferRequests.IncreaseForecastCartons(1,3),new WarehouseTransferRequests.IncreaseForecastCartons(2,1)))
assertThrows(ResponseStatusException.class,()->service.increase("C","WT1","OP",request));
verify(repository,never()).append(any(),any(),anyInt(),anyList(),any(),anyString());
verify(repository,never()).append(any(),anyList(),any(),anyString());
}
}
@@ -767,6 +767,29 @@ class WarehouseTransferServiceTest {
verify(orderDetails).insert(any(LastlegOrderDetail.class));
}
/** 已打印草稿再次保存时保留箱唛,并保存SKU预报件数。 */
@Test
void draftUpdatePreservesPrintedCartonMarksAndDeclaredQuantity() throws Exception {
var transfer = persistedTransfer(); transfer.setStatus(WarehouseTransferService.DRAFT);
var tailOrder = historicalOrder(); tailOrder.setId(31L); tailOrder.setCustomerId("TENANT-A"); tailOrder.setCurrentStatus(WarehouseTransferService.DRAFT);
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(orders.selectById(31L)).thenReturn(tailOrder);
var box = new WarehouseTransferCarton(); box.setId(88L); box.setCartonIndex(1); box.setCartonMark("BX-KEEP"); box.setTotalCartons(5); box.setStatus("EXPECTED");
when(transferCartons.listOrder("TENANT-A", transfer.getId())).thenReturn(List.of(box));
var jsonMapper = new ObjectMapper().findAndRegisterModules();
com.fasterxml.jackson.databind.node.ObjectNode requestJson = jsonMapper.valueToTree(request(false));
((com.fasterxml.jackson.databind.node.ObjectNode) requestJson.get("items").get(0)).put("quantity", 120L);
var withQuantity = jsonMapper.treeToValue(requestJson, WarehouseTransferRequests.Create.class);
var result = service.updateDraft("TENANT-A", "WTR-1", "USER-1", withQuantity);
var savedItem = ArgumentCaptor.forClass(WarehouseTransferItem.class);
verify(transferItems).insert(savedItem.capture());
assertEquals(120L, savedItem.getValue().getDeclaredQuantity());
assertEquals("BX-KEEP", box.getCartonMark());
verify(transferCartons).updateById(box);
verify(transferCartons, times(4)).insert(any(WarehouseTransferCarton.class));
}
@Test
void merchantCannotUpdateSubmittedTransfer() {
WarehouseTransferOrder transfer = persistedTransfer();
@@ -790,17 +813,15 @@ class WarehouseTransferServiceTest {
verify(transfers, never()).updateById(any(WarehouseTransferOrder.class));
}
/** 草稿预先打印与后续正式提交使用同一服务端箱唛。 */
@Test
void draftCannotPrintCartonLabelsBeforeSubmission() {
WarehouseTransferOrder transfer = persistedTransfer();
transfer.setStatus(WarehouseTransferService.DRAFT);
void draftCanPrintPersistedCartonLabels() {
WarehouseTransferOrder transfer = persistedTransfer(); transfer.setStatus(WarehouseTransferService.DRAFT);
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> service.cartonLabel("TENANT-A", "WTR-1"));
assertEquals(409, error.getStatusCode().value());
verify(transferCartons, never()).listOrder(anyString(), anyLong());
var carton = new WarehouseTransferCarton(); carton.setCartonMark("BX-PRESERVED"); carton.setCartonIndex(1); carton.setTotalCartons(1); carton.setStatus("EXPECTED");
when(transferCartons.listOrder("TENANT-A", transfer.getId())).thenReturn(List.of(carton));
var result = service.cartonLabel("TENANT-A", "WTR-1");
assertTrue(result.printableHtml().contains("BX-PRESERVED"));
}
/** 提交预报后即可打印全部预报箱唛,不依赖测量或照片完成。 */