🐛 修正分段改址的出发地同步与站数校验,详情字段移出列表

运单分支中间站改址后逐段同步 departure;无配载单的站数变化显式拒绝;已撤销异常不得再跟进或完成;配载单号、子运单号与可调整站点只在详情组装,列表路径不新增查询。
This commit is contained in:
2026-10-23 01:29:00 +08:00
parent 4658bbf8ca
commit 87b473594a
5 changed files with 173 additions and 44 deletions
@@ -135,7 +135,9 @@ public class ExceptionDisposalServiceImpl
@Override
public ExceptionDisposalVO detail(Long id) {
ExceptionDisposalVO vo = toVO(getExisting(id));
ExceptionDisposal entity = getExisting(id);
ExceptionDisposalVO vo = convertVO(entity);
fillRouteNodes(vo, fillRouteAndCargo(vo, entity.getWaybillId()));
vo.setFollowRecords(followRecords(id));
return vo;
}
@@ -375,7 +377,7 @@ public class ExceptionDisposalServiceImpl
throw new ServiceException("跟进说明不能超过500字");
}
ExceptionDisposal disposal = getExisting(request.getId());
if (STATUS_COMPLETED.equals(disposal.getDisposalStatus())) {
if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) {
throw new ServiceException("已完成的异常不允许继续跟进");
}
// 上传了附件但没写说明时,跟进说明留空,列表按附件回退展示
@@ -401,7 +403,7 @@ public class ExceptionDisposalServiceImpl
@Transactional(rollbackFor = Exception.class)
public void complete(Long id) {
ExceptionDisposal disposal = getExisting(id);
if (STATUS_COMPLETED.equals(disposal.getDisposalStatus())) {
if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) {
throw new ServiceException("异常已完成,请勿重复操作");
}
disposal.setDisposalStatus(STATUS_COMPLETED);
@@ -423,7 +425,7 @@ public class ExceptionDisposalServiceImpl
throw new ServiceException("存在无效的异常记录");
}
for (ExceptionDisposal disposal : disposals) {
if (STATUS_COMPLETED.equals(disposal.getDisposalStatus())) {
if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) {
throw new ServiceException("已完成的异常不允许重复完成");
}
disposal.setDisposalStatus(STATUS_COMPLETED);
@@ -697,11 +699,14 @@ public class ExceptionDisposalServiceImpl
.collect(Collectors.joining(TRANSIT_ADDRESS_JOINER));
sourceTaskInfoJson = loading.getTaskInfoJson();
} else {
// 运单的 route_json 是路线段形状,司机端按段解析:只按段改地址,绝不整份换成站点行。
// 没变就一个键都不动,避免把历史数据里缺省的字段顺手补齐成另一份结构。
routeJson = sourceAddresses.equals(targetAddresses)
? null
: rewriteWaybillRouteSegments(waybill.getRouteJson(), targetAddresses);
// 运单的 route_json 是路线段形状,司机端按段解析:只按段改地址,不整份换成站点行
List<Map<String, Object>> segments = routeSegments(waybill.getRouteJson());
boolean addressChanged = !sourceAddresses.equals(targetAddresses);
// 段形状可识别时站数必须等于段数 + 1;认不出段形状时不改 route_json
if (addressChanged && !segments.isEmpty() && segments.size() + 1 != targetAddresses.size()) {
throw new ServiceException("无配载单的运单不支持增减途经站");
}
routeJson = addressChanged ? rewriteWaybillRouteSegments(segments, targetAddresses) : null;
sourceTaskInfoJson = waybill.getTaskInfoJson();
}
String taskInfoJson = appendRouteChangeRecord(sourceTaskInfoJson, followContent);
@@ -773,22 +778,20 @@ public class ExceptionDisposalServiceImpl
}
/**
* 按路线段改写运单路线:第 i 段的到达地对应第 i+1 站,首段出发地对应首站。
* 其它键(segmentNo、transportType、未知键)原样保留。
* 按路线段改写运单路线:每段的出发地对应第 i 站、到达地对应第 i+1 站,其它键(segmentNo、
* transportType、未知键)原样保留。
* <p>
* 站数变了或现有 route_json 不是可识别的段数组时返回 null,调用方跳过路线改写:
* 段形状没有装卸归属,说不出哪一段被删掉或被挪动,硬写等于重写这趟车的行程;
* 历史节点行(PC 改运单路线写下的 address + tags)同理不动,改写会让司机端读不出路线。
* 现有 route_json 认不出段形状(空、坏 JSON、PC 节点行)时返回 null,调用方跳过路线改写;
* 站数与段数不匹配由调用方拒绝。
*/
private String rewriteWaybillRouteSegments(String routeJson, List<String> targetAddresses) {
List<Map<String, Object>> segments = routeSegments(routeJson);
if (segments.isEmpty() || segments.size() + 1 != targetAddresses.size()) {
private String rewriteWaybillRouteSegments(List<Map<String, Object>> segments, List<String> targetAddresses) {
if (segments.isEmpty()) {
return null;
}
List<Map<String, Object>> rewritten = new ArrayList<>(segments.size());
for (int index = 0; index < segments.size(); index++) {
Map<String, Object> segment = new LinkedHashMap<>(segments.get(index));
rewriteSegmentPoint(segment, "departure", index == 0 ? targetAddresses.get(0) : null);
rewriteSegmentPoint(segment, "departure", targetAddresses.get(index));
rewriteSegmentPoint(segment, "arrival", targetAddresses.get(index + 1));
rewritten.add(segment);
}
@@ -796,15 +799,11 @@ public class ExceptionDisposalServiceImpl
}
private void rewriteSegmentPoint(Map<String, Object> segment, String point, String targetAddress) {
if (targetAddress == null) {
return;
}
String currentAddress = firstNonBlank(text(segment.get(point + "Address")),
text(segment.get(point + "Name")), "");
if (Objects.equals(currentAddress, targetAddress)) {
return;
}
// 站名与地址是同一份文本:调整页只有一个地址输入框,改了地址站名也得跟着变
segment.put(point + "Address", targetAddress);
segment.put(point + "Name", targetAddress);
}
@@ -966,7 +965,14 @@ public class ExceptionDisposalServiceImpl
return disposal;
}
/** 列表与上报结果共用的转换:不含详情页的配载单号、子运单号与可调整站点 */
private ExceptionDisposalVO toVO(ExceptionDisposal entity) {
ExceptionDisposalVO vo = convertVO(entity);
fillRouteAndCargo(vo, entity.getWaybillId());
return vo;
}
private ExceptionDisposalVO convertVO(ExceptionDisposal entity) {
ExceptionDisposalVO vo = BeanUtil.copyProperties(entity, ExceptionDisposalVO.class);
if (vo == null) {
return null;
@@ -975,18 +981,17 @@ public class ExceptionDisposalServiceImpl
vo.setUpdateUserName(UserDisplayCache.getUserRealName(entity.getUpdateUser()));
vo.setDisposalStatusName(statusName(entity.getDisposalStatus()));
vo.setScenePhotoList(splitPhotos(entity.getScenePhotos()));
fillRouteAndCargo(vo, entity.getWaybillId());
return vo;
}
/** 按关联运单补齐详情页路线 / 货物 / 配载单展示字段 */
private void fillRouteAndCargo(ExceptionDisposalVO vo, Long waybillId) {
/** 按关联运单补齐路线与货物展示字段,返回本次取到的运单;运单不存在时返回 null */
private Waybill fillRouteAndCargo(ExceptionDisposalVO vo, Long waybillId) {
if (vo == null || waybillId == null) {
return;
return null;
}
Waybill waybill = waybillService.getById(waybillId);
if (waybill == null || Objects.equals(waybill.getIsDeleted(), 1)) {
return;
return null;
}
Map<String, String> route = new HashMap<>(2);
route.put("start", Func.toStr(waybill.getDepartureName(), ""));
@@ -997,8 +1002,7 @@ public class ExceptionDisposalServiceImpl
cargo.put("name", Func.toStr(waybill.getCargoName(), ""));
cargo.put("weight", formatWeight(waybill.getQuantity(), waybill.getQuantityUnit()));
vo.setCargo(cargo);
fillRouteNodes(vo, waybill);
return waybill;
}
/**
@@ -1008,6 +1012,14 @@ public class ExceptionDisposalServiceImpl
* 运单自己的路线段——历史异常仍要能打开,不能因为配载单不在了就打不开详情。
*/
private void fillRouteNodes(ExceptionDisposalVO vo, Waybill waybill) {
if (vo == null) {
return;
}
if (waybill == null) {
vo.setChildWaybillNos(List.of());
vo.setRouteNodes(List.of());
return;
}
LoadingManage loading = findLoading(waybill.getLoadingNo());
if (loading == null) {
vo.setChildWaybillNos(new ArrayList<>());