diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Driver.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Driver.java
index 1d13e2ec..80137624 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Driver.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Driver.java
@@ -28,6 +28,9 @@ package org.springblade.transport.pojo.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -111,6 +114,21 @@ public class Driver extends TenantEntity {
*/
@Schema(description = "身份证反面照")
private String idCardBack;
+ /**
+ * 身份证有效期起
+ */
+ @Schema(description = "身份证有效期起")
+ private LocalDate idCardValidStart;
+ /**
+ * 身份证有效期止
+ */
+ @Schema(description = "身份证有效期止")
+ private LocalDate idCardValidEnd;
+ /**
+ * 身份证长期有效
+ */
+ @Schema(description = "身份证长期有效")
+ private Integer idCardLongTerm;
/**
* 大头照
*/
@@ -221,5 +239,31 @@ public class Driver extends TenantEntity {
*/
@Schema(description = "备注")
private String remark;
+ /**
+ * 最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废,null 表示从未提交
+ *
+ * 仅为列表展示而冗余,不是 可用性判据;「是否已认证」一律读生效快照指针。
+ */
+ @Schema(description = "最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废,null为从未提交")
+ private Integer certificationStatus;
+ /**
+ * 最新一次驳回原因展示缓存
+ */
+ @Schema(description = "最新一次驳回原因展示缓存")
+ private String certificationRejectReason;
+ /**
+ * 生效快照指针:指向 blade_transport_cert_record.id
+ *
+ * 「该司机是否已认证」的唯一 判据。为 null 表示从未认证通过;
+ * 一经设置永不回退 ——提交、驳回、作废都不得修改它(见 ADR-0002)。
+ *
+ * ⚠️ {@code READ_ONLY}:本字段只能由审核链路 (03)写入。
+ * 司机档案的 {@code /submit} 直接以本实体接收请求体,若允许反序列化,
+ * 调用方就能自行填一个记录 ID 把自己「认证」掉,绕过审核与 D10 的可用性门槛。
+ */
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "生效快照指针")
+ private Long effectiveCertRecordId;
}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportCertRecord.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportCertRecord.java
new file mode 100644
index 00000000..93d0aab7
--- /dev/null
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportCertRecord.java
@@ -0,0 +1,156 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+import java.io.Serial;
+import java.time.LocalDateTime;
+
+/**
+ * 认证记录实体类
+ *
+ * 认证对象(司机 / 车辆)每一次提交 所产生的、不可变 的快照。
+ * 记录一经创建,其 {@code snapshotJson} 与提交元信息永不修改;被同对象的后续提交取代时
+ * 标记为「已作废」,而不是 删除。判定某个对象「是否已认证」只看它持有的生效快照指针,
+ * 与记录表里的任何一条记录的状态都无关。
+ *
+ * 设计依据:{@code docs/adr/0001-certification-record-immutable-snapshot.md},
+ * 术语见根工作区 {@code GLOSSARY.md}。
+ *
+ * ⚠️ {@code status} 承载的是「审核状态」,不是 BladeX 的「启用 / 停用」。
+ * 该字段继承自 {@link TenantEntity},带来两条必须堵住的框架通道:
+ * 一是 {@code BaseServiceImpl#resolveEntity} 在插入时会把 {@code null} 的 {@code status}
+ * 静默填成 {@code 1} (恰好是 {@link #STATUS_APPROVED});
+ * 二是继承来的 {@code changeStatus(ids, 1)} 会直接写 {@code status},等于批量通过审核。
+ * 二者都已在 {@code TransportCertRecordServiceImpl} 中封堵(前者校验抛错、后者覆写抛错),
+ * 审核一律走 03 的审核接口。
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("blade_transport_cert_record")
+@Schema(description = "认证记录")
+public class TransportCertRecord extends TenantEntity {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 认证对象类型:司机
+ */
+ public static final String BIZ_TYPE_DRIVER = "driver";
+ /**
+ * 认证对象类型:车辆
+ */
+ public static final String BIZ_TYPE_VEHICLE = "vehicle";
+
+ /**
+ * 审核状态:审核中(即该对象的「待审快照」)
+ */
+ public static final Integer STATUS_PENDING = 0;
+ /**
+ * 审核状态:已通过(该对象最新一条已通过者即其「生效快照」)
+ */
+ public static final Integer STATUS_APPROVED = 1;
+ /**
+ * 审核状态:已驳回(审核的否定结论,需带驳回原因)
+ */
+ public static final Integer STATUS_REJECTED = 2;
+ /**
+ * 审核状态:已作废(被同对象的再次提交取代,非驳回)
+ */
+ public static final Integer STATUS_SUPERSEDED = 3;
+
+ /**
+ * 认证对象类型:driver-司机,vehicle-车辆
+ *
+ * {@code NEVER}:记录一经创建,认证对象的身份不可改(详见类注释的不变式 I3)。
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ @Schema(description = "认证对象类型:driver-司机,vehicle-车辆")
+ private String bizType;
+ /**
+ * 认证对象主键:司机ID 或 车辆ID(车辆按具体车辆,不是司机)
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "认证对象主键")
+ private Long bizId;
+ /**
+ * 展示键:司机姓名 / 车牌号,供审核列表直接展示
+ */
+ @Schema(description = "展示键:司机姓名 / 车牌号")
+ private String bizKey;
+ /**
+ * 该次提交的全量认证内容(JSON),一经创建不可修改
+ *
+ * {@code NEVER}:这是不变式 I3 的结构性保证——本字段永不进入 UPDATE 语句 ,
+ * 因此不存在「某条链路忘了保护快照」的可能。改内容只能靠新提交一条记录 。
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ @Schema(description = "该次提交的全量认证内容JSON")
+ private String snapshotJson;
+ /**
+ * 驳回原因,仅当审核状态为已驳回时有值
+ */
+ @Schema(description = "驳回原因")
+ private String rejectReason;
+ /**
+ * 提交时间
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ @Schema(description = "提交时间")
+ private LocalDateTime submitTime;
+ /**
+ * 提交人
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "提交人")
+ private Long submitUser;
+ /**
+ * 审核时间
+ */
+ @Schema(description = "审核时间")
+ private LocalDateTime auditTime;
+ /**
+ * 审核人
+ */
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "审核人")
+ private Long auditUser;
+
+}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportVehicle.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportVehicle.java
index 4829cc33..d18a3008 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportVehicle.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/TransportVehicle.java
@@ -26,6 +26,9 @@
package org.springblade.transport.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -224,5 +227,21 @@ public class TransportVehicle extends TenantEntity {
/** 认证驳回原因 */
@Schema(description = "认证驳回原因")
private String certificationRejectReason;
+ /**
+ * 生效快照指针:指向 blade_transport_cert_record.id
+ *
+ * 「该车辆是否已认证」的唯一 判据。为 null 表示从未认证通过;
+ * 一经设置永不回退 ——重新提交、待审、被驳回都不得修改它(见 ADR-0002)。
+ * 改造后 {@link #certificationStatus} 降级为「最新提交状态」的展示缓存。
+ *
+ * ⚠️ {@code READ_ONLY}:本字段只能由审核链路 (03)写入。
+ * {@code POST /transport-vehicle/submit} 直接以本实体接收请求体,
+ * 若允许反序列化,调用方就能自行填一个记录 ID 把自己「认证」掉,
+ * 从而完全绕过审核与 D10 的可用性门槛。
+ */
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "生效快照指针")
+ private Long effectiveCertRecordId;
}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportCertRecordVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportCertRecordVO.java
new file mode 100644
index 00000000..8275d7be
--- /dev/null
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportCertRecordVO.java
@@ -0,0 +1,74 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.pojo.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+
+import java.io.Serial;
+
+/**
+ * 认证记录视图实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "认证记录")
+public class TransportCertRecordVO extends TransportCertRecord {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建人姓名
+ */
+ @TableField(exist = false)
+ @Schema(description = "创建人姓名")
+ private String createUserName;
+ /**
+ * 更新人姓名
+ */
+ @TableField(exist = false)
+ @Schema(description = "更新人姓名")
+ private String updateUserName;
+ /**
+ * 提交人姓名
+ */
+ @TableField(exist = false)
+ @Schema(description = "提交人姓名")
+ private String submitUserName;
+ /**
+ * 审核人姓名
+ */
+ @TableField(exist = false)
+ @Schema(description = "审核人姓名")
+ private String auditUserName;
+
+}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml
index 45ad067b..89e87067 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml
@@ -46,6 +46,12 @@
+
+
+
+
+
+
@@ -70,6 +76,9 @@
posts,
id_card_front,
id_card_back,
+ id_card_valid_start,
+ id_card_valid_end,
+ id_card_long_term,
head_photo,
driving_type,
driving_license_no,
@@ -91,7 +100,10 @@
organization_name,
emergency_contact_name,
emergency_contact_mobile,
- remark
+ remark,
+ certification_status,
+ certification_reject_reason,
+ effective_cert_record_id
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportCertRecordMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportCertRecordMapper.java
new file mode 100644
index 00000000..fc627ea0
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportCertRecordMapper.java
@@ -0,0 +1,38 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+
+/**
+ * 认证记录 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface TransportCertRecordMapper extends BaseMapper {
+
+}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml
index c7411ab8..0faafb78 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml
@@ -48,6 +48,7 @@
+
@@ -96,6 +97,7 @@
remark
, certification_status
, certification_reject_reason
+ , effective_cert_record_id
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportCertRecordService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportCertRecordService.java
new file mode 100644
index 00000000..2d9559ad
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportCertRecordService.java
@@ -0,0 +1,62 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+import org.springblade.transport.pojo.vo.TransportCertRecordVO;
+
+import java.util.List;
+
+/**
+ * 认证记录 服务类
+ *
+ * 只承载认证记录本身的读写与数据模型不变式 的守护;
+ * 提交(02/05)、审核(03)、可用性门槛(04)等业务链路在各自的票中实现。
+ *
+ * @author Chill
+ */
+public interface ITransportCertRecordService extends BaseService {
+
+ /**
+ * 某认证对象的认证记录列表,按提交时间倒序
+ *
+ * @param bizType 认证对象类型:driver-司机,vehicle-车辆
+ * @param bizId 认证对象主键
+ * @return 认证记录列表
+ */
+ List selectRecordList(String bizType, Long bizId);
+
+ /**
+ * 取某认证对象当前的待审快照(至多一条)
+ *
+ * @param bizType 认证对象类型:driver-司机,vehicle-车辆
+ * @param bizId 认证对象主键
+ * @return 待审快照,无则返回 null
+ */
+ TransportCertRecord selectPendingRecord(String bizType, Long bizId);
+
+}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportCertRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportCertRecordServiceImpl.java
new file mode 100644
index 00000000..eae06347
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportCertRecordServiceImpl.java
@@ -0,0 +1,188 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.transport.mapper.TransportCertRecordMapper;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+import org.springblade.transport.pojo.vo.TransportCertRecordVO;
+import org.springblade.transport.service.ITransportCertRecordService;
+import org.springblade.transport.wrapper.TransportCertRecordWrapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 认证记录 服务实现类
+ *
+ * @author Chill
+ */
+@Service
+public class TransportCertRecordServiceImpl extends BaseServiceImpl implements ITransportCertRecordService {
+
+ /**
+ * 合法的审核状态取值
+ */
+ private static final Set VALID_STATUS = Set.of(
+ TransportCertRecord.STATUS_PENDING,
+ TransportCertRecord.STATUS_APPROVED,
+ TransportCertRecord.STATUS_REJECTED,
+ TransportCertRecord.STATUS_SUPERSEDED
+ );
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean save(TransportCertRecord transportCertRecord) {
+ validateRecord(transportCertRecord);
+ return super.save(transportCertRecord);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean saveBatch(Collection entityList, int batchSize) {
+ entityList.forEach(this::validateRecord);
+ return super.saveBatch(entityList, batchSize);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean saveOrUpdateBatch(Collection entityList, int batchSize) {
+ entityList.forEach(this::validateRecord);
+ return super.saveOrUpdateBatch(entityList, batchSize);
+ }
+
+ /**
+ * 禁用继承自 {@code BaseService} 的「修改状态」
+ *
+ * ⚠️ 本表的 {@code status} 是审核状态 ,不是「启用 / 停用」。
+ * 而 {@code BaseServiceImpl#changeStatus} 会绕过 {@link #validateRecord} 直接写 status,
+ * 调用 {@code changeStatus(ids, 1)} 即等于批量通过审核 ——
+ * 审核人、审核时间、驳回原因全部缺失,既破坏 I3 的留痕,也会凭空造出生效快照。
+ * 审核必须走 03 的审核接口(作用于单条记录、带留痕)。
+ *
+ * @param ids 主键集合
+ * @param status 状态
+ * @return 永不返回,直接抛出
+ */
+ @Override
+ public boolean changeStatus(List ids, Integer status) {
+ throw new ServiceException("认证记录不支持直接修改状态,请使用审核接口");
+ }
+
+ @Override
+ public List selectRecordList(String bizType, Long bizId) {
+ return list(recordQuery(bizType, bizId)).stream()
+ .map(TransportCertRecordWrapper.build()::entityVO)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public TransportCertRecord selectPendingRecord(String bizType, Long bizId) {
+ validateBizTarget(bizType, bizId);
+ return getOne(Wrappers.lambdaQuery()
+ .eq(TransportCertRecord::getBizType, bizType)
+ .eq(TransportCertRecord::getBizId, bizId)
+ .eq(TransportCertRecord::getStatus, TransportCertRecord.STATUS_PENDING)
+ .last("LIMIT 1"));
+ }
+
+ /**
+ * 认证对象检索条件:按提交时间倒序
+ *
+ * 排序用 {@code submit_time} 而非框架默认的 {@code create_time},是刻意为之:
+ * 「认证记录」的业务时间轴是提交 ,ticket 02 明确要求
+ * 「查询某辆车的认证记录列表:按提交时间倒序」。
+ *
+ * @param bizType 认证对象类型
+ * @param bizId 认证对象主键
+ * @return 检索条件
+ */
+ private LambdaQueryWrapper recordQuery(String bizType, Long bizId) {
+ validateBizTarget(bizType, bizId);
+ return Wrappers.lambdaQuery()
+ .eq(TransportCertRecord::getBizType, bizType)
+ .eq(TransportCertRecord::getBizId, bizId)
+ .orderByDesc(TransportCertRecord::getSubmitTime);
+ }
+
+ /**
+ * 认证对象标识校验
+ *
+ * @param bizType 认证对象类型
+ * @param bizId 认证对象主键
+ */
+ private void validateBizTarget(String bizType, Long bizId) {
+ if (Func.isEmpty(bizType) || Func.isEmpty(bizId)) {
+ throw new ServiceException("认证对象类型与主键不能为空");
+ }
+ }
+
+ /**
+ * 记录落库前的强制校验
+ *
+ * ⚠️ 这里防的是框架的一个静默 行为:{@code BaseServiceImpl#resolveEntity} 在插入时
+ * 若 {@code status} 为 {@code null},会填充 {@code BladeConstant.DB_STATUS_NORMAL}(值 1),
+ * 而 1 在本表恰好是 {@link TransportCertRecord#STATUS_APPROVED}。
+ * 那会让一条「忘了设状态」的提交凭空变成「已通过」,进而可能被当成生效快照。
+ * 故此处对空状态与非法取值直接拒绝,不依赖调用方自觉。
+ *
+ * @param transportCertRecord 待落库的认证记录
+ */
+ private void validateRecord(TransportCertRecord transportCertRecord) {
+ if (Func.isEmpty(transportCertRecord)) {
+ throw new ServiceException("认证记录不能为空");
+ }
+ if (Func.isEmpty(transportCertRecord.getBizType())) {
+ throw new ServiceException("认证对象类型不能为空");
+ }
+ if (Func.isEmpty(transportCertRecord.getBizId())) {
+ throw new ServiceException("认证对象主键不能为空");
+ }
+ if (Func.isEmpty(transportCertRecord.getSnapshotJson())) {
+ throw new ServiceException("认证内容快照不能为空");
+ }
+ Integer status = transportCertRecord.getStatus();
+ if (Func.isEmpty(status)) {
+ throw new ServiceException("认证记录状态不能为空,不允许依赖框架默认值(默认值 1 会被误判为已通过)");
+ }
+ if (!VALID_STATUS.contains(status)) {
+ throw new ServiceException("认证记录状态取值不正确");
+ }
+ if (TransportCertRecord.STATUS_REJECTED.equals(status) && Func.isEmpty(transportCertRecord.getRejectReason())) {
+ throw new ServiceException("请输入认证驳回原因");
+ }
+ }
+
+}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/TransportCertRecordWrapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/TransportCertRecordWrapper.java
new file mode 100644
index 00000000..d7e5171d
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/wrapper/TransportCertRecordWrapper.java
@@ -0,0 +1,57 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.wrapper;
+
+import org.springblade.core.mp.support.BaseEntityWrapper;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.system.cache.UserCache;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+import org.springblade.transport.pojo.vo.TransportCertRecordVO;
+
+import java.util.Objects;
+
+/**
+ * 认证记录包装类
+ *
+ * @author Chill
+ */
+public class TransportCertRecordWrapper extends BaseEntityWrapper {
+
+ public static TransportCertRecordWrapper build() {
+ return new TransportCertRecordWrapper();
+ }
+
+ @Override
+ public TransportCertRecordVO entityVO(TransportCertRecord transportCertRecord) {
+ TransportCertRecordVO transportCertRecordVO = Objects.requireNonNull(BeanUtil.copyProperties(transportCertRecord, TransportCertRecordVO.class));
+ transportCertRecordVO.setCreateUserName(UserCache.getUserRealName(transportCertRecord.getCreateUser()));
+ transportCertRecordVO.setUpdateUserName(UserCache.getUserRealName(transportCertRecord.getUpdateUser()));
+ transportCertRecordVO.setSubmitUserName(UserCache.getUserRealName(transportCertRecord.getSubmitUser()));
+ transportCertRecordVO.setAuditUserName(UserCache.getUserRealName(transportCertRecord.getAuditUser()));
+ return transportCertRecordVO;
+ }
+
+}
diff --git a/doc/sql/transport/blade_transport_cert_record_20261002.sql b/doc/sql/transport/blade_transport_cert_record_20261002.sql
new file mode 100644
index 00000000..b43c754d
--- /dev/null
+++ b/doc/sql/transport/blade_transport_cert_record_20261002.sql
@@ -0,0 +1,189 @@
+-- 认证记录数据模型(司机 / 车辆共用一张不可变快照表)
+--
+-- 背景与设计依据:
+-- docs/adr/0001-certification-record-immutable-snapshot.md(不可变快照 + 生效指针)
+-- docs/adr/0002-certification-does-not-block-usage.md(提交不影响可用性)
+-- 术语定义见根工作区 GLOSSARY.md
+--
+-- 本脚本只做 schema 变更,不含任何业务数据订正:
+-- 1. 新建 blade_transport_cert_record(认证记录,司机与车辆共用,biz_type 区分)
+-- 2. blade_transport_driver 补认证字段、生效快照指针、身份证有效期
+-- 3. blade_transport_vehicle 补生效快照指针
+-- 4. blade_transport_driver 排序规则订正为 utf8mb4_general_ci
+-- 5. 新增「认证审核-司机 / 车辆」菜单与权限码
+--
+-- 幂等:全部语句可重复执行(建表用 IF NOT EXISTS,加列先查 information_schema)。
+-- MySQL 5.7+ 兼容。
+--
+-- ⚠️ 生效快照指针(effective_cert_record_id)的语义:
+-- "该认证对象是否已认证"的**唯一**判据。为 NULL 表示从未通过审核。
+-- 一旦被指向,**永不回退为 NULL**——提交、驳回、作废都不得修改它。
+--
+-- ⚠️ 留给后续票的两条约束(本脚本无法用 DDL 表达):
+-- 1) 指针所指记录的 status 必须恒为「已通过」(1),且不得再被改动。
+-- 这正是不变式 I2 的后半句;由 03 的「已终态记录不可再次审核」保证。
+-- 2) 指针所指记录的 biz_type / biz_id 必须与持有它的对象一致。
+-- 由 03 写指针时保证(通过时用的是待审记录自身的 biz_id)。
+
+-- ---------------------------------------------------------------------------
+-- 1. 认证记录表
+-- ---------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `blade_transport_cert_record` (
+ `id` bigint(20) NOT NULL COMMENT '主键',
+ `tenant_id` varchar(12) NOT NULL DEFAULT '000000' COMMENT '租户ID',
+ `biz_type` varchar(20) NOT NULL COMMENT '认证对象类型:driver-司机,vehicle-车辆',
+ `biz_id` bigint(20) NOT NULL COMMENT '认证对象主键:司机ID或车辆ID(车辆按具体车辆,不是司机)',
+ `biz_key` varchar(100) DEFAULT NULL COMMENT '展示键:司机姓名/车牌号',
+ `status` int(11) NOT NULL COMMENT '审核状态:0审核中 1已通过 2已驳回 3已作废',
+ `snapshot_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '该次提交的全量认证内容JSON,一经创建不可修改',
+ `reject_reason` varchar(200) DEFAULT NULL COMMENT '驳回原因,仅状态为已驳回时有值',
+ `submit_time` datetime DEFAULT NULL COMMENT '提交时间',
+ `submit_user` bigint(20) DEFAULT NULL COMMENT '提交人',
+ `audit_time` datetime DEFAULT NULL COMMENT '审核时间',
+ `audit_user` bigint(20) DEFAULT NULL COMMENT '审核人',
+ `create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
+ `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime DEFAULT NULL COMMENT '修改时间',
+ `is_deleted` int(11) DEFAULT '0' COMMENT '是否已删除',
+ `pending_biz_id` bigint(20) GENERATED ALWAYS AS (if((`status` = 0) and (`is_deleted` = 0),`biz_id`,NULL)) STORED COMMENT '待审唯一键:仅未删除的审核中记录非空,用于保证同对象至多一条待审快照',
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE KEY `uk_cert_record_pending` (`tenant_id`,`biz_type`,`pending_biz_id`) USING BTREE,
+ KEY `idx_cert_record_biz` (`biz_type`,`biz_id`,`submit_time`) USING BTREE,
+ KEY `idx_cert_record_status` (`biz_type`,`status`,`submit_time`) USING BTREE,
+ KEY `idx_cert_record_submit_time` (`submit_time`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='认证记录';
+
+-- ---------------------------------------------------------------------------
+-- 2~4. 加列与排序规则订正
+-- ---------------------------------------------------------------------------
+DELIMITER $$
+
+DROP PROCEDURE IF EXISTS `upgrade_transport_cert_record_20261002`$$
+CREATE PROCEDURE `upgrade_transport_cert_record_20261002`()
+BEGIN
+ DECLARE db_name varchar(128);
+ SET db_name = DATABASE();
+
+ -- ---- 2. 司机表:认证字段 + 生效快照指针 ----
+ -- certification_status 是「最新提交状态」的展示缓存,不是可用性判据;
+ -- 初始为 NULL 表示「从未提交」,与「审核中(0)」语义不同。
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'certification_status') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `certification_status` int(11) DEFAULT NULL COMMENT '最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废,NULL为从未提交' AFTER `remark`;
+ END IF;
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'certification_reject_reason') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `certification_reject_reason` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最新一次驳回原因展示缓存' AFTER `certification_status`;
+ END IF;
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'effective_cert_record_id') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `effective_cert_record_id` bigint(20) DEFAULT NULL COMMENT '生效快照指针:指向 blade_transport_cert_record.id,NULL 表示从未认证通过;一经设置永不回退' AFTER `certification_reject_reason`;
+ END IF;
+
+ -- ---- 2.1 司机表:身份证有效期(小程序 person.vue 标为必填,原表缺列)----
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'id_card_valid_start') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `id_card_valid_start` date DEFAULT NULL COMMENT '身份证有效期起' AFTER `id_card_back`;
+ END IF;
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'id_card_valid_end') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `id_card_valid_end` date DEFAULT NULL COMMENT '身份证有效期止' AFTER `id_card_valid_start`;
+ END IF;
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND column_name = 'id_card_long_term') THEN
+ ALTER TABLE `blade_transport_driver` ADD COLUMN `id_card_long_term` int(11) DEFAULT '0' COMMENT '身份证长期有效:0否 1是' AFTER `id_card_valid_end`;
+ END IF;
+
+ -- ---- 3. 车辆表:生效快照指针 ----
+ -- 现有 certification_status / certification_reject_reason 保留,降级为「最新提交状态」展示缓存;
+ -- 可用性判定一律改读本指针(改造见 ticket 04)。
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = db_name AND table_name = 'blade_transport_vehicle' AND column_name = 'effective_cert_record_id') THEN
+ ALTER TABLE `blade_transport_vehicle` ADD COLUMN `effective_cert_record_id` bigint(20) DEFAULT NULL COMMENT '生效快照指针:指向 blade_transport_cert_record.id,NULL 表示从未认证通过;一经设置永不回退' AFTER `certification_reject_reason`;
+ END IF;
+
+ -- ---- 4. 司机表排序规则订正 ----
+ -- blade_transport_driver 建表未显式指定 COLLATE,MySQL 8+ 默认 utf8mb4_0900_ai_ci,
+ -- 与项目规范(utf8mb4_general_ci)不一致;blade_transport_vehicle 曾因此报
+ -- Illegal mix of collations,故一并订正,避免司机认证 JOIN 重演同一故障。
+ IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = db_name AND table_name = 'blade_transport_driver' AND table_collation <> 'utf8mb4_general_ci') THEN
+ ALTER TABLE `blade_transport_driver` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
+ END IF;
+
+ -- ---- 4.1 待审唯一键自修复 ----
+ -- 生成列必须同时排除「已逻辑删除」的行:MyBatis-Plus 的 removeById 只把 is_deleted 置 1,
+ -- 不会改 status。若生成列只看 status = 0,一条被逻辑删除的待审记录会继续占用唯一键,
+ -- 导致该认证对象**永远无法再提交**(ticket 02 明确要求车辆认证按逻辑删除处理)。
+ -- 建表语句已是正确版本;这里负责把按早期版本建过表的环境就地修正过来。
+ IF EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = db_name AND table_name = 'blade_transport_cert_record'
+ AND column_name = 'pending_biz_id' AND generation_expression NOT LIKE '%is_deleted%'
+ ) THEN
+ ALTER TABLE `blade_transport_cert_record`
+ MODIFY COLUMN `pending_biz_id` bigint(20) GENERATED ALWAYS AS (if((`status` = 0) and (`is_deleted` = 0),`biz_id`,NULL)) STORED COMMENT '待审唯一键:仅未删除的审核中记录非空,用于保证同对象至多一条待审快照';
+ END IF;
+
+ -- ---- 4.2 tenant_id 收紧为 NOT NULL ----
+ -- tenant_id 是待审唯一键 uk_cert_record_pending 的第一列,而 MySQL 的唯一索引**不对 NULL 去重**:
+ -- 若 tenant_id 允许为 NULL,两条 tenant_id IS NULL 的待审记录可以并存,I1 会被整个绕过。
+ -- 本库三张表(driver / vehicle / waybill)实测均无 tenant_id 为 NULL 的行,
+ -- 且「NOT NULL DEFAULT '000000'」是项目既有写法(doc/sql/transport 下 23 个脚本如此)。
+ IF EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = db_name AND table_name = 'blade_transport_cert_record'
+ AND column_name = 'tenant_id' AND is_nullable = 'YES'
+ ) THEN
+ UPDATE `blade_transport_cert_record` SET `tenant_id` = '000000' WHERE `tenant_id` IS NULL;
+ ALTER TABLE `blade_transport_cert_record`
+ MODIFY COLUMN `tenant_id` varchar(12) NOT NULL DEFAULT '000000' COMMENT '租户ID';
+ END IF;
+
+ -- ---- 4.3 车辆认证状态缓存词表对齐 ----
+ -- 改造前车辆侧是「0认证中 1认证通过 2认证驳回」,不含「已作废」;
+ -- 而 02/03 会把「被同对象再次提交取代」写成 3,届时该列无对应语义。
+ -- 取值 0/1/2 在两套词表下含义一致(进行中 / 通过 / 驳回),故这里只补 3 并统一措辞,
+ -- 不改变任何既有数据的含义。仅改列注释(元数据),不动数据。
+ IF EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = db_name AND table_name = 'blade_transport_vehicle'
+ AND column_name = 'certification_status' AND column_comment NOT LIKE '%已作废%'
+ ) THEN
+ ALTER TABLE `blade_transport_vehicle`
+ MODIFY COLUMN `certification_status` int(11) DEFAULT '0' COMMENT '最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废;可用性判定只看 effective_cert_record_id';
+ END IF;
+ IF EXISTS (
+ SELECT 1 FROM information_schema.columns
+ WHERE table_schema = db_name AND table_name = 'blade_transport_vehicle'
+ AND column_name = 'certification_reject_reason' AND column_comment NOT LIKE '%展示缓存%'
+ ) THEN
+ ALTER TABLE `blade_transport_vehicle`
+ MODIFY COLUMN `certification_reject_reason` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最新一次驳回原因展示缓存';
+ END IF;
+END$$
+
+CALL `upgrade_transport_cert_record_20261002`()$$
+DROP PROCEDURE `upgrade_transport_cert_record_20261002`$$
+
+DELIMITER ;
+
+-- ---------------------------------------------------------------------------
+-- 5. 菜单与权限码
+-- 挂在「运力管理」(2079000000000000001) 下,与司机管理 / 车辆管理同级。
+-- 权限码按 spec 约定:列表用 cert_certification_audit_{driver|vehicle},
+-- 审核动作用对应 _audit 后缀。
+-- ---------------------------------------------------------------------------
+INSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `component`, `remark`, `is_deleted`) VALUES
+(2079000000000000300, 2079000000000000001, 'cert_certification_audit_driver', '认证审核-司机', 'cert_certification_audit_driver', '/transportCapacity/cert-audit-driver', 'iconfont iconicon_doc', 4, 1, 0, 1, '', '司机认证待审列表', 0),
+(2079000000000000301, 2079000000000000300, 'cert_certification_audit_driver_audit', '审核', 'cert_certification_audit_driver_audit', '', '', 1, 2, 0, 1, NULL, '通过/驳回司机认证', 0),
+(2079000000000000310, 2079000000000000001, 'cert_certification_audit_vehicle', '认证审核-车辆', 'cert_certification_audit_vehicle', '/transportCapacity/cert-audit-vehicle', 'iconfont iconicon_doc', 5, 1, 0, 1, '', '车辆认证待审列表', 0),
+(2079000000000000311, 2079000000000000310, 'cert_certification_audit_vehicle_audit', '审核', 'cert_certification_audit_vehicle_audit', '', '', 1, 2, 0, 1, NULL, '通过/驳回车辆认证', 0)
+ON DUPLICATE KEY UPDATE
+ `parent_id` = VALUES(`parent_id`),
+ `code` = VALUES(`code`),
+ `name` = VALUES(`name`),
+ `alias` = VALUES(`alias`),
+ `path` = VALUES(`path`),
+ `source` = VALUES(`source`),
+ `sort` = VALUES(`sort`),
+ `category` = VALUES(`category`),
+ `action` = VALUES(`action`),
+ `is_open` = VALUES(`is_open`),
+ `component` = VALUES(`component`),
+ `remark` = VALUES(`remark`),
+ `is_deleted` = VALUES(`is_deleted`);
diff --git a/doc/sql/transport/blade_transport_driver.sql b/doc/sql/transport/blade_transport_driver.sql
index 57ccd4fc..d21eb393 100644
--- a/doc/sql/transport/blade_transport_driver.sql
+++ b/doc/sql/transport/blade_transport_driver.sql
@@ -17,6 +17,9 @@ CREATE TABLE `blade_transport_driver` (
`posts` varchar(100) DEFAULT NULL COMMENT '岗位,多个使用逗号分隔',
`id_card_front` varchar(1000) DEFAULT NULL COMMENT '身份证正面照',
`id_card_back` varchar(1000) DEFAULT NULL COMMENT '身份证反面照',
+ `id_card_valid_start` date DEFAULT NULL COMMENT '身份证有效期起',
+ `id_card_valid_end` date DEFAULT NULL COMMENT '身份证有效期止',
+ `id_card_long_term` int(11) DEFAULT '0' COMMENT '身份证长期有效:0否 1是',
`head_photo` varchar(1000) DEFAULT NULL COMMENT '大头照',
`driving_type` varchar(20) NOT NULL COMMENT '准驾车型',
`driving_license_no` varchar(50) DEFAULT NULL COMMENT '驾驶证档案编号',
@@ -39,6 +42,9 @@ CREATE TABLE `blade_transport_driver` (
`emergency_contact_name` varchar(20) NOT NULL COMMENT '紧急联系人姓名',
`emergency_contact_mobile` varchar(20) NOT NULL COMMENT '紧急联系人手机号',
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
+ `certification_status` int(11) DEFAULT NULL COMMENT '最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废,NULL为从未提交',
+ `certification_reject_reason` varchar(200) DEFAULT NULL COMMENT '最新一次驳回原因展示缓存',
+ `effective_cert_record_id` bigint(20) DEFAULT NULL COMMENT '生效快照指针:指向 blade_transport_cert_record.id,NULL 表示从未认证通过;一经设置永不回退',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
@@ -54,7 +60,7 @@ CREATE TABLE `blade_transport_driver` (
KEY `idx_transport_driver_license_end` (`driving_license_end_date`) USING BTREE,
KEY `idx_transport_driver_qualification_end` (`qualification_end_date`) USING BTREE,
KEY `idx_transport_driver_status` (`status`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='司机管理';
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='司机管理';
-- ----------------------------
-- Records of blade_menu for transport capacity driver
diff --git a/doc/sql/transport/blade_transport_vehicle.sql b/doc/sql/transport/blade_transport_vehicle.sql
index 3a5f3f22..43000c2b 100644
--- a/doc/sql/transport/blade_transport_vehicle.sql
+++ b/doc/sql/transport/blade_transport_vehicle.sql
@@ -39,8 +39,9 @@ CREATE TABLE `blade_transport_vehicle` (
`registration_date` date DEFAULT NULL COMMENT '机动车登记日期',
`registration_image` varchar(1000) DEFAULT NULL COMMENT '机动车登记本图片',
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
- `certification_status` int(11) DEFAULT '0' COMMENT '认证状态:0认证中 1认证通过 2认证驳回',
- `certification_reject_reason` varchar(200) DEFAULT NULL COMMENT '认证驳回原因',
+ `certification_status` int(11) DEFAULT '0' COMMENT '最新提交状态展示缓存:0审核中 1已通过 2已驳回 3已作废;可用性判定只看 effective_cert_record_id',
+ `certification_reject_reason` varchar(200) DEFAULT NULL COMMENT '最新一次驳回原因展示缓存',
+ `effective_cert_record_id` bigint(20) DEFAULT NULL COMMENT '生效快照指针:指向 blade_transport_cert_record.id,NULL 表示从未认证通过;一经设置永不回退',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',