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 d86e008c..575ce831 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 @@ -144,6 +144,17 @@ public class ExceptionDisposalServiceImpl // 整单异常同样受这条约束:没有节点不等于没有上报人。 driverWaybillService.assertCurrentDriverOwnsWaybill(disposal.getWaybillId()); + // 上报门禁之零:运单得先在路上,且不能已经结束。 + // 判据与司机打卡同一来源;归属校验会顺手把「无需接单」的待执行运单校正为进行中, + // 所以状态要在它之后重新读,不能用 fillFromWaybill 里校正前的那份对象。 + Waybill reportWaybill = waybillService.getById(disposal.getWaybillId()); + String blockedReason = reportWaybill == null + ? null + : WaybillProcessSupport.reportBlockedReason(reportWaybill.getBusinessStatus()); + if (blockedReason != null) { + throw new ServiceException(blockedReason); + } + // 节点可以为空:全局入口(首页快捷格、打卡页底部按钮)上报的异常不指定环节, // 它无从定位阻断区间,由门禁按「阻断该运单全部未打卡节点」处理。 // 节点非空时才要求它确属该运单;节点名取自过程配置而非请求体,避免落进一个该节点并不叫的名字。 diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/WaybillProcessSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/WaybillProcessSupport.java index 2cf47ca4..4462c3a3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/WaybillProcessSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/WaybillProcessSupport.java @@ -191,6 +191,23 @@ public final class WaybillProcessSupport { return last != null && last.equals(day); } + /** + * 上报异常前的运单状态门禁原因;可上报时返回 null。 + *
+ * 与打卡共用「已开始运输」判据,但文案说的是能不能报,不是能不能打。 + * 结束优先于未开始:已完成/已取消的运单先说结束,不再提「等你上路」。 + */ + public static String reportBlockedReason(String businessStatus) { + String endedReason = punchEndedReason(businessStatus); + if (endedReason != null) { + return endedReason + ",不能上报异常"; + } + if (!isTransitPunchStarted(businessStatus)) { + return "运单尚未开始运输,暂不可上报异常"; + } + return null; + } + public static boolean isTerminalBusinessStatus(String businessStatus) { return "draft".equals(businessStatus) || "completed".equals(businessStatus) diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ExceptionDisposalReportGateTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ExceptionDisposalReportGateTest.java index be77a57c..4a64ae2a 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ExceptionDisposalReportGateTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ExceptionDisposalReportGateTest.java @@ -245,6 +245,51 @@ class ExceptionDisposalReportGateTest { assertEquals("韦司机", savedDisposal().getReporterName()); } + // ------------------------------------------------------------ 运单状态门禁 + + @Test + @DisplayName("未开始运输的运单不能上报:整单异常同样被拒") + void notStartedWaybillRejectsWaybillLevelReport() { + stubWaybillStatus("waiting_dispatch"); + + ServiceException exception = submitExpectRejected(null); + + assertEquals("运单尚未开始运输,暂不可上报异常", exception.getMessage()); + } + + @Test + @DisplayName("已完成的运单不能再上报") + void completedWaybillRejectsReport() { + stubWaybillStatus("completed"); + + ServiceException exception = submitExpectRejected("load"); + + assertEquals("运单已完成,不能上报异常", exception.getMessage()); + } + + @Test + @DisplayName("已取消的运单不能再上报") + void cancelledWaybillRejectsReport() { + stubWaybillStatus("cancelled"); + + ServiceException exception = submitExpectRejected(null); + + assertEquals("运单已取消,不能上报异常", exception.getMessage()); + } + + @Test + @DisplayName("运单状态门禁在节点门禁之前:未开始运输时先说没上路,不报节点已打卡") + void statusGateBeatsNodeGate() { + stubWaybillStatus("pending"); + stubNode("load", "装货", "cargo"); + when(driverWaybillService.isNodePunched(WAYBILL_ID, "load")).thenReturn(true); + + ServiceException exception = submitExpectRejected("load"); + + assertEquals("运单尚未开始运输,暂不可上报异常", exception.getMessage()); + verify(driverWaybillService, never()).findPunchNode(any(), any()); + } + // ------------------------------------------------------------ 上报人归属 @Test @@ -273,6 +318,13 @@ class ExceptionDisposalReportGateTest { // ------------------------------------------------------------ 夹具 + /** 改写夹具运单的业务状态,供状态门禁用例复写 getById 的桩 */ + private void stubWaybillStatus(String businessStatus) { + Waybill waybill = waybill(); + waybill.setBusinessStatus(businessStatus); + when(waybillService.getById(WAYBILL_ID)).thenReturn(waybill); + } + private void stubNode(String key, String name, String type) { when(driverWaybillService.findPunchNode(WAYBILL_ID, key)).thenReturn(node(key, name, type)); when(driverWaybillService.isNodePunched(WAYBILL_ID, key)).thenReturn(false); @@ -358,6 +410,8 @@ class ExceptionDisposalReportGateTest { waybill.setWaybillNo("YD202610070001"); waybill.setVehicleNo("桂AAW3255"); waybill.setIsDeleted(0); + // 状态门禁:夹具默认进行中,状态类用例自己改 + waybill.setBusinessStatus("running"); return waybill; } diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/support/WaybillProcessSupportTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/support/WaybillProcessSupportTest.java index 28712838..b09c82c4 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/support/WaybillProcessSupportTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/support/WaybillProcessSupportTest.java @@ -449,4 +449,15 @@ class WaybillProcessSupportTest { assertFalse(WaybillProcessSupport.isPunchedOn(null, now)); } + @Test + @DisplayName("上报状态门禁:只有进行中可报,未开始与已结束各有各的话") + void reportBlockedReasonOnlyForRunningWaybill() { + assertNull(WaybillProcessSupport.reportBlockedReason("running")); + assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("pending")); + assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("dispatching")); + // 结束优先:已完成/已取消先说结束,不提「等你上路」 + assertEquals("运单已完成,不能上报异常", WaybillProcessSupport.reportBlockedReason("completed")); + assertEquals("运单已取消,不能上报异常", WaybillProcessSupport.reportBlockedReason("cancelled")); + } + }