✅ 锁定末位打卡回单需确认时进入待回单确认
This commit is contained in:
+3
@@ -52,4 +52,7 @@ public class DriverNodePunchVO implements Serializable {
|
||||
@Schema(description = "本次打卡是否使运单自动完成")
|
||||
private Boolean waybillCompleted;
|
||||
|
||||
@Schema(description = "本次打卡后的运单业务状态:running / pending_return_confirm / completed")
|
||||
private String waybillBusinessStatus;
|
||||
|
||||
}
|
||||
|
||||
+96
-10
@@ -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;
|
||||
|
||||
/**
|
||||
* 司机打完末位打卡节点后运单自动完成。
|
||||
* 司机打完末位打卡节点后运单的去向。
|
||||
* <p>
|
||||
* 命中要四个条件同时成立:本次打卡的是「司机可打卡节点」的末位、该节点是到货/卸货/签收、
|
||||
* 回单无需确认完成、运单本身允许完成。任一不成立都只落打卡流水,
|
||||
* 响应里的 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<String> statusesWritten = new ArrayList<>();
|
||||
// 模拟另一事务在这次打卡推进过程节点时把运单置为已完成(管理端强制完成)
|
||||
when(waybillService.updateById(any())).thenAnswer(invocation -> {
|
||||
Waybill entity = invocation.getArgument(0);
|
||||
statusesWritten.add(entity.getBusinessStatus());
|
||||
entity.setBusinessStatus("completed");
|
||||
return true;
|
||||
});
|
||||
|
||||
DriverNodePunchVO vo = submit("sign");
|
||||
|
||||
assertFalse(statusesWritten.contains("pending_return_confirm"),
|
||||
"运单已被完成,不能再改写回待回单确认");
|
||||
assertEquals("completed", vo.getWaybillBusinessStatus());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 夹具
|
||||
|
||||
/** 打卡流程最后一次落库时运单的业务状态;状态写库走 updateById,各次传的是同一个实体引用 */
|
||||
private String writtenBusinessStatus() {
|
||||
ArgumentCaptor<Waybill> captor = ArgumentCaptor.forClass(Waybill.class);
|
||||
verify(waybillService, atLeastOnce()).updateById(captor.capture());
|
||||
List<Waybill> written = captor.getAllValues();
|
||||
return written.get(written.size() - 1).getBusinessStatus();
|
||||
}
|
||||
|
||||
private DriverNodePunchVO submit(String nodeCode) {
|
||||
NodeSubmitDTO dto = new NodeSubmitDTO();
|
||||
dto.setWaybillId(WAYBILL_ID);
|
||||
|
||||
Reference in New Issue
Block a user