♻️ 收紧上报门禁查询、去掉平行列表接口并统一节点编码比较

This commit is contained in:
2026-10-07 13:24:02 +08:00
parent 3e687c8519
commit 3d3ed0ca27
5 changed files with 110 additions and 120 deletions
@@ -114,12 +114,6 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
private static final DateTimeFormatter DATE_DOT_SHORT = DateTimeFormatter.ofPattern("MM.dd");
private static final DateTimeFormatter TIME_HM = DateTimeFormatter.ofPattern("HH:mm");
/**
* 异常处置终态:已完成 / 已撤销。只有终态解除打卡门禁——「处理中」只表示有人
* 跟进过,问题可能仍在,不放行。
*/
private static final List<String> SETTLED_EXCEPTION_STATUSES = Arrays.asList("completed", "revoked");
/** 被异常阻断时的不可打卡原因(司机端展示) */
private static final String LOCK_REASON_EXCEPTION = "该运单存在未处理异常,处理完成后才能打卡";
@@ -811,17 +805,15 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
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);
.anyMatch(punch -> WaybillProcessSupport.sameNodeCode(nodeCode, punch.getNodeCode()));
}
@Override
public void assertCurrentDriverOwnsWaybill(Long waybillId) {
// 与司机打卡、司机接单共用同一套归属判定,不另立第二套规则
// 归属口径与司机打卡、司机接单一致:都按司机档案自述绑定的车牌判定
Driver driver = requireCurrentDriver();
loadDriverWaybill(waybillId, currentBoundPlates(driver));
}
@@ -864,7 +856,7 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
return exceptionDisposalMapper.selectList(Wrappers.<ExceptionDisposal>lambdaQuery()
.eq(ExceptionDisposal::getWaybillId, waybillId)
.eq(ExceptionDisposal::getIsDeleted, 0)
.notIn(ExceptionDisposal::getDisposalStatus, SETTLED_EXCEPTION_STATUSES)
.notIn(ExceptionDisposal::getDisposalStatus, WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES)
.orderByAsc(ExceptionDisposal::getReportTime)
.orderByAsc(ExceptionDisposal::getId));
}
@@ -1107,18 +1099,17 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
// 它作用于整张运单,所以每个可打卡节点——包括已打卡的与在途的——都挂标签、都算相关。
// 相关只有两种来源:归属本节点的、以及整单的;被前序节点异常牵连阻断的节点
// 只是不能打卡,那条异常并不归属它,故不进入它的相关列表。
// 未了结异常已按上报时间升序取出,过滤后仍保持该顺序;id 转字符串是因为主键是 int64,
// 前端直收数字会丢精度。
List<ExceptionDisposal> unresolved = unresolvedExceptions(waybill.getId());
List<String> unresolvedCodes = unresolved.stream()
.map(item -> Func.toStr(item.getNodeCode(), "").trim())
.collect(Collectors.toList());
List<Long> unresolvedIds = unresolved.stream()
.map(ExceptionDisposal::getId)
.collect(Collectors.toList());
boolean hasWaybillException = WaybillProcessSupport.hasWaybillLevelException(unresolvedCodes);
boolean hasWaybillException = unresolved.stream()
.anyMatch(item -> WaybillProcessSupport.isWaybillLevelException(item.getNodeCode()));
for (DriverPunchNodeVO vo : nodes) {
vo.setHasWaybillException(hasWaybillException);
vo.setRelatedExceptionIds(
WaybillProcessSupport.relatedExceptionIds(vo.getKey(), unresolvedCodes, unresolvedIds));
vo.setRelatedExceptionIds(unresolved.stream()
.filter(item -> WaybillProcessSupport.relatesToNode(vo.getKey(), item.getNodeCode()))
.map(item -> String.valueOf(item.getId()))
.collect(Collectors.toList()));
}
for (DriverPunchNodeVO vo : nodes) {
@@ -54,6 +54,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -182,12 +183,12 @@ public class ExceptionDisposalServiceImpl
* 节点编码忽略大小写:历史数据里的写法不一定与过程配置的 key 完全一致。
*/
private boolean hasUnresolvedNodeException(Long waybillId, String nodeCode) {
List<ExceptionDisposal> existing = baseMapper.selectList(Wrappers.<ExceptionDisposal>lambdaQuery()
Long count = baseMapper.selectCount(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()));
.eq(ExceptionDisposal::getIsDeleted, 0)
.notIn(ExceptionDisposal::getDisposalStatus, WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES)
.apply("LOWER(node_code) = {0}", nodeCode.toLowerCase(Locale.ROOT)));
return count != null && count > 0;
}
/** 按运单补齐运单号 / 车牌 / 项目 / 承运商等展示字段 */
@@ -58,6 +58,8 @@ public final class WaybillProcessSupport {
/** 异常处置终态:completed=已完成,revoked=已撤销。只有终态解除打卡门禁 */
private static final String DISPOSAL_COMPLETED = "completed";
private static final String DISPOSAL_REVOKED = "revoked";
/** 异常处置终态状态码集合:不在此集合内即未了结(processing 也在未了结之列) */
public static final List<String> SETTLED_DISPOSAL_STATUSES = List.of(DISPOSAL_COMPLETED, DISPOSAL_REVOKED);
private static final DateTimeFormatter HM = DateTimeFormatter.ofPattern("H:mm");
private static final DateTimeFormatter HM_PADDED = DateTimeFormatter.ofPattern("HH:mm");
@@ -391,42 +393,31 @@ public final class WaybillProcessSupport {
}
/**
* 是否存在未了结的整单异常。
* 两个节点编码是否指同一个节点:两侧去空白、忽略大小写。
* <p>
* 整单异常不归属任何过程节点,它的节点编码为空;它作用于整张运单,
* 所以该运单每个可打卡节点都要挂上「整单异常」标记。
* 空与空不算相等——节点编码为空表示整单,它不归属任何过程节点。
*/
public static boolean hasWaybillLevelException(List<String> unresolvedNodeCodes) {
if (unresolvedNodeCodes == null) {
return false;
}
return unresolvedNodeCodes.stream().anyMatch(code -> stringVal(code).isEmpty());
public static boolean sameNodeCode(String left, String right) {
String leftCode = stringVal(left);
String rightCode = stringVal(right);
return !leftCode.isEmpty() && leftCode.equalsIgnoreCase(rightCode);
}
/**
* 与某节点相关的未了结异常 id,一律以字符串返回。
* 该异常是否为整单异常:异常不归属任何过程节点时,节点编码为空。
*/
public static boolean isWaybillLevelException(String exceptionNodeCode) {
return stringVal(exceptionNodeCode).isEmpty();
}
/**
* 该异常是否与某节点相关。
* <p>
* 相关只有两种来源:归属该节点的异常,以及整单异常(节点编码为空,对每个节点都算相关)。
* 被前序节点异常牵连阻断的节点不算相关——它自己并没有出过事。
* <p>
* 顺序保持入参顺序,调用方按上报时间升序取出未了结异常即可。
* id 转成字符串是因为主键是 int64,前端直收数字会丢精度。
*/
public static List<String> relatedExceptionIds(String nodeKey, List<String> unresolvedNodeCodes,
List<Long> unresolvedIds) {
if (unresolvedNodeCodes == null || unresolvedIds == null) {
return Collections.emptyList();
}
String key = stringVal(nodeKey);
List<String> related = new ArrayList<>();
int size = Math.min(unresolvedNodeCodes.size(), unresolvedIds.size());
for (int i = 0; i < size; i++) {
String code = stringVal(unresolvedNodeCodes.get(i));
if (code.isEmpty() || code.equalsIgnoreCase(key)) {
related.add(String.valueOf(unresolvedIds.get(i)));
}
}
return related;
public static boolean relatesToNode(String nodeKey, String exceptionNodeCode) {
return isWaybillLevelException(exceptionNodeCode) || sameNodeCode(nodeKey, exceptionNodeCode);
}
/**