🐛 重新认证也要过内容校验,不再只校验新建车辆
起先 validateCertContent 只在 createVehicleForDriver 里调用,走 request.id 的重新认证路径 一句都不校验:待审内容虽然要等审核通过才写进车辆档案,但它已经是一条不可变记录, 并会在通过时按字段写回车辆行,于是「内容校验」对重新认证形同虚设。 - 内容校验提到 submitVehicleCert 里、resolveBoundVehicle 之前,两条路径共用; - 「所属组织非空」拆成 requireOrganizationForNewVehicle,仍只在新车建档时要求: 该列只在 INSERT 时需要,它属于内部管理归属,拿它拦住重新认证只会让司机无从下手。 - 补 2 个用例:重新认证传空「所有人」必须被拒;司机档案没有所属组织时重新认证仍应放行。 - 顺带把 7 个此前只传车牌/ID 的用例改用字段齐全的请求:否则它们会因为「车牌颜色不能为空」 提前失败,看着还绿、其实已经不再验证原本要验证的越权与作废逻辑。
This commit is contained in:
+25
-11
@@ -90,19 +90,14 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
/**
|
||||
* 司机端提交的车辆认证内容校验
|
||||
* <p>
|
||||
* 服务端必须独立校验一遍,不能依赖小程序的前端校验:前端可绕过,
|
||||
* 且这些字段里 {@code organization_name}、{@code approved_load_kg}、
|
||||
* {@code business_relation}、{@code energy_type}、{@code registration_no}
|
||||
* 都是 NOT NULL 且无默认值——缺一个这条 INSERT 就直接报数据库错误,
|
||||
* 用户看到的是一串英文异常,而不是「哪个字段没填」。
|
||||
* 服务端必须独立校验一遍,不能依赖小程序的前端校验:前端可绕过。
|
||||
* 校验发生在**落快照之前**——待审内容虽然要等审核通过才写进车辆档案,但它已经是一条
|
||||
* 不可变记录、并会在通过时按字段写回车辆行({@code applySnapshotToVehicle}),
|
||||
* 校验只做在「新建车辆」那一条路上,重新认证就成了绕过校验的后门。
|
||||
*
|
||||
* @param driver 当前司机
|
||||
* @param request 提交内容
|
||||
*/
|
||||
private void validateCertContent(Driver driver, VehicleCertSubmitRequest request) {
|
||||
if (Func.isEmpty(driver.getOrganizationName())) {
|
||||
throw new ServiceException("司机档案缺少所属组织,请联系管理员补充后再提交");
|
||||
}
|
||||
private void validateCertContent(VehicleCertSubmitRequest request) {
|
||||
requireText(request.getPlateColor(), "车牌颜色不能为空", SHORT_TEXT_MAX_LENGTH);
|
||||
requireText(request.getVehicleType(), "车辆类型不能为空", SHORT_TEXT_MAX_LENGTH);
|
||||
requireText(request.getOwnerName(), "所有人不能为空", OWNER_NAME_MAX_LENGTH);
|
||||
@@ -128,6 +123,21 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 建档前置条件:车辆行的 {@code organization_name} 是 NOT NULL 且无默认值,缺了这条 INSERT
|
||||
* 直接报「Field 'organization_name' doesn't have a default value」,用户看到的是一串英文异常。
|
||||
* <p>
|
||||
* 只在**新建车辆**时要求:重新认证不会写这一列(它由管理端维护,属于内部管理归属),
|
||||
* 拿它拦住重新认证只会让司机无从下手。
|
||||
*
|
||||
* @param driver 当前司机
|
||||
*/
|
||||
private void requireOrganizationForNewVehicle(Driver driver) {
|
||||
if (Func.isEmpty(driver.getOrganizationName())) {
|
||||
throw new ServiceException("司机档案缺少所属组织,请联系管理员补充后再提交");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本必填 + 长度校验
|
||||
*
|
||||
@@ -226,6 +236,9 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
throw new ServiceException("认证内容不能为空");
|
||||
}
|
||||
Driver driver = requireCurrentDriver();
|
||||
// 校验在 resolveBoundVehicle 之前:它既覆盖「新建车辆」,也覆盖「重新认证」——
|
||||
// 后者原先一点校验都不做,内容全靠审核环节兜底
|
||||
validateCertContent(request);
|
||||
TransportVehicle vehicle = resolveBoundVehicle(driver, request);
|
||||
String snapshotJson = JSON.toJSONString(request);
|
||||
|
||||
@@ -441,7 +454,8 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
*/
|
||||
private TransportVehicle createVehicleForDriver(Driver driver, VehicleCertSubmitRequest request,
|
||||
String normalizedPlate) {
|
||||
validateCertContent(driver, request);
|
||||
// 内容校验已在 submitVehicleCert 里统一做过;这里只剩「这条 INSERT 需要什么」的前置条件
|
||||
requireOrganizationForNewVehicle(driver);
|
||||
|
||||
TransportVehicle vehicle = new TransportVehicle();
|
||||
vehicle.setPlateNo(normalizedPlate);
|
||||
|
||||
+49
-17
@@ -73,9 +73,8 @@ class DriverCertServiceImplTest {
|
||||
when(certRecordService.selectPendingRecord(TransportCertRecord.BIZ_TYPE_VEHICLE, VEHICLE_ID))
|
||||
.thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
request.setPlateNo(PLATE);
|
||||
request.setVin("VIN123");
|
||||
|
||||
service.submitVehicleCert(request);
|
||||
@@ -99,9 +98,8 @@ class DriverCertServiceImplTest {
|
||||
when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, PLATE, null));
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest vehicleRequest = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest vehicleRequest = completeVehicleRequest(PLATE);
|
||||
vehicleRequest.setId(VEHICLE_ID);
|
||||
vehicleRequest.setPlateNo(PLATE);
|
||||
service.submitVehicleCert(vehicleRequest);
|
||||
|
||||
PersonCertSubmitRequest personRequest = new PersonCertSubmitRequest();
|
||||
@@ -129,7 +127,7 @@ class DriverCertServiceImplTest {
|
||||
.thenReturn(vehicle(VEHICLE_ID, PLATE, effectiveId));
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
service.submitVehicleCert(request);
|
||||
|
||||
@@ -151,7 +149,7 @@ class DriverCertServiceImplTest {
|
||||
when(certRecordService.selectPendingRecord(TransportCertRecord.BIZ_TYPE_VEHICLE, VEHICLE_ID))
|
||||
.thenReturn(previous);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
service.submitVehicleCert(request);
|
||||
|
||||
@@ -169,7 +167,7 @@ class DriverCertServiceImplTest {
|
||||
// selectPendingRecord 只返回待审;已通过的不该被它返回
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
service.submitVehicleCert(request);
|
||||
|
||||
@@ -185,7 +183,7 @@ class DriverCertServiceImplTest {
|
||||
when(transportVehicleService.getById(VEHICLE_ID))
|
||||
.thenReturn(vehicle(VEHICLE_ID, "桂B99999", null));
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.submitVehicleCert(request));
|
||||
@@ -200,7 +198,7 @@ class DriverCertServiceImplTest {
|
||||
when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, "桂a32523", null));
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest("桂a32523");
|
||||
request.setId(VEHICLE_ID);
|
||||
|
||||
service.submitVehicleCert(request);
|
||||
@@ -212,7 +210,7 @@ class DriverCertServiceImplTest {
|
||||
void submitVehicleRejectsWhenNoBoundVehicle() {
|
||||
when(driverAppService.currentDriverEntity()).thenReturn(driver(null));
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.submitVehicleCert(request));
|
||||
@@ -448,8 +446,7 @@ class DriverCertServiceImplTest {
|
||||
when(transportVehicleService.list(any(LambdaQueryWrapper.class)))
|
||||
.thenReturn(List.of(vehicle(VEHICLE_ID, "桂A32523", null))); // 已登记,但不在本人名下
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
request.setPlateNo("桂A32523");
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest("桂A32523");
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.submitVehicleCert(request));
|
||||
verify(transportVehicleService, never()).save(any(TransportVehicle.class));
|
||||
@@ -464,8 +461,7 @@ class DriverCertServiceImplTest {
|
||||
.thenReturn(List.of(vehicle(VEHICLE_ID, "桂A·32523", null))); // 写法不同但同一辆
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
request.setPlateNo("桂a32523");
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest("桂a32523");
|
||||
|
||||
service.submitVehicleCert(request);
|
||||
|
||||
@@ -480,14 +476,50 @@ class DriverCertServiceImplTest {
|
||||
void submitVehicleRejectsBlankPlate() {
|
||||
when(driverAppService.currentDriverEntity()).thenReturn(driver("桂A32523"));
|
||||
|
||||
VehicleCertSubmitRequest request = new VehicleCertSubmitRequest();
|
||||
request.setPlateNo(" ");
|
||||
// 内容给全,确保被拒的原因是车牌为空而不是别的字段(否则这条用例形同虚设)
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(" ");
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.submitVehicleCert(request));
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> service.submitVehicleCert(request));
|
||||
assertTrue(exception.getMessage().contains("车牌"), exception.getMessage());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 辅助
|
||||
|
||||
@Test
|
||||
@DisplayName("重新认证同样要过内容校验:只校验「新建车辆」等于给重新认证留了后门")
|
||||
void submitVehicleValidatesContentOnResubmit() {
|
||||
when(driverAppService.currentDriverEntity()).thenReturn(driver(PLATE));
|
||||
when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, PLATE, null));
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
request.setOwnerName("");
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> service.submitVehicleCert(request));
|
||||
assertEquals("所有人不能为空", exception.getMessage());
|
||||
verify(certRecordService, never()).save(any(TransportCertRecord.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("重新认证不要求司机档案有所属组织:那一列只在新建车辆时写,与管理归属无关")
|
||||
void submitVehicleResubmitDoesNotRequireOrganization() {
|
||||
Driver driver = driver(PLATE);
|
||||
driver.setOrganizationName(null);
|
||||
when(driverAppService.currentDriverEntity()).thenReturn(driver);
|
||||
when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(VEHICLE_ID, PLATE, null));
|
||||
when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null);
|
||||
|
||||
VehicleCertSubmitRequest request = completeVehicleRequest(PLATE);
|
||||
request.setId(VEHICLE_ID);
|
||||
|
||||
service.submitVehicleCert(request);
|
||||
|
||||
verify(certRecordService).save(any(TransportCertRecord.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("司机建车:字段不齐时给可读提示,而不是让数据库报 NOT NULL")
|
||||
void submitVehicleRejectsIncompleteRequest() {
|
||||
|
||||
Reference in New Issue
Block a user