diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java index 9d8fb576..6f7e7876 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java @@ -40,9 +40,11 @@ import org.springblade.transport.pojo.vo.BaiduOcrResultVO; import org.springblade.transport.pojo.vo.TransportCertRecordVO; import org.springblade.transport.service.IDriverCertService; import org.springblade.transport.support.OcrTypeMapping; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -100,6 +102,13 @@ public class DriverAuthController extends BladeController { return R.data(driverCertService.submitVehicleCert(request)); } + @DeleteMapping("/vehicle/{id}") + @ApiOperationSupport(order = 5) + @Operation(summary = "删除车辆认证", description = "对车辆做逻辑删除;认证记录保留,不物理删除") + public R deleteVehicle(@PathVariable Long id) { + return R.status(driverCertService.deleteVehicleCert(id)); + } + @GetMapping("/vehicle/effective") @ApiOperationSupport(order = 5) @Operation(summary = "车辆认证生效值", description = "返回该车当前生效快照的内容") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java index f8b71437..7a76e5ff 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java @@ -71,6 +71,18 @@ public interface IDriverCertService { */ Long submitVehicleCert(VehicleCertSubmitRequest request); + /** + * 删除车辆认证(按逻辑删除处理) + *

+ * 只把车辆标记为已删除,**认证记录一律保留**——记录是不可变快照, + * 也是「曾通过即永久可用」这一判定的依据,物理删除会让历史无从追溯。 + * 只能删除本人绑定的车辆,越权拒绝。 + * + * @param vehicleId 车辆主键 + * @return 是否成功 + */ + boolean deleteVehicleCert(Long vehicleId); + /** * 读取某辆车当前生效的认证内容 * diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java index 072eafc9..d23127f2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java @@ -49,6 +49,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -127,6 +128,25 @@ public class DriverCertServiceImpl implements IDriverCertService { return record.getId(); } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteVehicleCert(Long vehicleId) { + if (Func.isEmpty(vehicleId)) { + throw new ServiceException("车辆主键不能为空"); + } + Driver driver = requireCurrentDriver(); + TransportVehicle vehicle = transportVehicleService.getById(vehicleId); + if (vehicle == null) { + throw new ServiceException("车辆不存在"); + } + // 越权校验:与提交链路同一套绑定判定 + if (!VehiclePlateSupport.isBound(driver.getDrivingVehicle(), vehicle.getPlateNo())) { + throw new ServiceException("无权删除该车辆的认证"); + } + // 逻辑删除车辆;认证记录**不删**——它是不可变快照,且是「曾通过即永久可用」的依据 + return transportVehicleService.deleteLogic(Collections.singletonList(vehicleId)); + } + @Override public VehicleCertSubmitRequest getEffectiveVehicleCert(Long vehicleId) { if (Func.isEmpty(vehicleId)) { diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java index 9a658c88..3b715c05 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java @@ -330,6 +330,45 @@ class DriverCertServiceImplTest { .selectRecordList(TransportCertRecord.BIZ_TYPE_DRIVER, DRIVER_ID); } + // ---------------------------------------------------------------- 删除车辆认证 + + @Test + @DisplayName("删除车辆认证:逻辑删除车辆,且**不删**认证记录") + void deleteVehicleCertIsLogicalOnly() { + when(driverAppService.currentDriverEntity()).thenReturn(driver(PLATE)); + when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, PLATE, 777L)); + + service.deleteVehicleCert(VEHICLE_ID); + + // 走的是 deleteLogic(is_deleted=1),不是物理删除 + verify(transportVehicleService).deleteLogic(java.util.Collections.singletonList(VEHICLE_ID)); + verify(transportVehicleService, never()).removeById(anyLong()); + // 认证记录是历史依据,任何情况下都不得被删 + verify(certRecordService, never()).removeById(anyLong()); + verify(certRecordService, never()).removeByIds(any()); + } + + @Test + @DisplayName("删除车辆认证:越权必须拒绝") + void deleteVehicleCertRejectsUnbound() { + when(driverAppService.currentDriverEntity()).thenReturn(driver(PLATE)); + when(transportVehicleService.getById(VEHICLE_ID)) + .thenReturn(vehicle(VEHICLE_ID, "桂B99999", null)); + + assertThrows(ServiceException.class, () -> service.deleteVehicleCert(VEHICLE_ID)); + verify(transportVehicleService, never()).deleteLogic(any()); + } + + @Test + @DisplayName("删除车辆认证:车牌写法差异不影响绑定判定") + void deleteVehicleCertAcceptsMessyPlate() { + when(driverAppService.currentDriverEntity()).thenReturn(driver("桂A·32523")); + when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, "桂a32523", null)); + + service.deleteVehicleCert(VEHICLE_ID); + verify(transportVehicleService).deleteLogic(java.util.Collections.singletonList(VEHICLE_ID)); + } + // ---------------------------------------------------------------- 辅助 private Driver driver(String drivingVehicle) {