diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CertAuditServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CertAuditServiceImpl.java index 07248769..4fe25a83 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CertAuditServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CertAuditServiceImpl.java @@ -206,7 +206,7 @@ public class CertAuditServiceImpl implements ICertAuditService { // 与指针合并成同一次 UPDATE,不额外多写一次同一行。 applySnapshotToVehicle(vehicle, record.getSnapshotJson()); } - transportVehicleService.updateById(vehicle); + requireTargetUpdated(transportVehicleService.updateById(vehicle), "车辆", record.getBizId()); return; } Driver driver = new Driver(); @@ -225,7 +225,26 @@ public class CertAuditServiceImpl implements ICertAuditService { // 与指针合并成同一次 UPDATE,不额外多写一次同一行。 applySnapshotToDriver(driver, record.getSnapshotJson()); } - driverService.updateById(driver); + requireTargetUpdated(driverService.updateById(driver), "司机", record.getBizId()); + } + + /** + * 目标对象的写入必须命中一行,否则整体失败 + *

+ * {@code updateById} 命中 0 行时**不抛异常、只返回 false**。原先丢弃这个返回值, + * 于是「审核记录已通过、司机/车辆档案一个字没变」能静默成立:生效快照指针没写进去, + * 对象下次接单仍被判未认证,而审核页显示的是已通过。此处让事务整体回滚, + * 宁可报错也不留下状态与档案互相矛盾的数据。 + * + * @param updated 更新结果 + * @param target 对象名称,用于提示 + * @param id 对象主键 + */ + private void requireTargetUpdated(boolean updated, String target, Long id) { + if (!updated) { + throw new ServiceException("未能更新" + target + "档案(ID:" + id + + "),本次审核已回滚、未生效;请核对该对象是否存在、未被删除,且与认证记录属于同一租户"); + } } /** diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/CertAuditServiceImplTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/CertAuditServiceImplTest.java index 93416743..503311a8 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/CertAuditServiceImplTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/CertAuditServiceImplTest.java @@ -50,6 +50,9 @@ class CertAuditServiceImplTest { transportVehicleService = mock(ITransportVehicleService.class); driverService = mock(IDriverService.class); service = new CertAuditServiceImpl(certRecordService, certRecordMapper, transportVehicleService, driverService); + // 目标对象的写入默认成功;「写入一行都没命中」由专门的用例覆盖 + when(transportVehicleService.updateById(any(TransportVehicle.class))).thenReturn(true); + when(driverService.updateById(any(Driver.class))).thenReturn(true); } // ------------------------------------------------------------ 通过 @@ -174,6 +177,29 @@ class CertAuditServiceImplTest { "审核通过不得把司机的车辆绑定清空"); } + @Test + @DisplayName("通过:目标对象一行都没命中时必须整体失败,不得「记录已通过、档案没变」") + void approveFailsWhenVehicleNotUpdated() { + when(certRecordService.getById(RECORD_ID)).thenReturn(vehicleRecord(TransportCertRecord.STATUS_PENDING)); + when(transportVehicleService.updateById(any(TransportVehicle.class))).thenReturn(false); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.audit(RECORD_ID, true, null)); + assertTrue(exception.getMessage().contains("未生效"), exception.getMessage()); + } + + @Test + @DisplayName("通过司机认证:司机档案没命中时同样必须整体失败,不得静默放行") + void approveFailsWhenDriverNotUpdated() { + when(certRecordService.getById(RECORD_ID)).thenReturn(driverRecord(TransportCertRecord.STATUS_PENDING)); + when(driverService.getById(DRIVER_ID)).thenReturn(driver(null)); + when(driverService.updateById(any(Driver.class))).thenReturn(false); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.audit(RECORD_ID, true, null)); + assertTrue(exception.getMessage().contains("司机"), exception.getMessage()); + } + // ------------------------------------------------------------ 驳回 @Test