✨ 节点异常上报增加打卡与重复上报校验

This commit is contained in:
2026-10-07 12:16:22 +08:00
parent bf37c47856
commit 7e84b670b6
5 changed files with 489 additions and 1 deletions
@@ -108,4 +108,11 @@ public interface IDriverWaybillService {
*/
Map<String, Object> findPunchNode(Long waybillId, String nodeCode);
/**
* 该运单的指定节点当前是否存在打卡流水;节点编码忽略大小写。
* <p>
* 以打卡流水为准而非运单当前节点:管理端取消打卡后这里要立刻回到未打卡。
*/
boolean isNodePunched(Long waybillId, String nodeCode);
}
@@ -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>
@@ -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;