🐛 修复在途打卡绕过异常门禁:未了结异常落在在途之后时仍可打卡
在途的异常阻断此前按过程配置里节点的位置区间计算:异常节点及其后的节点不可打卡。 异常报在「卸货」这类排在在途之后的节点时,区间只有卸货、签收,在途被放行, 等于车越接近目的地越能报在途;而异常报在「装货」(在途之前)反而会锁住在途。 改为运单级判定:该运单只要存在未了结异常,在途就不可打卡,与异常归属哪个节点无关。 普通打卡节点仍按区间(异常所在节点及其后)阻断。判定集中在 WaybillProcessSupport.blockedNodeKeys,卡片禁用态与 /enroute/submit 的强制校验共用它。 异常落在非打卡节点上(接单/回单、或已从过程配置删掉的节点)时没有可跳转的具体异常, 故 blockedExceptionId 可能为空,卡片只显示不可打卡原因。
This commit is contained in:
+13
-8
@@ -869,7 +869,9 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
||||
/**
|
||||
* 断言指定节点未被异常门禁阻断。
|
||||
* <p>
|
||||
* 阻断范围是未了结异常所在节点及其后的节点;前端禁用只是体验层,这里才是强制点。
|
||||
* 普通节点:阻断范围是未了结异常所在节点及其后的节点。
|
||||
* 在途节点:该运单存在任何未了结异常即被阻断,与异常落在哪个节点无关。
|
||||
* 前端禁用只是体验层,这里才是强制点。
|
||||
*/
|
||||
private void assertNodeNotBlocked(String processJson, Long waybillId, String nodeCode) {
|
||||
Set<String> blocked = WaybillProcessSupport.blockedNodeKeys(
|
||||
@@ -931,8 +933,8 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
||||
/**
|
||||
* 组装过程配置 punch=是 的打卡节点列表。
|
||||
* <p>
|
||||
* 节点自身不做先后顺序门禁(未打卡即可打,与打卡先后无关);
|
||||
* 唯一的跨节点约束是异常门禁:存在未了结异常时,异常所在节点及其后的节点不可打卡。
|
||||
* 两道跨节点门禁:异常门禁(未了结异常所在节点及其后节点不可打卡,在途另按运单级——
|
||||
* 该运单有任何未了结异常就不可打卡)、顺序门禁(前置打卡节点未完成则不可打卡,在途豁免)。
|
||||
* 默认展开:第一个未完成、且未被异常阻断的可见节点。
|
||||
* 非在途节点「已打卡」以节点打卡表为准。
|
||||
* 节点字段(定位/货量/凭证)取自动态过程配置。
|
||||
@@ -1020,19 +1022,22 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
||||
nodes.add(vo);
|
||||
}
|
||||
|
||||
// 异常门禁:未了结异常所在节点、及其在过程配置顺序中位于其后的节点不可打卡。
|
||||
// 异常门禁:未了结异常所在节点、及其在过程配置顺序中位于其后的节点不可打卡;
|
||||
// 在途另按运单级——该运单只要还有未了结异常,在途就不可打卡。
|
||||
// 已打卡节点不受影响,仍可展开查看回填内容。
|
||||
ExceptionDisposal blocker = blockingException(waybill, processJson);
|
||||
if (blocker != null) {
|
||||
String blockerNodeCode = Func.toStr(blocker.getNodeCode(), "").trim();
|
||||
Set<String> blockedKeys = WaybillProcessSupport.blockedNodeKeysFrom(processJson, blockerNodeCode);
|
||||
Set<String> blockedKeys = WaybillProcessSupport.blockedNodeKeys(
|
||||
processJson, unresolvedExceptionNodeCodeList(waybill.getId()));
|
||||
if (!blockedKeys.isEmpty()) {
|
||||
for (DriverPunchNodeVO vo : nodes) {
|
||||
if (Boolean.TRUE.equals(vo.getDone()) || !blockedKeys.contains(vo.getKey())) {
|
||||
continue;
|
||||
}
|
||||
vo.setBlocked(true);
|
||||
vo.setActionable(false);
|
||||
vo.setBlockedExceptionId(blocker.getId());
|
||||
if (blocker != null) {
|
||||
vo.setBlockedExceptionId(blocker.getId());
|
||||
}
|
||||
vo.setLockReason(LOCK_REASON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-3
@@ -405,9 +405,14 @@ public final class WaybillProcessSupport {
|
||||
/**
|
||||
* 计算被异常门禁阻断的过程节点 key。
|
||||
* <p>
|
||||
* 阻断范围:未了结异常所在节点、及其在过程配置顺序中位于其后的所有节点。
|
||||
* 普通打卡节点按区间阻断:未了结异常所在节点、及其在过程配置顺序中位于其后的所有节点。
|
||||
* 未指明节点(nodeCode 为空)的异常无从定位区间,阻断全部可打卡节点。
|
||||
* <p>
|
||||
* **在途节点按运单级阻断**:该运单只要存在任何未了结异常,在途就不可打卡,
|
||||
* 与异常落在哪个节点无关。在途只回答「此刻在哪」,不表示某个环节完成,
|
||||
* 区间对它没有意义——按区间算的话,异常报在「装货」(在途之前)会锁住在途,
|
||||
* 报在「到货」(在途之后)反而放行,等于越接近目的地越能报在途。
|
||||
* <p>
|
||||
* 多个未了结异常时按**最早**的那个起算——它锁住的区间最大,覆盖其余异常。
|
||||
* 需要知道具体是「哪条异常」在拦人时,用 {@link #earliestBlockingNodeCode}。
|
||||
*
|
||||
@@ -415,8 +420,16 @@ public final class WaybillProcessSupport {
|
||||
* @param unresolvedNodeCodes 该运单未了结异常的节点 key;含空串表示存在未指明节点的异常
|
||||
*/
|
||||
public static Set<String> blockedNodeKeys(String processJson, Collection<String> unresolvedNodeCodes) {
|
||||
String earliest = earliestBlockingNodeCode(processJson, unresolvedNodeCodes);
|
||||
return blockedNodeKeysFrom(processJson, earliest);
|
||||
Set<String> blocked = new LinkedHashSet<>(
|
||||
blockedNodeKeysFrom(processJson, earliestBlockingNodeCode(processJson, unresolvedNodeCodes)));
|
||||
if (unresolvedNodeCodes == null || unresolvedNodeCodes.isEmpty()) {
|
||||
return blocked;
|
||||
}
|
||||
String transitKey = transitNodeKey(processJson);
|
||||
if (Func.isNotEmpty(transitKey)) {
|
||||
blocked.add(transitKey);
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user