🐛 修认证流程清空司机车辆绑定,导致车辆列表消失

Driver.drivingVehicle 标了 @TableField(updateStrategy = FieldStrategy.ALWAYS),
该策略忽略 null 直接写:任何一次 updateById 只要 patch 里没带这个字段,
就会把库里的 driving_vehicle 写成 NULL。而司机端的车辆列表正是按这一列
的车牌匹配出来的(DriverAppServiceImpl.myVehicles 开头就判空返回空列表),
清空即页面显示「暂无绑定车辆」,且司机在界面上无法自行恢复。

两条链路都会触发:
- 提交认证:updateDriverDisplayCache 只 patch id/状态/驳回原因;
- 审核通过:本次改动新增的快照写回,同样只 patch 证件字段。
已在两处 updateById 前把该车的原值搬进 patch,并补回归测试:
先确认去掉修复后测试确实失败(expected 桂A12345,桂B67890 but was null)。

保留 ALWAYS 策略不动——它保证了「能显式清空车辆绑定」这一能力,
问题在于 patch 式更新必须自己带上原值,而不是删掉策略。
This commit is contained in:
2026-10-04 23:24:22 +08:00
parent 4fa6d4c430
commit 7c4c887d74
3 changed files with 48 additions and 0 deletions
@@ -213,6 +213,10 @@ public class CertAuditServiceImpl implements ICertAuditService {
driver.setId(record.getBizId());
driver.setCertificationStatus(status);
driver.setCertificationRejectReason(reason);
// 必须带上 driving_vehicle 原值:该列是 FieldStrategy.ALWAYS,
// 更新时忽略 null 直接写,patch 里不带它就会把司机的车辆绑定清成 NULL,
// 而车辆列表正是按这一列匹配的(清空即「暂无绑定车辆」)。
copyDrivingVehicle(record.getBizId(), driver);
if (pass) {
driver.setEffectiveCertRecordId(newEffectiveId);
// 通过即生效:与车辆侧同理,把这次快照的证件内容一并写回司机档案。
@@ -224,6 +228,24 @@ public class CertAuditServiceImpl implements ICertAuditService {
driverService.updateById(driver);
}
/**
* 把司机当前的车辆绑定原值搬进 patch,避免被 ALWAYS 更新策略清空
* <p>
* {@code driving_vehicle} 上标了 {@code FieldStrategy.ALWAYS},任何一次
* {@code updateById} 都会把该列写成 patch 里的值(null 也照写)。
* 而司机端的车辆列表是按这一列的车牌匹配出来的,一旦被清空,
* 页面就变成「暂无绑定车辆」,且无法从界面上恢复。
*
* @param driverId 司机主键
* @param patch 待更新的司机 patch(就地写入原值)
*/
private void copyDrivingVehicle(Long driverId, Driver patch) {
Driver current = driverService.getById(driverId);
if (current != null) {
patch.setDrivingVehicle(current.getDrivingVehicle());
}
}
/**
* 把生效快照里的司机证件字段搬进司机档案(就地写入同一个 patch 对象)
* <p>
@@ -368,6 +368,13 @@ public class DriverCertServiceImpl implements IDriverCertService {
patch.setId(driverId);
patch.setCertificationStatus(status);
patch.setCertificationRejectReason(rejectReason);
// 必须带上 driving_vehicle 原值:该列标了 FieldStrategy.ALWAYS,
// 更新时忽略 null 直接写,patch 里不带它就会把司机的车辆绑定清成 NULL,
// 而司机端的车辆列表正是按这一列的车牌匹配出来的(清空即「暂无绑定车辆」)。
Driver current = driverService.getById(driverId);
if (current != null) {
patch.setDrivingVehicle(current.getDrivingVehicle());
}
driverService.updateById(patch);
}
@@ -155,6 +155,25 @@ class CertAuditServiceImplTest {
assertEquals(RECORD_ID, captor.getValue().getEffectiveCertRecordId());
}
@Test
@DisplayName("通过司机认证:patch 必须带上车辆绑定,不得把 driving_vehicle 清空")
void approveDriverKeepsDrivingVehicle() {
TransportCertRecord record = driverRecord(TransportCertRecord.STATUS_PENDING);
when(certRecordService.getById(RECORD_ID)).thenReturn(record);
Driver existing = driver(null);
existing.setDrivingVehicle("桂A12345,桂B67890");
when(driverService.getById(DRIVER_ID)).thenReturn(existing);
service.audit(RECORD_ID, true, null);
ArgumentCaptor<Driver> captor = ArgumentCaptor.forClass(Driver.class);
verify(driverService).updateById(captor.capture());
// driving_vehicle 标了 FieldStrategy.ALWAYS,patch 里为 null 就会把该列写成 NULL;
// 而司机端车辆列表正是按这一列匹配的,清空即「暂无绑定车辆」且界面上无法恢复
assertEquals("桂A12345,桂B67890", captor.getValue().getDrivingVehicle(),
"审核通过不得把司机的车辆绑定清空");
}
// ------------------------------------------------------------ 驳回
@Test