✨ 节点异常上报增加打卡与重复上报校验
This commit is contained in:
+7
@@ -108,4 +108,11 @@ public interface IDriverWaybillService {
|
||||
*/
|
||||
Map<String, Object> findPunchNode(Long waybillId, String nodeCode);
|
||||
|
||||
/**
|
||||
* 该运单的指定节点当前是否存在打卡流水;节点编码忽略大小写。
|
||||
* <p>
|
||||
* 以打卡流水为准而非运单当前节点:管理端取消打卡后这里要立刻回到未打卡。
|
||||
*/
|
||||
boolean isNodePunched(Long waybillId, String nodeCode);
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -805,6 +805,20 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
||||
return findPunchNodeConfig(resolveProcessJson(waybill), nodeCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNodePunched(Long waybillId, String nodeCode) {
|
||||
if (waybillId == null || Func.isEmpty(nodeCode)) {
|
||||
return false;
|
||||
}
|
||||
// 不复用 latestNodePunchMap:它的 key 被 toLowerCase 过,与过程配置 key 比不出来
|
||||
String key = nodeCode.trim();
|
||||
return nodePunchMapper.selectList(Wrappers.<WaybillNodePunch>lambdaQuery()
|
||||
.eq(WaybillNodePunch::getWaybillId, waybillId))
|
||||
.stream()
|
||||
.map(punch -> Func.toStr(punch.getNodeCode(), "").trim())
|
||||
.anyMatch(key::equalsIgnoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* 该运单「未处理完成的异常」所属节点 key(未达终态:已完成 / 已撤销)。
|
||||
* <p>
|
||||
|
||||
+29
-1
@@ -147,7 +147,19 @@ public class ExceptionDisposalServiceImpl
|
||||
if (nodeCfg == null) {
|
||||
throw new ServiceException("异常节点不存在");
|
||||
}
|
||||
disposal.setNodeCode(WaybillProcessSupport.nodeKey(nodeCfg));
|
||||
String nodeKey = WaybillProcessSupport.nodeKey(nodeCfg);
|
||||
// 上报门禁之一:已打卡说明这个环节已经过去,事后才发现的问题走整单异常。
|
||||
// 在途例外——它可重复打卡、没有完成含义,打卡与否不说明环节是否已经过去。
|
||||
if (!WaybillProcessSupport.isTransitNodePublic(nodeCfg)
|
||||
&& driverWaybillService.isNodePunched(disposal.getWaybillId(), nodeKey)) {
|
||||
throw new ServiceException("该节点已打卡,不能上报异常");
|
||||
}
|
||||
// 上报门禁之二:同一节点同时至多一条未了结异常,处理中也占位。
|
||||
// 约束是同时性的:异常了结后该节点重新可以上报。
|
||||
if (hasUnresolvedNodeException(disposal.getWaybillId(), nodeKey)) {
|
||||
throw new ServiceException("该节点已有未处理异常,处理完成后才能再次上报");
|
||||
}
|
||||
disposal.setNodeCode(nodeKey);
|
||||
disposal.setNodeName(WaybillProcessSupport.nodeName(nodeCfg));
|
||||
}
|
||||
|
||||
@@ -157,6 +169,22 @@ public class ExceptionDisposalServiceImpl
|
||||
return toVO(disposal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 该运单的指定节点是否已有未了结(未达终态)的异常。
|
||||
* <p>
|
||||
* 未了结的判据与打卡门禁同一份:已完成、已撤销之外的状态都算,
|
||||
* 「处理中」同样占位——它只是有人跟进过,问题可能仍在。
|
||||
* 节点编码忽略大小写:历史数据里的写法不一定与过程配置的 key 完全一致。
|
||||
*/
|
||||
private boolean hasUnresolvedNodeException(Long waybillId, String nodeCode) {
|
||||
List<ExceptionDisposal> existing = baseMapper.selectList(Wrappers.<ExceptionDisposal>lambdaQuery()
|
||||
.eq(ExceptionDisposal::getWaybillId, waybillId)
|
||||
.eq(ExceptionDisposal::getIsDeleted, 0));
|
||||
return existing.stream().anyMatch(item ->
|
||||
nodeCode.equalsIgnoreCase(Func.toStr(item.getNodeCode(), "").trim())
|
||||
&& !WaybillProcessSupport.isSettledDisposal(item.getDisposalStatus()));
|
||||
}
|
||||
|
||||
/** 按运单补齐运单号 / 车牌 / 项目 / 承运商等展示字段 */
|
||||
private void fillFromWaybill(ExceptionDisposal disposal, Long waybillId, String waybillNo) {
|
||||
Waybill waybill = null;
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
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.springblade.transport.mapper.ExceptionDisposalMapper;
|
||||
import org.springblade.transport.mapper.WaybillEnroutePunchMapper;
|
||||
import org.springblade.transport.mapper.WaybillNodePunchMapper;
|
||||
import org.springblade.transport.pojo.entity.WaybillNodePunch;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.service.IProcessConfigService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.CertificationGate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 节点是否已打卡的判定。
|
||||
* <p>
|
||||
* 它读的是打卡流水表,而不是运单的当前节点:管理端取消打卡会把流水逻辑删除,
|
||||
* 那时这里必须立刻回到未打卡,否则已取消的打卡仍会挡住异常上报。
|
||||
* 节点编码按忽略大小写比对——流水里的写法与过程配置的 key 不保证一致。
|
||||
*/
|
||||
class DriverWaybillNodePunchTest {
|
||||
|
||||
private static final Long WAYBILL_ID = 5201L;
|
||||
|
||||
private WaybillNodePunchMapper nodePunchMapper;
|
||||
private DriverWaybillServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||
assistant.setCurrentNamespace("test");
|
||||
TableInfoHelper.initTableInfo(assistant, WaybillNodePunch.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
nodePunchMapper = mock(WaybillNodePunchMapper.class);
|
||||
service = new DriverWaybillServiceImpl(
|
||||
mock(IWaybillService.class),
|
||||
mock(IDriverService.class),
|
||||
mock(IProcessConfigService.class),
|
||||
mock(ITransportVehicleService.class),
|
||||
mock(CertificationGate.class),
|
||||
mock(WaybillEnroutePunchMapper.class),
|
||||
nodePunchMapper,
|
||||
mock(ExceptionDisposalMapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("没有该节点的打卡流水就是未打卡")
|
||||
void withoutPunchRecordNodeIsNotPunched() {
|
||||
when(nodePunchMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
assertFalse(service.isNodePunched(WAYBILL_ID, "load"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("有该节点的打卡流水即已打卡")
|
||||
void punchRecordMakesNodePunched() {
|
||||
when(nodePunchMapper.selectList(any())).thenReturn(List.of(punch("load")));
|
||||
|
||||
assertTrue(service.isNodePunched(WAYBILL_ID, "load"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("别的节点的打卡流水不算本节点已打卡")
|
||||
void otherNodePunchDoesNotCount() {
|
||||
when(nodePunchMapper.selectList(any())).thenReturn(List.of(punch("arrive_scene")));
|
||||
|
||||
assertFalse(service.isNodePunched(WAYBILL_ID, "load"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("流水里的节点编码大小写与查询不一致时仍算同一节点")
|
||||
void nodeCodeComparisonIgnoresCase() {
|
||||
when(nodePunchMapper.selectList(any())).thenReturn(List.of(punch("LOAD")));
|
||||
|
||||
assertTrue(service.isNodePunched(WAYBILL_ID, "load"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("运单或节点缺失时判为未打卡,不抛异常")
|
||||
void missingArgumentsMeanNotPunched() {
|
||||
assertFalse(service.isNodePunched(null, "load"));
|
||||
assertFalse(service.isNodePunched(WAYBILL_ID, " "));
|
||||
}
|
||||
|
||||
private WaybillNodePunch punch(String nodeCode) {
|
||||
WaybillNodePunch punch = new WaybillNodePunch();
|
||||
punch.setId(6201L);
|
||||
punch.setWaybillId(WAYBILL_ID);
|
||||
punch.setNodeCode(nodeCode);
|
||||
return punch;
|
||||
}
|
||||
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
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;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.mapper.ExceptionDisposalFollowRecordMapper;
|
||||
import org.springblade.transport.mapper.ExceptionDisposalMapper;
|
||||
import org.springblade.transport.pojo.entity.ExceptionDisposal;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.service.IDriverWaybillService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
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.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 异常上报门禁:已打卡的节点、已有未了结异常的节点都不能再上报。
|
||||
* <p>
|
||||
* 这里的口径决定「这个节点还能不能再报一条异常」,与打卡门禁是两回事:
|
||||
* 一个管还能不能报,一个管还能不能打卡。两者共用同一份未了结判定,
|
||||
* 但解除条件互不相同,所以断言分开下。
|
||||
*/
|
||||
class ExceptionDisposalReportGateTest {
|
||||
|
||||
private static final Long WAYBILL_ID = 5101L;
|
||||
private static final Long USER_ID = 7001L;
|
||||
|
||||
private ExceptionDisposalMapper disposalMapper;
|
||||
private IWaybillService waybillService;
|
||||
private IDriverWaybillService driverWaybillService;
|
||||
private ExceptionDisposalServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
// 构造 LambdaQueryWrapper 需要 MyBatis-Plus 的实体元数据缓存
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||
assistant.setCurrentNamespace("test");
|
||||
TableInfoHelper.initTableInfo(assistant, ExceptionDisposal.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
disposalMapper = mock(ExceptionDisposalMapper.class);
|
||||
waybillService = mock(IWaybillService.class);
|
||||
driverWaybillService = mock(IDriverWaybillService.class);
|
||||
service = new ExceptionDisposalServiceImpl(
|
||||
mock(ExceptionDisposalFollowRecordMapper.class), waybillService, driverWaybillService);
|
||||
inject("baseMapper", disposalMapper);
|
||||
|
||||
when(waybillService.getById(WAYBILL_ID)).thenReturn(waybill());
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of());
|
||||
when(disposalMapper.insert(any(ExceptionDisposal.class))).thenReturn(1);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 已打卡
|
||||
|
||||
@Test
|
||||
@DisplayName("已打卡的普通节点不能再上报异常")
|
||||
void punchedNodeRejectsReport() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(true);
|
||||
|
||||
ServiceException exception = submitExpectRejected("load");
|
||||
|
||||
assertEquals("该节点已打卡,不能上报异常", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("在途已打卡仍可上报:它可重复打卡、没有完成含义,打卡与否不说明环节已经过去")
|
||||
void punchedTransitNodeStillAcceptsReport() {
|
||||
stubNode("transit", "在途", "transit");
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "transit")).thenReturn(true);
|
||||
|
||||
submitOk("transit");
|
||||
|
||||
assertEquals("transit", savedDisposal().getNodeCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("未打卡的普通节点照常可上报")
|
||||
void unpunchedNodeAcceptsReport() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(false);
|
||||
|
||||
submitOk("load");
|
||||
|
||||
assertEquals("load", savedDisposal().getNodeCode());
|
||||
assertEquals("装货", savedDisposal().getNodeName());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 已有未了结异常
|
||||
|
||||
@Test
|
||||
@DisplayName("节点已有待处理异常时拒绝再报")
|
||||
void pendingNodeExceptionRejectsReport() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
stubUnresolvedNodeException("load", "pending");
|
||||
|
||||
ServiceException exception = submitExpectRejected("load");
|
||||
|
||||
assertEquals("该节点已有未处理异常,处理完成后才能再次上报", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("节点异常已有人跟进(处理中)时同样拒绝:处理中不解除任何门禁")
|
||||
void processingNodeExceptionRejectsReport() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
stubUnresolvedNodeException("load", "processing");
|
||||
|
||||
ServiceException exception = submitExpectRejected("load");
|
||||
|
||||
assertEquals("该节点已有未处理异常,处理完成后才能再次上报", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("该节点的异常已完成、或被撤销后,节点重新可以上报")
|
||||
void settledNodeExceptionAllowsReportAgain() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of(
|
||||
nodeException("load", "completed"),
|
||||
nodeException("load", "revoked")));
|
||||
|
||||
submitOk("load");
|
||||
|
||||
assertEquals("load", savedDisposal().getNodeCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("别的节点的未了结异常不占用本节点:约束只落在异常自己归属的节点上")
|
||||
void otherNodesExceptionDoesNotBlockReport() {
|
||||
stubNode("unload", "卸货", "cargo");
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of(nodeException("load", "pending")));
|
||||
|
||||
submitOk("unload");
|
||||
|
||||
assertEquals("unload", savedDisposal().getNodeCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("节点编码大小写不敏感:库里存 LOAD、过程配置叫 load,仍算同一节点")
|
||||
void nodeCodeComparisonIgnoresCase() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of(nodeException("LOAD", "pending")));
|
||||
|
||||
ServiceException exception = submitExpectRejected("load");
|
||||
|
||||
assertEquals("该节点已有未处理异常,处理完成后才能再次上报", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("门禁拿过程配置里的节点 key 去比对,而不是请求体里那串写法")
|
||||
void gateUsesNodeKeyResolvedFromProcessConfig() {
|
||||
// 请求体写的是大写,过程配置里的 key 是小写;打卡流水按配置的 key 落库
|
||||
when(driverWaybillService.findPunchNode(WAYBILL_ID, "LOAD")).thenReturn(node("load", "装货", "cargo"));
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(true);
|
||||
|
||||
ServiceException exception = submitExpectRejected("LOAD");
|
||||
|
||||
assertEquals("该节点已打卡,不能上报异常", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("未了结异常的查询按运单收敛,并排除已删除的行")
|
||||
void unresolvedQueryIsScopedToWaybillAndSkipsDeletedRows() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(false);
|
||||
|
||||
submitOk("load");
|
||||
|
||||
ArgumentCaptor<LambdaQueryWrapper<ExceptionDisposal>> captor =
|
||||
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
verify(disposalMapper).selectList(captor.capture());
|
||||
String sql = captor.getValue().getTargetSql();
|
||||
|
||||
assertTrue(sql.contains("waybill_id"),
|
||||
"未了结异常必须限在本运单内,否则别的运单的异常会挡住这次上报:" + sql);
|
||||
assertTrue(sql.contains("is_deleted"),
|
||||
"已删除的异常不算未了结:" + sql);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 整单异常不受门禁
|
||||
|
||||
@Test
|
||||
@DisplayName("不带节点的整单异常不受门禁约束,可有未了结整单异常仍继续上报")
|
||||
void waybillLevelReportIsNotGated() {
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of(nodeException(null, "pending")));
|
||||
|
||||
submitOk(null);
|
||||
|
||||
ArgumentCaptor<ExceptionDisposal> captor = ArgumentCaptor.forClass(ExceptionDisposal.class);
|
||||
verify(disposalMapper).insert(captor.capture());
|
||||
assertNull(captor.getValue().getNodeCode(), "整单异常没有节点,不得凭空补一个");
|
||||
verify(driverWaybillService, never()).findPunchNode(any(), any());
|
||||
verify(driverWaybillService, never()).isNodePunched(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("上报人取登录态,请求体里的上报人一律被忽略")
|
||||
void reporterComesFromLoginState() {
|
||||
stubNode("load", "装货", "cargo");
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(false);
|
||||
ExceptionDisposal request = request("load");
|
||||
request.setReporterId(999999L);
|
||||
request.setReporterName("冒充的人");
|
||||
|
||||
runSubmit(request);
|
||||
|
||||
assertEquals(USER_ID, savedDisposal().getReporterId());
|
||||
assertEquals("韦司机", savedDisposal().getReporterName());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 夹具
|
||||
|
||||
private void stubNode(String key, String name, String type) {
|
||||
when(driverWaybillService.findPunchNode(WAYBILL_ID, key)).thenReturn(node(key, name, type));
|
||||
when(driverWaybillService.isNodePunched(WAYBILL_ID, key)).thenReturn(false);
|
||||
}
|
||||
|
||||
private void stubUnresolvedNodeException(String nodeCode, String status) {
|
||||
when(disposalMapper.selectList(any())).thenReturn(List.of(nodeException(nodeCode, status)));
|
||||
}
|
||||
|
||||
private ServiceException submitExpectRejected(String nodeCode) {
|
||||
ServiceException exception = assertThrows(ServiceException.class, () -> runSubmit(request(nodeCode)));
|
||||
// 门禁在落库之前:被拒时库里不能多一行
|
||||
verify(disposalMapper, never()).insert(any(ExceptionDisposal.class));
|
||||
return exception;
|
||||
}
|
||||
|
||||
private void submitOk(String nodeCode) {
|
||||
runSubmit(request(nodeCode));
|
||||
}
|
||||
|
||||
private void runSubmit(ExceptionDisposal request) {
|
||||
try (MockedStatic<AuthUtil> authUtil = mockStatic(AuthUtil.class);
|
||||
MockedStatic<UserCache> userCache = mockStatic(UserCache.class)) {
|
||||
authUtil.when(AuthUtil::getUserId).thenReturn(USER_ID);
|
||||
userCache.when(() -> UserCache.getUserRealName(USER_ID)).thenReturn("韦司机");
|
||||
service.submitReport(request);
|
||||
}
|
||||
}
|
||||
|
||||
private ExceptionDisposal savedDisposal() {
|
||||
ArgumentCaptor<ExceptionDisposal> captor = ArgumentCaptor.forClass(ExceptionDisposal.class);
|
||||
verify(disposalMapper).insert(captor.capture());
|
||||
return captor.getValue();
|
||||
}
|
||||
|
||||
private ExceptionDisposal request(String nodeCode) {
|
||||
ExceptionDisposal request = new ExceptionDisposal();
|
||||
request.setWaybillId(WAYBILL_ID);
|
||||
request.setExceptionType("cargo_damage");
|
||||
request.setReportDescription("装货时发现外包装破损");
|
||||
request.setNodeCode(nodeCode);
|
||||
return request;
|
||||
}
|
||||
|
||||
private ExceptionDisposal nodeException(String nodeCode, String status) {
|
||||
ExceptionDisposal disposal = new ExceptionDisposal();
|
||||
disposal.setId(6101L);
|
||||
disposal.setWaybillId(WAYBILL_ID);
|
||||
disposal.setNodeCode(nodeCode);
|
||||
disposal.setDisposalStatus(status);
|
||||
disposal.setIsDeleted(0);
|
||||
disposal.setReportTime(LocalDateTime.of(2026, 10, 6, 9, 30));
|
||||
return disposal;
|
||||
}
|
||||
|
||||
private Map<String, Object> node(String key, String name, String type) {
|
||||
Map<String, Object> cfg = new HashMap<>();
|
||||
cfg.put("key", key);
|
||||
cfg.put("name", name);
|
||||
cfg.put("type", type);
|
||||
cfg.put("enabled", true);
|
||||
cfg.put("punch", true);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private Waybill waybill() {
|
||||
Waybill waybill = new Waybill();
|
||||
waybill.setId(WAYBILL_ID);
|
||||
waybill.setWaybillNo("YD202610070001");
|
||||
waybill.setVehicleNo("桂AAW3255");
|
||||
waybill.setIsDeleted(0);
|
||||
return waybill;
|
||||
}
|
||||
|
||||
private void inject(String field, Object value) throws Exception {
|
||||
Class<?> type = service.getClass();
|
||||
while (type != null) {
|
||||
try {
|
||||
Field declared = type.getDeclaredField(field);
|
||||
declared.setAccessible(true);
|
||||
declared.set(service, value);
|
||||
return;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("未找到字段:" + field);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user