🐛 重新认证也要过内容校验,不再只校验新建车辆

起先 validateCertContent 只在 createVehicleForDriver 里调用,走 request.id 的重新认证路径
一句都不校验:待审内容虽然要等审核通过才写进车辆档案,但它已经是一条不可变记录,
并会在通过时按字段写回车辆行,于是「内容校验」对重新认证形同虚设。

- 内容校验提到 submitVehicleCert 里、resolveBoundVehicle 之前,两条路径共用;
- 「所属组织非空」拆成 requireOrganizationForNewVehicle,仍只在新车建档时要求:
  该列只在 INSERT 时需要,它属于内部管理归属,拿它拦住重新认证只会让司机无从下手。
- 补 2 个用例:重新认证传空「所有人」必须被拒;司机档案没有所属组织时重新认证仍应放行。
- 顺带把 7 个此前只传车牌/ID 的用例改用字段齐全的请求:否则它们会因为「车牌颜色不能为空」
  提前失败,看着还绿、其实已经不再验证原本要验证的越权与作废逻辑。
This commit is contained in:
2026-10-03 17:56:56 +08:00
parent 11e62c172c
commit ee135cd150
2 changed files with 74 additions and 28 deletions
@@ -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);