diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverNodePunchVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverNodePunchVO.java index 691cb573..d5d10b60 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverNodePunchVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverNodePunchVO.java @@ -52,4 +52,7 @@ public class DriverNodePunchVO implements Serializable { @Schema(description = "本次打卡是否使运单自动完成") private Boolean waybillCompleted; + @Schema(description = "本次打卡后的运单业务状态:running / pending_return_confirm / completed") + private String waybillBusinessStatus; + } diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverWaybillAutoCompleteTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverWaybillAutoCompleteTest.java index f2eeac76..3c1f83fe 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverWaybillAutoCompleteTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverWaybillAutoCompleteTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.MockedStatic; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.secure.utils.AuthUtil; @@ -28,6 +29,7 @@ import org.springblade.transport.service.IWaybillService; import org.springblade.transport.service.ITripQueryService; import org.springblade.transport.support.CertificationGate; +import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -35,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; @@ -42,11 +45,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * 司机打完末位打卡节点后运单自动完成。 + * 司机打完末位打卡节点后运单的去向。 *
- * 命中要四个条件同时成立:本次打卡的是「司机可打卡节点」的末位、该节点是到货/卸货/签收、
- * 回单无需确认完成、运单本身允许完成。任一不成立都只落打卡流水,
- * 响应里的 waybillCompleted 始终取完成接口的返回值,前端据此决定是否提示「运单已完成」。
+ * 末位打卡是到货/卸货/签收、运单本身允许完成时,回单是否需业务员确认决定去向:
+ * 无需确认则本次一并完成运单,需确认则把运单置为待回单确认等业务员动手。
+ * 任一条件不成立都只落打卡流水。
+ * 响应里的 waybillCompleted 始终取完成接口的返回值,前端据此决定是否提示「运单已完成」;
+ * 待回单确认不是已完成,只能靠 waybillBusinessStatus 分辨。
*/
class DriverWaybillAutoCompleteTest {
@@ -72,6 +77,12 @@ class DriverWaybillAutoCompleteTest {
[{"key":"dispatch","name":"发货","type":"cargo","enabled":true,"punch":true}]
""";
+ /** 末位司机打卡节点是「发货」,回单启用且需确认完成 */
+ private static final String DISPATCH_LAST_RETURN_CONFIRM_JSON = """
+ [{"key":"dispatch","name":"发货","type":"cargo","enabled":true,"punch":true},
+ {"key":"return","name":"回单","type":"confirm","enabled":true,"punch":true,"confirmMode":"yes"}]
+ """;
+
private IWaybillService waybillService;
private IDriverService driverService;
private WaybillNodePunchMapper nodePunchMapper;
@@ -118,7 +129,7 @@ class DriverWaybillAutoCompleteTest {
// ------------------------------------------------------------ 命中
@Test
- @DisplayName("末位签收打卡且回单无需确认时调完成接口,响应标记 waybillCompleted")
+ @DisplayName("末位签收打卡且回单无需确认时调完成接口,响应标记 waybillCompleted 与 completed")
void lastDeliveryPunchCompletesWaybill() {
givenWaybill(SIGN_LAST_JSON, "running");
when(waybillService.completeWithoutDeptCheck(WAYBILL_ID)).thenReturn(true);
@@ -127,6 +138,7 @@ class DriverWaybillAutoCompleteTest {
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
assertTrue(vo.getWaybillCompleted(), "完成接口返回成功,响应要告诉前端运单已完成");
+ assertEquals("completed", vo.getWaybillBusinessStatus(), "完成后运单业务状态要如实回报");
}
@Test
@@ -139,6 +151,25 @@ class DriverWaybillAutoCompleteTest {
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
assertFalse(vo.getWaybillCompleted(), "完成接口自己说不算完成时,前端不能提示运单已完成");
+ assertEquals("running", vo.getWaybillBusinessStatus(), "完成没落库,运单仍在进行中");
+ }
+
+ // ------------------------------------------------------------ 待回单确认
+
+ @Test
+ @DisplayName("回单需业务员确认时末位打卡进入待回单确认,落库并回报,且不调完成接口")
+ void returnNeedingConfirmationEntersPendingReturnConfirm() {
+ givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
+
+ DriverNodePunchVO vo = submit("sign");
+
+ verify(waybillService, never()).completeWithoutDeptCheck(any());
+ verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
+ assertFalse(vo.getWaybillCompleted(), "待回单确认不是已完成");
+ assertEquals("pending_return_confirm", vo.getWaybillBusinessStatus(),
+ "客户端要知道打卡已提交、等业务员确认回单");
+ assertEquals("pending_return_confirm", writtenBusinessStatus(),
+ "状态要落库,否则下次查询又回到进行中");
}
// ------------------------------------------------------------ 不命中
@@ -153,18 +184,31 @@ class DriverWaybillAutoCompleteTest {
verify(waybillService, never()).completeWithoutDeptCheck(any());
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
assertFalse(vo.getWaybillCompleted());
+ assertEquals("running", vo.getWaybillBusinessStatus());
}
@Test
- @DisplayName("回单启用且需确认完成时只打卡不完成")
- void returnNeedingConfirmationDoesNotComplete() {
+ @DisplayName("回单需确认但不是末位打卡:运单留在进行中")
+ void nonLastPunchDoesNotEnterReturnConfirmation() {
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
- DriverNodePunchVO vo = submit("sign");
+ DriverNodePunchVO vo = submit("load");
+
+ verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
+ assertEquals("running", vo.getWaybillBusinessStatus(), "后面还有签收要打,不能提前停在待回单确认");
+ assertEquals("running", writtenBusinessStatus());
+ }
+
+ @Test
+ @DisplayName("回单需确认但末位不是到货/卸货/签收:运单留在进行中")
+ void nonDeliveryLastNodeDoesNotEnterReturnConfirmation() {
+ givenWaybill(DISPATCH_LAST_RETURN_CONFIRM_JSON, "running");
+
+ DriverNodePunchVO vo = submit("dispatch");
verify(waybillService, never()).completeWithoutDeptCheck(any());
- verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
- assertFalse(vo.getWaybillCompleted());
+ assertEquals("running", vo.getWaybillBusinessStatus(), "发货不是到货类节点,打完不该停在等待回单");
+ assertEquals("running", writtenBusinessStatus());
}
// ------------------------------------------------------------ 前置:运单本身能否完成
@@ -208,8 +252,50 @@ class DriverWaybillAutoCompleteTest {
assertFalse(vo.getWaybillCompleted());
}
+ @Test
+ @DisplayName("有未了结异常时回单需确认也不进待回单确认——四条进入条件共同成立才进")
+ void unresolvedExceptionBlocksEnteringReturnConfirmation() {
+ givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
+ when(exceptionDisposalMapper.selectList(any())).thenReturn(List.of(unresolvedException("unload")));
+
+ DriverNodePunchVO vo = submit("sign");
+
+ assertEquals("running", vo.getWaybillBusinessStatus(), "异常未了结,不能把运单停在等待回单");
+ assertEquals("running", writtenBusinessStatus());
+ }
+
+ // ------------------------------------------------------------ 并发:只从进行中进入
+
+ @Test
+ @DisplayName("打卡期间运单已被推到终态时不再写待回单确认——只从进行中进入")
+ void concurrentTerminalStatusPreventsEnteringReturnConfirmation() {
+ givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
+ List