修复订单审核后履约任务未生成

This commit is contained in:
2026-08-08 10:17:46 +08:00
parent 71fde99ceb
commit 38e7094520
6 changed files with 254 additions and 1 deletions
@@ -0,0 +1,84 @@
package com.baiyue.firstleg.service;
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
import com.baiyue.firstleg.entity.FulfillmentTaskEntity;
import com.baiyue.firstleg.repository.FulfillmentTaskRepository;
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.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.dao.DuplicateKeyException;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class FulfillmentTaskProvisioningServiceTest {
private static final LocalDateTime NOW = LocalDateTime.of(2026, 8, 8, 10, 0);
@Mock private FulfillmentTaskRepository repository;
private FulfillmentTaskProvisioningService service;
@BeforeEach
void setUp() {
service = new FulfillmentTaskProvisioningService(repository, new ObjectMapper().findAndRegisterModules());
}
@Test
void shouldCreatePendingTaskFromApprovedOrderSnapshot() {
FulfillmentTaskEntity created = service.provision(command(), NOW);
ArgumentCaptor<FulfillmentTaskEntity> captor = ArgumentCaptor.forClass(FulfillmentTaskEntity.class);
verify(repository).insert(captor.capture());
assertThat(created).isSameAs(captor.getValue());
assertThat(created.getTenantId()).isEqualTo(10001L);
assertThat(created.getOrderId()).isEqualTo(77L);
assertThat(created.getOrderNo()).isEqualTo("FL-77");
assertThat(created.getStatus()).isEqualTo("PENDING_ASSIGNMENT");
assertThat(created.getOrderSnapshotJson()).contains("\"tenantId\":10001", "\"orderId\":77", "\"inboundCode\":\"RK-77\"");
assertThat(created.getCreatedAt()).isEqualTo(NOW);
assertThat(created.getDeleted()).isFalse();
assertThat(created.getVersion()).isZero();
}
@Test
void shouldReturnExistingTaskForIdempotentReplay() {
FulfillmentTaskEntity existing = task(700L);
when(repository.findByOrder(10001L, 77L)).thenReturn(existing);
assertThat(service.provision(command(), NOW)).isSameAs(existing);
verify(repository, never()).insert(any());
}
@Test
void shouldConvergeOnConcurrentTaskCreation() {
FulfillmentTaskEntity winner = task(701L);
doThrow(new DuplicateKeyException("concurrent insert")).when(repository).insert(any());
when(repository.findByOrderForUpdate(10001L, 77L)).thenReturn(winner);
assertThat(service.provision(command(), NOW)).isSameAs(winner);
}
private static FulfillmentTaskEntity task(long id) {
FulfillmentTaskEntity task = new FulfillmentTaskEntity();
task.setId(id); task.setTenantId(10001L); task.setOrderId(77L); task.setOrderNo("FL-77");
return task;
}
private static OrderApprovedInboundCommand command() {
return new OrderApprovedInboundCommand(
10001L, 77L, "FL-77", "RK-77", 7L, "测试客户", 9L, "义乌仓",
null, null, null, "FBA", "AIR", "空运", null, null,
"BUY_DECLARATION", "TAX_EXCLUDED", null, null, "FBA", null,
"ONT8", "ONT8", "验收备注", null, null);
}
}
@@ -51,6 +51,18 @@ class FulfillmentTaskServiceTest {
.isEqualTo(ResultCode.NOT_FOUND);
}
@Test
@DisplayName("按客户和订单查询返回审核后生成的履约任务")
void shouldReturnProvisionedTaskByTenantAndOrder() {
FulfillmentTaskEntity task = new FulfillmentTaskEntity();
task.setId(700L); task.setTenantId(10001L); task.setOrderId(77L);
when(repository.findByOrder(10001L, 77L)).thenReturn(task);
assertThat(service.byOrder(10001L, 77L)).isSameAs(task);
verify(repository).findByOrder(10001L, 77L);
}
@Test
@DisplayName("查询分配历史前先校验任务归属租户")
void shouldVerifyTaskTenantBeforeLoadingAssignmentHistory() {
@@ -1,15 +1,20 @@
package com.baiyue.firstleg.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.entity.InboundOrderEntity;
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
import com.baiyue.firstleg.entity.FulfillmentTaskEntity;
import com.baiyue.firstleg.mapper.InboundOrderMapper;
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.ExtendWith;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@@ -26,10 +31,18 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class InboundOrderCreationServiceTest {
@BeforeAll
static void initializeMetadata() {
TableInfoHelper.initTableInfo(
new MapperBuilderAssistant(new MybatisConfiguration(), "test"), InboundOrderEntity.class);
}
@Mock
private InboundOrderMapper orders;
@Mock
private InboundStatusHistoryMapper histories;
@Mock
private FulfillmentTaskProvisioningService fulfillmentTasks;
@InjectMocks
private InboundOrderCreationService service;
@@ -41,7 +54,9 @@ class InboundOrderCreationServiceTest {
existing.setTenantId(10001L);
existing.setOrderId(77L);
existing.setInboundCode("RK-CUSTOM-77");
existing.setTaskId(700L);
when(orders.selectOne(any(Wrapper.class))).thenReturn(null, existing);
when(fulfillmentTasks.provision(any(), any())).thenReturn(task(700L));
long createdId = service.createFromApprovedOrder(command);
long replayedId = service.createFromApprovedOrder(command);
@@ -50,6 +65,7 @@ class InboundOrderCreationServiceTest {
verify(orders, times(1)).insert(inboundCaptor.capture());
verify(histories, times(1)).insert(any(InboundStatusHistoryEntity.class));
assertThat(inboundCaptor.getValue().getInboundCode()).isEqualTo("RK-CUSTOM-77");
assertThat(inboundCaptor.getValue().getTaskId()).isEqualTo(700L);
assertThat(inboundCaptor.getValue().getCarrierId()).isNull();
assertThat(inboundCaptor.getValue().getTaxMode()).isEqualTo("BUY_DECLARATION");
assertThat(inboundCaptor.getValue().getPricingTaxMode()).isEqualTo("TAX_EXCLUDED");
@@ -72,7 +88,9 @@ class InboundOrderCreationServiceTest {
@Test
void shouldConvergeOnConcurrentDuplicateInsertWinner() {
InboundOrderEntity winner = inbound(901L, "RK-CUSTOM-77", "UNPRICED", null, null);
winner.setTaskId(700L);
when(orders.selectOne(any(Wrapper.class))).thenReturn(null, winner);
when(fulfillmentTasks.provision(any(), any())).thenReturn(task(700L));
doThrow(new org.springframework.dao.DuplicateKeyException("concurrent insert"))
.when(orders).insert(any(InboundOrderEntity.class));
@@ -80,6 +98,20 @@ class InboundOrderCreationServiceTest {
verify(histories, never()).insert(any(InboundStatusHistoryEntity.class));
}
@Test
void shouldBackfillTaskLinkForInboundCreatedBeforeTaskProvisioning() {
InboundOrderEntity existing = inbound(900L, "RK-CUSTOM-77", "UNPRICED", null, null);
when(orders.selectOne(any(Wrapper.class))).thenReturn(existing);
when(orders.update(any(), any(Wrapper.class))).thenReturn(1);
when(fulfillmentTasks.provision(any(), any())).thenReturn(task(700L));
assertThat(service.createFromApprovedOrder(approvedCommand("RK-CUSTOM-77"))).isEqualTo(900L);
assertThat(existing.getTaskId()).isEqualTo(700L);
verify(orders).update(any(), any(Wrapper.class));
verify(orders, never()).insert(any(InboundOrderEntity.class));
}
private static InboundOrderEntity inbound(long id, String code, String feeStatus,
Long carrierId, String carrierName) {
InboundOrderEntity inbound = new InboundOrderEntity();
@@ -93,6 +125,15 @@ class InboundOrderCreationServiceTest {
return inbound;
}
private static FulfillmentTaskEntity task(long id) {
FulfillmentTaskEntity task = new FulfillmentTaskEntity();
task.setId(id);
task.setTenantId(10001L);
task.setOrderId(77L);
task.setOrderNo("FL-77");
return task;
}
private static OrderApprovedInboundCommand approvedCommand(String inboundCode) {
return new OrderApprovedInboundCommand(
10001L, 77L, "FL-77", inboundCode, 7L, "测试客户", 9L, "义乌仓",