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 9d1c07dc..b7bb4e34 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
@@ -191,7 +191,6 @@ public class CertAuditServiceImpl implements ICertAuditService {
private void updateCertTarget(TransportCertRecord record, boolean pass, String rejectReason) {
boolean vehicleTarget = TransportCertRecord.BIZ_TYPE_VEHICLE.equals(record.getBizType());
Integer status = pass ? TransportCertRecord.STATUS_APPROVED : TransportCertRecord.STATUS_REJECTED;
- String reason = pass ? null : CertificationRuleSupport.reasonForReject(rejectReason);
// 只有通过才切指针;驳回时保持 null,框架的 not_null 更新策略会跳过该列
Long newEffectiveId = pass ? record.getId() : null;
@@ -199,22 +198,25 @@ public class CertAuditServiceImpl implements ICertAuditService {
TransportVehicle vehicle = new TransportVehicle();
vehicle.setId(record.getBizId());
vehicle.setCertificationStatus(status);
- vehicle.setCertificationRejectReason(reason);
if (pass) {
vehicle.setEffectiveCertRecordId(newEffectiveId);
// 通过即生效:把这次快照的证件内容一并写回车辆档案,让「车辆档案 = 生效值」成立。
// 不写回的话,审核通过之后管理端与司机端看到的仍是提交之前的旧值——
// 状态显示「已通过」、内容却还是旧的,是最容易让人误判的一种不一致。
- // 与指针合并成同一次 UPDATE,不额外多写一次同一行。
+ // 与指针合并成同一次 UPDATE。
applySnapshotToVehicle(vehicle, record.getSnapshotJson());
+ } else {
+ vehicle.setCertificationRejectReason(CertificationRuleSupport.reasonForReject(rejectReason));
}
requireTargetUpdated(transportVehicleService.updateById(vehicle), "车辆", record.getBizId());
+ if (pass) {
+ clearVehicleRejectReason(record.getBizId());
+ }
return;
}
Driver driver = new Driver();
driver.setId(record.getBizId());
driver.setCertificationStatus(status);
- driver.setCertificationRejectReason(reason);
// 必须带上 driving_vehicle 原值:该列是 FieldStrategy.ALWAYS,
// 更新时忽略 null 直接写,patch 里不带它就会把司机的车辆绑定清成 NULL,
// 而车辆列表正是按这一列匹配的(清空即「暂无绑定车辆」)。
@@ -224,10 +226,45 @@ public class CertAuditServiceImpl implements ICertAuditService {
// 通过即生效:与车辆侧同理,把这次快照的证件内容一并写回司机档案。
// 不写回的话,司机档案里始终是管理端当初录入的旧值(甚至为空),
// 而认证状态已是「已通过」——状态与内容不一致最容易让人误判。
- // 与指针合并成同一次 UPDATE,不额外多写一次同一行。
+ // 与指针合并成同一次 UPDATE。
applySnapshotToDriver(driver, record.getSnapshotJson());
+ } else {
+ driver.setCertificationRejectReason(CertificationRuleSupport.reasonForReject(rejectReason));
}
requireTargetUpdated(driverService.updateById(driver), "司机", record.getBizId());
+ if (pass) {
+ clearDriverRejectReason(record.getBizId());
+ }
+ }
+
+ /**
+ * 清空车辆档案上的驳回原因展示缓存
+ *
+ * 该列没有驳回原因时必须是 NULL(建表即 DEFAULT NULL)。但框架的 not_null 更新策略
+ * 会跳过 patch 里的 null 字段,交给 updateById 的 null 到不了 SQL,
+ * 「已通过」的车辆会一直挂着上一次的驳回文案,故单独发一条只改该列的语句。
+ * 它不碰生效快照指针。
+ *
+ * @param vehicleId 车辆主键
+ */
+ private void clearVehicleRejectReason(Long vehicleId) {
+ transportVehicleService.update(Wrappers.lambdaUpdate()
+ .eq(TransportVehicle::getId, vehicleId)
+ .set(TransportVehicle::getCertificationRejectReason, null));
+ }
+
+ /**
+ * 清空司机档案上的驳回原因展示缓存
+ *
+ * 与车辆侧同理:null 交给 updateById 到不了 SQL,只能单独发一条只改该列的语句。
+ * 它不碰生效快照指针,也不碰车辆绑定。
+ *
+ * @param driverId 司机主键
+ */
+ private void clearDriverRejectReason(Long driverId) {
+ driverService.update(Wrappers.lambdaUpdate()
+ .eq(Driver::getId, driverId)
+ .set(Driver::getCertificationRejectReason, null));
}
/**
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 7246529d..f172a547 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
@@ -376,6 +376,13 @@ public class DriverCertServiceImpl implements IDriverCertService {
patch.setDrivingVehicle(current.getDrivingVehicle());
}
driverService.updateById(patch);
+ if (Func.isBlank(rejectReason)) {
+ // 重新提交时驳回原因应为空,但框架的 not_null 更新策略会跳过 patch 里的 null,
+ // 只写 null 到不了 SQL,旧驳回文案会一直挂到审核通过,故单独清一次
+ driverService.update(Wrappers.lambdaUpdate()
+ .eq(Driver::getId, driverId)
+ .set(Driver::getCertificationRejectReason, null));
+ }
}
/**
@@ -389,6 +396,12 @@ public class DriverCertServiceImpl implements IDriverCertService {
patch.setCertificationStatus(status);
patch.setCertificationRejectReason(rejectReason);
transportVehicleService.updateById(patch);
+ if (Func.isBlank(rejectReason)) {
+ // 与司机侧同理:null 交给 updateById 到不了 SQL,旧驳回文案会一直挂着
+ transportVehicleService.update(Wrappers.lambdaUpdate()
+ .eq(TransportVehicle::getId, vehicleId)
+ .set(TransportVehicle::getCertificationRejectReason, null));
+ }
}
/**
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 503311a8..e570361d 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
@@ -1,5 +1,11 @@
package org.springblade.transport.service.impl;
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -14,7 +20,10 @@ import org.springblade.transport.service.IDriverService;
import org.springblade.transport.service.ITransportCertRecordService;
import org.springblade.transport.service.ITransportVehicleService;
+import java.util.Collection;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -43,6 +52,16 @@ class CertAuditServiceImplTest {
private IDriverService driverService;
private CertAuditServiceImpl service;
+ @BeforeAll
+ static void initTableInfo() {
+ // 构造 LambdaUpdateWrapper 需要 MyBatis-Plus 的实体元数据缓存
+ MybatisConfiguration configuration = new MybatisConfiguration();
+ MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
+ assistant.setCurrentNamespace("test");
+ TableInfoHelper.initTableInfo(assistant, Driver.class);
+ TableInfoHelper.initTableInfo(assistant, TransportVehicle.class);
+ }
+
@BeforeEach
void setUp() {
certRecordService = mock(ITransportCertRecordService.class);
@@ -330,6 +349,73 @@ class CertAuditServiceImplTest {
assertNull(detail.getEffectiveRecordId());
}
+ // -------------------------------------------- 驳回原因的展示缓存
+
+ @Test
+ @DisplayName("通过车辆认证:显式清空档案上的驳回原因(not_null 策略会让 null 到不了 SQL)")
+ @SuppressWarnings("unchecked")
+ void approveClearsStaleVehicleRejectReason() {
+ TransportCertRecord record = vehicleRecord(TransportCertRecord.STATUS_PENDING);
+ when(certRecordService.getById(RECORD_ID)).thenReturn(record);
+ when(transportVehicleService.getById(VEHICLE_ID)).thenReturn(vehicle(null));
+
+ service.audit(RECORD_ID, true, null);
+
+ ArgumentCaptor> captor = ArgumentCaptor.forClass(Wrapper.class);
+ verify(transportVehicleService).update(captor.capture());
+ LambdaUpdateWrapper wrapper =
+ (LambdaUpdateWrapper) captor.getValue();
+ assertTrue(wrapper.getSqlSet().contains("certification_reject_reason"),
+ "通过时必须显式清空驳回原因,实际: " + wrapper.getSqlSet());
+ Collection