From aa7caf5a4ce896b60581f54c06c2f07f360d4756 Mon Sep 17 00:00:00 2001 From: weicw1996 Date: Fri, 23 Oct 2026 01:29:09 +0800 Subject: [PATCH] =?UTF-8?q?:recycle:=20=E6=95=B4=E7=90=86=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=A4=84=E7=BD=AE=E8=B7=AF=E7=BA=BF=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E7=9A=84=E6=B3=A8=E9=87=8A=E3=80=81=E5=8F=82=E6=95=B0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E4=B8=8E=E7=BB=88=E6=80=81=E5=AE=88=E5=8D=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 注释只留状态与约束,去掉推理与取舍表述;firstNonBlank / rewriteSegmentPoint 参数改为有语义的名字;reject / revoke / adjustRoute 共用 assertNotSettled,文案沿用「该异常已处理」。 --- .../impl/ExceptionDisposalServiceImpl.java | 95 +++++++++---------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ExceptionDisposalServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ExceptionDisposalServiceImpl.java index 774e1c7e..bf0d38b7 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ExceptionDisposalServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ExceptionDisposalServiceImpl.java @@ -440,10 +440,7 @@ public class ExceptionDisposalServiceImpl throw new ServiceException("请选择需要拒绝的异常"); } ExceptionDisposal disposal = getExisting(request.getId()); - // 已完成、已撤销都算已了结:再拒绝一次会覆盖最新处置结论,并往处置记录里多添一条 - if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) { - throw new ServiceException("该异常已处理"); - } + assertNotSettled(disposal); String rejectReason = Func.toStr(request.getRejectReason(), "").trim(); if (Func.isBlank(rejectReason)) { throw new ServiceException("请填写拒绝原因"); @@ -467,14 +464,12 @@ public class ExceptionDisposalServiceImpl public void revoke(Long id) { ExceptionDisposal disposal = getExisting(id); Long currentUserId = AuthUtil.getUserId(); - // 撤销是上报人收回自己的上报,不是一次处置:别的账号(包括调度员)要推翻上报只能走拒绝 + // 撤销是上报人收回自己的上报,不是一次处置 if (currentUserId == null || !Objects.equals(disposal.getReporterId(), currentUserId)) { throw new ServiceException("只能撤销本人上报的异常"); } - if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) { - throw new ServiceException("该异常已处理"); - } - // 不写跟进记录,也不动 latest_follow_content:处置时间线里不该出现一条由上报人「处置」的记录 + assertNotSettled(disposal); + // 不写跟进记录,也不动 latest_follow_content disposal.setDisposalStatus(STATUS_REVOKED); updateById(disposal); } @@ -486,25 +481,23 @@ public class ExceptionDisposalServiceImpl throw new ServiceException("请选择需要调整路线的异常"); } ExceptionDisposal disposal = getExisting(request.getId()); - if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) { - throw new ServiceException("该异常已处理"); - } + assertNotSettled(disposal); Waybill waybill = disposal.getWaybillId() == null ? null : waybillService.getById(disposal.getWaybillId()); if (waybill == null || Objects.equals(waybill.getIsDeleted(), 1)) { throw new ServiceException("关联运单不存在"); } - // 路线改在谁身上跟着配载单走:配载单是这趟车的执行主体,司机端打卡读的就是它的路线 + // 路线改在谁身上跟着配载单走:配载单是这趟车的执行主体 LoadingManage loading = findLoading(waybill.getLoadingNo()); if (loading == null && Func.isNotBlank(waybill.getLoadingNo())) { throw new ServiceException("运单已关联配载单,请在配载单中修改"); } - // 校验与拼装全部在写入之前完成:任何一条不通过都不该留下半条处置记录 + // 校验与拼装全部在写入之前完成 List stations = validateRouteNodes(request.getRouteNodes(), loading); List sourceAddresses = loading != null ? loadingStationAddresses(loading) : waybillStationAddresses(waybill); List targetAddresses = stations.stream().map(Station::address).toList(); - // 首站地址只读;原数据里首站地址缺失时无从比对,放行而不是拒绝整次调整 + // 首站地址不可修改;原数据首站地址为空时跳过比对 if (!sourceAddresses.isEmpty() && Func.isNotBlank(sourceAddresses.get(0)) && !Objects.equals(sourceAddresses.get(0), targetAddresses.get(0))) { throw new ServiceException("起始地址不允许修改"); @@ -515,8 +508,7 @@ public class ExceptionDisposalServiceImpl } RouteChangePlan plan = planRouteChange(loading, waybill, stations, sourceAddresses, targetAddresses); - // 事务内顺序固定:跟进记录 → 载体路线 → 异常状态。 - // 少了任何一步都读不出「谁在什么时候把路线改成了什么」,所以任一步失败都整体回滚 + // 事务内顺序固定:跟进记录 → 载体路线 → 异常状态 insertFollowRecord(disposal.getId(), plan.followContent()); if (loading != null) { applyLoadingRoute(loading, plan); @@ -528,6 +520,13 @@ public class ExceptionDisposalServiceImpl updateById(disposal); } + /** 已了结(已完成 / 已撤销)的异常不允许再次处置 */ + private void assertNotSettled(ExceptionDisposal disposal) { + if (WaybillProcessSupport.SETTLED_DISPOSAL_STATUSES.contains(disposal.getDisposalStatus())) { + throw new ServiceException("该异常已处理"); + } + } + /** 一条跟进记录:处置记录里「先写记录再改状态」的顺序由调用方的事务保证 */ private void insertFollowRecord(Long disposalId, String content) { ExceptionDisposalFollowRecord record = new ExceptionDisposalFollowRecord(); @@ -553,7 +552,7 @@ public class ExceptionDisposalServiceImpl /** * 校验并规范化站点:站数至少两个、首站 start、末站 end、其余 transit,地址都不能为空。 *

- * kind 不额外接受「按位置猜」的写法:站序就是落库顺序,kind 与位置不一致时写下去的首末站地址会是错的。 + * kind 由位置决定,必须与站序一致。 */ private List validateRouteNodes(List routeNodes, LoadingManage loading) { @@ -597,10 +596,9 @@ public class ExceptionDisposalServiceImpl } /** - * 站点标记里的数字是配载单运单集合里的位次(1 起),越界的标记会让司机端找不到对应子运单, - * 那样的路线读不出装卸与货物,宁可报错也不落库。 + * 站点标记里的数字是配载单运单集合里的位次(1 起),越界时拒绝。 *

- * 没有配载单就没有子运单序号可言:运单的路线段没有装卸归属,站点标记不参与落库。 + * 没有配载单时没有子运单序号,站点标记不参与落库。 */ private void assertTagsMatchChildWaybills(List stations, LoadingManage loading) { if (loading == null) { @@ -626,7 +624,7 @@ public class ExceptionDisposalServiceImpl List waybillIds = JsonUtil.parse(waybillIdsJson, List.class); return waybillIds == null ? 0 : waybillIds.size(); } catch (Exception ignored) { - // 运单集合不是 JSON 数组时按没有子运单处理,站点标记一律对不上 + // 运单集合不是 JSON 数组时按没有子运单处理 return 0; } } @@ -645,7 +643,7 @@ public class ExceptionDisposalServiceImpl /** * 运单当前路线的站点地址:首段出发地是首站,其后每段的到达地各成一站。 - * 段里缺地址时退化用段名——运单详情与调整页用的也是这两列。 + * 段里缺地址时用站名替代。 */ private List waybillStationAddresses(Waybill waybill) { List> segments = routeSegments(waybill.getRouteJson()); @@ -739,7 +737,7 @@ public class ExceptionDisposalServiceImpl parts.add("到货地 " + displayAddress(sourceEnd) + " → " + displayAddress(targetEnd)); } if (parts.isEmpty()) { - // 首末站都没动,变化只能落在途经站:地址细节塞不进 500 字的跟进记录,按站数说明 + // 首末站未变时按途经站数量说明变化 parts.add("途经站由 " + transitCount(sourceAddresses) + " 个改为 " + transitCount(targetAddresses) + " 个"); } return ROUTE_CHANGE_PREFIX + String.join(";", parts); @@ -770,7 +768,7 @@ public class ExceptionDisposalServiceImpl // 站点行没有独立的站名,展示地址与地址取同一份文本 row.put("displayAddress", station.address()); row.put("tags", station.tags()); - // locked 只影响 PC 编辑弹窗能否拖动,司机端不读;这里写入的路线始终可继续调整 + // locked 只影响 PC 编辑弹窗能否拖动,司机端不读 row.put("locked", false); rows.add(row); } @@ -798,20 +796,20 @@ public class ExceptionDisposalServiceImpl return JsonUtil.toJson(rewritten); } - private void rewriteSegmentPoint(Map segment, String point, String targetAddress) { - String currentAddress = firstNonBlank(text(segment.get(point + "Address")), - text(segment.get(point + "Name")), ""); + /** 站点地址与站名写同一份文本 */ + private void rewriteSegmentPoint(Map segment, String pointKey, String targetAddress) { + String currentAddress = firstNonBlank(text(segment.get(pointKey + "Address")), + text(segment.get(pointKey + "Name")), ""); if (Objects.equals(currentAddress, targetAddress)) { return; } - segment.put(point + "Address", targetAddress); - segment.put(point + "Name", targetAddress); + segment.put(pointKey + "Address", targetAddress); + segment.put(pointKey + "Name", targetAddress); } /** - * 识别路线段形状的 route_json。运单历史上存在两种形状,另一种是 PC 写下的节点行 - * (address + tags + 运单号),司机端按段解析读不出来,因此这里只认段:元素带段字段、 - * 且不带 address / tags。 + * 识别路线段形状的 route_json:元素带段字段,且不带 address / tags。 + * 另一形状是 PC 写下的节点行(address + tags + 运单号),司机端按段解析读不出来。 */ private List> routeSegments(String routeJson) { List> elements = jsonObjectList(routeJson); @@ -829,8 +827,7 @@ public class ExceptionDisposalServiceImpl /** * 任务信息是整列覆盖:读出原对象,只把本次变更记录插到最前面,其余键原样写回。 - * 原来的内容不是 JSON 对象(坏数据或历史数组)时按空对象重建,只丢这一列的无法解析部分, - * 不能让一次路线调整因为附件信息列而失败。 + * 原来的内容不是 JSON 对象(坏数据或历史数组)时按空对象重建。 */ private String appendRouteChangeRecord(String taskInfoJson, String content) { Map taskInfo = parseTaskInfo(taskInfoJson); @@ -849,7 +846,7 @@ public class ExceptionDisposalServiceImpl return JsonUtil.toJson(taskInfo); } - /** 任务信息只认 JSON 对象:数组与坏数据都按空对象重建,这一列不能挡住整次路线调整 */ + /** 任务信息只认 JSON 对象:数组与坏数据都按空对象重建 */ private Map parseTaskInfo(String taskInfoJson) { Map taskInfo = new LinkedHashMap<>(); if (Func.isBlank(taskInfoJson)) { @@ -889,7 +886,7 @@ public class ExceptionDisposalServiceImpl } } - /** 解析成对象数组;坏 JSON 与空值都按空数组处理,路线坏了不该让整次操作崩掉 */ + /** 解析成对象数组;坏 JSON 与空值都按空数组处理 */ private List> jsonObjectList(String json) { List> result = new ArrayList<>(); if (Func.isBlank(json)) { @@ -916,18 +913,18 @@ public class ExceptionDisposalServiceImpl return value == null ? "" : String.valueOf(value).trim(); } - private String firstNonBlank(String first, String second, String fallback) { - if (Func.isNotBlank(first)) { - return first; + private String firstNonBlank(String addressValue, String nameValue, String fallbackValue) { + if (Func.isNotBlank(addressValue)) { + return addressValue; } - return Func.isNotBlank(second) ? second : fallback; + return Func.isNotBlank(nameValue) ? nameValue : fallbackValue; } /** 规范化后的一个站点:地址 + 该站的装卸标记 */ private record Station(String address, List tags) { } - /** 一次路线调整要落库的全部内容,写入阶段只负责 set 与 update */ + /** 一次路线调整要落库的全部内容 */ private record RouteChangePlan(String followContent, String routeJson, String departureAddress, @@ -1008,8 +1005,8 @@ public class ExceptionDisposalServiceImpl /** * 详情页的可编辑站点与配载单信息。 *

- * 配载单是这趟车的执行主体,站点取它的站点行;运单没有配载单号、或配载单已被删除 / 解绑时退回 - * 运单自己的路线段——历史异常仍要能打开,不能因为配载单不在了就打不开详情。 + * 站点取配载单的站点行;运单没有配载单号,或配载单已删除时退回运单的路线段。 + * 运单不存在时三项各给空值。 */ private void fillRouteNodes(ExceptionDisposalVO vo, Waybill waybill) { if (vo == null) { @@ -1061,7 +1058,7 @@ public class ExceptionDisposalServiceImpl /** * 没有配载单时从路线段推导站点:首段出发地是首站,其后每段的到达地各成一站。 - * 地址口径与改路线时的比对一致(地址优先、站名兜底),否则用户什么都没改直接提交也会被判成改过地址。 + * 地址口径与改路线时的比对一致(地址优先、站名兜底)。 */ private List waybillRouteNodes(Waybill waybill) { List nodes = new ArrayList<>(); @@ -1078,7 +1075,7 @@ public class ExceptionDisposalServiceImpl return stationKinds(nodes); } - /** 站序声明:首站 start、末站 end、其余 transit;不足两个站就没有首末可言,不给前端调整入口 */ + /** 站序声明:首站 start、末站 end、其余 transit;不足两个站时返回空列表 */ private List stationKinds(List nodes) { if (nodes.size() < 2) { return new ArrayList<>(); @@ -1092,7 +1089,7 @@ public class ExceptionDisposalServiceImpl /** * 站点标记指向配载单运单集合里的第 N 张子运单,取其项目名去重。 - * 标记对不上子运单(越界、子运单已不存在)时跳过,一个坏标记不该让整个详情打不开。 + * 标记对不上子运单(越界、子运单已不存在)时跳过。 */ private List stationProjects(List tags, Map childWaybills) { Set projects = new LinkedHashSet<>(); @@ -1111,7 +1108,7 @@ public class ExceptionDisposalServiceImpl /** * 按配载单运单集合的位次取子运单,位次从 0 起(站点标记里的 N 减一)。 - * 位次不能因为某张子运单查不到而前移,否则标记会指到别的子运单上。 + * 位次不因缺子运单而前移。 */ private Map childWaybillsByOrder(LoadingManage loading) { Map childWaybills = new LinkedHashMap<>(); @@ -1153,7 +1150,7 @@ public class ExceptionDisposalServiceImpl } } } catch (Exception ignored) { - // 运单集合坏了就没有子运单可言,详情退回运单自己的路线,不影响打开 + // 运单集合不是 JSON 数组时按没有子运单处理 return new ArrayList<>(); } return waybillIds;