diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PersonCertSubmitRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PersonCertSubmitRequest.java new file mode 100644 index 00000000..29f20d5c --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PersonCertSubmitRequest.java @@ -0,0 +1,90 @@ +/** + * 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.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; + +/** + * 司机端个人认证提交请求 + *
+ * 一次提交承载个人认证的**全部**内容(身份证 + 驾驶证 + 从业资格证), + * 三证一起提交、一起审核,不拆开。 + * + * @author Chill + */ +@Data +@Schema(description = "司机端个人认证提交请求") +public class PersonCertSubmitRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "真实姓名") + private String realName; + @Schema(description = "身份证号") + private String idCardNo; + @Schema(description = "身份证正面照") + private String idCardFrontUrl; + @Schema(description = "身份证反面照") + private String idCardBackUrl; + @Schema(description = "身份证有效期起") + private LocalDate idCardValidStart; + @Schema(description = "身份证有效期止") + private LocalDate idCardValidEnd; + @Schema(description = "身份证长期有效") + private Integer idCardLongTerm; + + @Schema(description = "驾驶证档案编号") + private String driverFileNo; + @Schema(description = "准驾车型") + private String driverClass; + @Schema(description = "驾驶证主页照片") + private String driverLicenseFrontUrl; + @Schema(description = "驾驶证副页照片") + private String driverLicenseBackUrl; + @Schema(description = "驾驶证有效期起") + private LocalDate driverValidStart; + @Schema(description = "驾驶证有效期止") + private LocalDate driverValidEnd; + @Schema(description = "驾驶证长期有效") + private Integer driverLongTerm; + + @Schema(description = "从业资格证号") + private String qualificationNo; + @Schema(description = "从业资格证照片") + private String qualificationUrl; + @Schema(description = "从业资格证有效期止") + private LocalDate qualificationValidEnd; + + @Schema(description = "大头照") + private String headPhoto; + +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/VehicleCertSubmitRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/VehicleCertSubmitRequest.java new file mode 100644 index 00000000..a05861a4 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/VehicleCertSubmitRequest.java @@ -0,0 +1,87 @@ +/** + * 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.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; + +/** + * 司机端车辆认证提交请求 + *
+ * 一次提交承载该车辆认证的**全部**内容(行驶证 + 道路运输证),两证一起提交、一起审核。 + * 提交只产生新的待审记录,不覆盖车辆当前已生效的认证内容。 + * + * @author Chill + */ +@Data +@Schema(description = "司机端车辆认证提交请求") +public class VehicleCertSubmitRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "车辆ID:新增时为空,重新认证时必填") + private Long id; + @Schema(description = "车牌号") + private String plateNo; + @Schema(description = "车辆类型") + private String vehicleType; + @Schema(description = "车辆识别代号(车架号)") + private String vin; + @Schema(description = "发动机号") + private String engineNo; + @Schema(description = "车牌颜色") + private String plateColor; + + @Schema(description = "行驶证档案编号") + private String licenseNo; + @Schema(description = "行驶证主页照片") + private String licenseFrontUrl; + @Schema(description = "行驶证副页照片") + private String licenseBackUrl; + @Schema(description = "行驶证有效期起") + private LocalDate licenseValidStart; + @Schema(description = "行驶证有效期止") + private LocalDate licenseValidEnd; + @Schema(description = "行驶证长期有效") + private Integer licenseLongTerm; + + @Schema(description = "道路运输证号") + private String roadTransportNo; + @Schema(description = "道路运输证照片") + private String roadTransportUrl; + @Schema(description = "道路运输证有效期起") + private LocalDate roadTransportValidStart; + @Schema(description = "道路运输证有效期止") + private LocalDate roadTransportValidEnd; + @Schema(description = "道路运输证长期有效") + private Integer roadTransportLongTerm; + +} 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 index 8275d7be..ad12a17e 100644 --- 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 @@ -70,5 +70,13 @@ public class TransportCertRecordVO extends TransportCertRecord { @TableField(exist = false) @Schema(description = "审核人姓名") private String auditUserName; + /** + * 状态中文文案:审核中 / 已通过 / 已驳回 / 已作废 + *
+ * 由服务层统一翻译后下发,避免小程序、Web、接口三处各造一套说法。 + */ + @TableField(exist = false) + @Schema(description = "状态中文文案") + private String statusText; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java new file mode 100644 index 00000000..708e02ab --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverAuthController.java @@ -0,0 +1,121 @@ +/** + * 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.controller; + +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.tool.api.R; +import org.springblade.transport.pojo.dto.PersonCertSubmitRequest; +import org.springblade.transport.pojo.dto.VehicleCertSubmitRequest; +import org.springblade.transport.pojo.vo.TransportCertRecordVO; +import org.springblade.transport.service.IDriverCertService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 司机端认证(小程序) + *
+ * 对外路径:{@code /api/blade-transport/driver/auth/**} + * 同时兼容未去前缀直连 {@code /blade-transport/driver/auth/**}。 + *
+ * 提交审核**不影响**该对象当前的可用性:待审内容只落认证记录,
+ * 审核通过前对外呈现的仍是旧的生效值。
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping({"/driver/auth", "/blade-transport/driver/auth"})
+@Tag(name = "司机端认证", description = "小程序司机个人认证与车辆认证")
+public class DriverAuthController extends BladeController {
+
+ private final IDriverCertService driverCertService;
+
+ // ------------------------------------------------------------ 个人认证
+
+ @PostMapping("/person")
+ @ApiOperationSupport(order = 1)
+ @Operation(summary = "提交个人认证", description = "三证(身份证+驾驶证+从业资格证)一起提交,生成待审记录")
+ public R
+ * 认证提交要写回司机表,且越权校验要读 {@code driving_vehicle},
+ * 故这里返回实体而非视图对象。解析顺序:先按登录手机号匹配,
+ * 匹配不到再按 {@code user_id} 兜底。
+ *
+ * @return 司机实体;未登录或查不到时返回 null
+ */
+ Driver currentDriverEntity();
+
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java
new file mode 100644
index 00000000..f8b71437
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IDriverCertService.java
@@ -0,0 +1,99 @@
+/**
+ * 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.transport.pojo.dto.PersonCertSubmitRequest;
+import org.springblade.transport.pojo.dto.VehicleCertSubmitRequest;
+import org.springblade.transport.pojo.vo.TransportCertRecordVO;
+
+import java.util.List;
+
+/**
+ * 司机端认证服务(小程序)
+ *
+ * 认证对象是「一事一议」的:司机的个人认证与每一辆车的车辆认证各自独立成立、
+ * 独立审核、独立留痕。本服务只负责司机端能做什么,管理端审核在审核服务里。
+ *
+ * @author Chill
+ */
+public interface IDriverCertService {
+
+ /**
+ * 提交个人认证(全量:身份证 + 驾驶证 + 从业资格证,三证一起审)
+ *
+ * 生成一条待审记录,并把本人前一条待审记录置为已作废。
+ * **不修改生效快照**:已认证的司机在本次审核通过前仍按旧生效值认定。
+ *
+ * @param request 个人认证内容
+ * @return 新生成的认证记录主键
+ */
+ Long submitPersonCert(PersonCertSubmitRequest request);
+
+ /**
+ * 读取本人当前生效的个人认证内容(供个人认证卡片展示)
+ *
+ * @return 生效快照内容;从未认证通过时返回 null
+ */
+ PersonCertSubmitRequest getEffectivePersonCert();
+
+ /**
+ * 提交车辆认证(新增或重新认证)
+ *
+ * 生成一条待审记录,并把该车前一条待审记录置为已作废。
+ * **不修改生效快照**:已认证的车辆在本次审核通过前仍按旧生效值认定,
+ * 因此提交审核不会影响车辆正常接单。
+ *
+ * @param request 车辆认证内容
+ * @return 新生成的认证记录主键
+ */
+ Long submitVehicleCert(VehicleCertSubmitRequest request);
+
+ /**
+ * 读取某辆车当前生效的认证内容
+ *
+ * @param vehicleId 车辆主键
+ * @return 生效快照内容;从未认证通过时返回 null
+ */
+ VehicleCertSubmitRequest getEffectiveVehicleCert(Long vehicleId);
+
+ /**
+ * 查询某认证对象的认证记录列表(按提交时间倒序)
+ *
+ * @param bizType 认证对象类型:driver-司机,vehicle-车辆
+ * @param bizId 认证对象主键
+ * @return 认证记录列表
+ */
+ List
+ * 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.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.transport.pojo.dto.PersonCertSubmitRequest;
+import org.springblade.transport.pojo.dto.VehicleCertSubmitRequest;
+import org.springblade.transport.pojo.entity.Driver;
+import org.springblade.transport.pojo.entity.TransportCertRecord;
+import org.springblade.transport.pojo.entity.TransportVehicle;
+import org.springblade.transport.pojo.vo.TransportCertRecordVO;
+import org.springblade.transport.service.IDriverAppService;
+import org.springblade.transport.service.IDriverCertService;
+import org.springblade.transport.service.IDriverService;
+import org.springblade.transport.service.ITransportCertRecordService;
+import org.springblade.transport.service.ITransportVehicleService;
+import org.springblade.transport.support.CertificationRuleSupport;
+import org.springblade.transport.support.VehiclePlateSupport;
+import org.springblade.transport.wrapper.TransportCertRecordWrapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 司机端认证服务实现
+ *
+ * 提交链路只做三件事:把本次提交的内容**原样**存成一条新的待审记录、
+ * 把该对象前一条待审记录置为已作废、刷新展示缓存。
+ *
+ * ⚠️ 提交**绝不**修改 {@code effective_cert_record_id},也**不覆盖**证件字段,
+ * 这正是「提交审核不影响车辆正常使用」的实现方式:待审内容只存在于记录表里,
+ * 在审核通过之前,该对象对外呈现的仍然是旧的生效值。
+ *
+ * @author Chill
+ */
+@Service
+@RequiredArgsConstructor
+public class DriverCertServiceImpl implements IDriverCertService {
+
+ private final IDriverAppService driverAppService;
+ private final IDriverService driverService;
+ private final ITransportVehicleService transportVehicleService;
+ private final ITransportCertRecordService transportCertRecordService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long submitPersonCert(PersonCertSubmitRequest request) {
+ if (Func.isEmpty(request)) {
+ throw new ServiceException("认证内容不能为空");
+ }
+ Driver driver = requireCurrentDriver();
+ String snapshotJson = JSON.toJSONString(request);
+
+ // 先作废本人上一条待审,再落新记录:任何时刻同一对象至多一条待审。
+ // 作废只动「待审」,已通过/已驳回都是终态,尤其不得碰生效快照指着的那条。
+ supersedePending(TransportCertRecord.BIZ_TYPE_DRIVER, driver.getId());
+
+ TransportCertRecord record = newRecord(
+ TransportCertRecord.BIZ_TYPE_DRIVER, driver.getId(),
+ Func.toStr(driver.getDriverName(), ""), snapshotJson, driver.getId());
+ transportCertRecordService.save(record);
+
+ // 展示缓存:让小程序卡片显示「审核中」。可用性判定不看这个字段。
+ updateDriverDisplayCache(driver.getId(), TransportCertRecord.STATUS_PENDING, null);
+ return record.getId();
+ }
+
+ @Override
+ public PersonCertSubmitRequest getEffectivePersonCert() {
+ Driver driver = requireCurrentDriver();
+ // 当前司机实体已加载过,直接读它身上的指针,不再回查一次
+ TransportCertRecord effective = findEffectiveRecord(driver.getEffectiveCertRecordId());
+ return effective == null
+ ? null
+ : JSON.parseObject(effective.getSnapshotJson(), PersonCertSubmitRequest.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long submitVehicleCert(VehicleCertSubmitRequest request) {
+ if (Func.isEmpty(request)) {
+ throw new ServiceException("认证内容不能为空");
+ }
+ Driver driver = requireCurrentDriver();
+ TransportVehicle vehicle = resolveBoundVehicle(driver, request);
+ String snapshotJson = JSON.toJSONString(request);
+
+ supersedePending(TransportCertRecord.BIZ_TYPE_VEHICLE, vehicle.getId());
+
+ TransportCertRecord record = newRecord(
+ TransportCertRecord.BIZ_TYPE_VEHICLE, vehicle.getId(),
+ Func.toStr(vehicle.getPlateNo(), ""), snapshotJson, driver.getId());
+ transportCertRecordService.save(record);
+
+ updateVehicleDisplayCache(vehicle.getId(), TransportCertRecord.STATUS_PENDING, null);
+ return record.getId();
+ }
+
+ @Override
+ public VehicleCertSubmitRequest getEffectiveVehicleCert(Long vehicleId) {
+ if (Func.isEmpty(vehicleId)) {
+ throw new ServiceException("车辆主键不能为空");
+ }
+ TransportVehicle vehicle = transportVehicleService.getById(vehicleId);
+ if (vehicle == null) {
+ throw new ServiceException("车辆不存在");
+ }
+ if (!VehiclePlateSupport.isBound(requireCurrentDriver().getDrivingVehicle(), vehicle.getPlateNo())) {
+ throw new ServiceException("无权查看该车辆的认证信息");
+ }
+ TransportCertRecord effective = findEffectiveRecord(vehicle.getEffectiveCertRecordId());
+ return effective == null
+ ? null
+ : JSON.parseObject(effective.getSnapshotJson(), VehicleCertSubmitRequest.class);
+ }
+
+ @Override
+ public List
+ * 状态必须显式设置为待审:框架在插入时会把空的 {@code status} 静默填成 1,
+ * 而 1 在本表是「已通过」,那会让一次提交凭空变成审核通过。
+ */
+ private TransportCertRecord newRecord(String bizType, Long bizId, String bizKey,
+ String snapshotJson, Long submitUser) {
+ TransportCertRecord record = new TransportCertRecord();
+ record.setBizType(bizType);
+ record.setBizId(bizId);
+ record.setBizKey(bizKey);
+ record.setSnapshotJson(snapshotJson);
+ record.setSubmitTime(LocalDateTime.now());
+ record.setSubmitUser(submitUser);
+ CertificationRuleSupport.prepareForSubmit(record);
+ return record;
+ }
+
+ /**
+ * 按生效快照指针取那条记录
+ *
+ * 只按指针查,不看记录表里任何一条的「最新状态」——这正是生效值与待审值分离的意义。
+ *
+ * @param effectiveRecordId 生效快照指针
+ * @return 生效快照记录;指针为空时返回 null
+ */
+ private TransportCertRecord findEffectiveRecord(Long effectiveRecordId) {
+ return Func.isEmpty(effectiveRecordId)
+ ? null
+ : transportCertRecordService.getById(effectiveRecordId);
+ }
+
+ /**
+ * 刷新司机侧的「最新提交状态」展示缓存
+ *
+ * 只写展示缓存,**不碰生效快照指针**。
+ */
+ private void updateDriverDisplayCache(Long driverId, Integer status, String rejectReason) {
+ Driver patch = new Driver();
+ patch.setId(driverId);
+ patch.setCertificationStatus(status);
+ patch.setCertificationRejectReason(rejectReason);
+ driverService.updateById(patch);
+ }
+
+ /**
+ * 刷新车辆侧的「最新提交状态」展示缓存
+ *
+ * 只写展示缓存,**不碰生效快照指针**,也不覆盖车辆当前的证件字段。
+ */
+ private void updateVehicleDisplayCache(Long vehicleId, Integer status, String rejectReason) {
+ TransportVehicle patch = new TransportVehicle();
+ patch.setId(vehicleId);
+ patch.setCertificationStatus(status);
+ patch.setCertificationRejectReason(rejectReason);
+ transportVehicleService.updateById(patch);
+ }
+
+ /**
+ * 解析本次提交指向的车辆,并校验它确实绑定在当前司机名下
+ *
+ * @param driver 当前司机
+ * @param request 提交内容
+ * @return 目标车辆
+ */
+ private TransportVehicle resolveBoundVehicle(Driver driver, VehicleCertSubmitRequest request) {
+ if (Func.isEmpty(driver.getDrivingVehicle())) {
+ throw new ServiceException("当前司机未绑定任何车辆");
+ }
+ TransportVehicle vehicle = Func.isEmpty(request.getId())
+ ? findByPlateWithinBound(driver, request.getPlateNo())
+ : transportVehicleService.getById(request.getId());
+ if (vehicle == null) {
+ throw new ServiceException("车辆不存在或未绑定到当前司机");
+ }
+ // 越权校验:无论按 id 还是按车牌取到,最后都核对一次绑定关系
+ if (!VehiclePlateSupport.isBound(driver.getDrivingVehicle(), vehicle.getPlateNo())) {
+ throw new ServiceException("无权操作该车辆的认证");
+ }
+ return vehicle;
+ }
+
+ private TransportVehicle findByPlateWithinBound(Driver driver, String plateNo) {
+ if (Func.isEmpty(plateNo)) {
+ throw new ServiceException("车牌号不能为空");
+ }
+ String normalized = VehiclePlateSupport.normalize(plateNo);
+ List
+ * 司机只能查自己的;车辆必须是本人绑定的。越权一律拒绝,不返回空列表
+ * ——空列表会让调用方以为「没有记录」,而不是「无权查看」。
+ */
+ private void requireReadableTarget(String bizType, Long bizId) {
+ if (Func.isEmpty(bizId)) {
+ throw new ServiceException("认证对象主键不能为空");
+ }
+ Driver driver = requireCurrentDriver();
+ if (TransportCertRecord.BIZ_TYPE_DRIVER.equals(bizType)) {
+ if (!bizId.equals(driver.getId())) {
+ throw new ServiceException("无权查看其他司机的认证记录");
+ }
+ return;
+ }
+ TransportVehicle vehicle = transportVehicleService.getById(bizId);
+ if (vehicle == null) {
+ throw new ServiceException("车辆不存在");
+ }
+ if (!VehiclePlateSupport.isBound(driver.getDrivingVehicle(), vehicle.getPlateNo())) {
+ throw new ServiceException("无权查看该车辆的认证记录");
+ }
+ }
+
+ private void requireValidBizType(String bizType) {
+ if (!CertificationRuleSupport.isValidBizType(bizType)) {
+ throw new ServiceException("认证对象类型不正确");
+ }
+ }
+
+ private Driver requireCurrentDriver() {
+ Driver driver = driverAppService.currentDriverEntity();
+ if (driver == null) {
+ throw new ServiceException("未找到当前登录用户对应的司机档案");
+ }
+ return driver;
+ }
+
+}
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
index d7e5171d..843a6a92 100644
--- 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
@@ -30,6 +30,7 @@ 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 org.springblade.transport.support.CertificationRuleSupport;
import java.util.Objects;
@@ -51,6 +52,8 @@ public class TransportCertRecordWrapper extends BaseEntityWrapper> recordList(@RequestParam String bizType,
+ @RequestParam Long bizId) {
+ return R.data(driverCertService.listRecords(bizType, bizId));
+ }
+
+ @GetMapping("/record/detail")
+ @ApiOperationSupport(order = 7)
+ @Operation(summary = "认证记录详情", description = "内容取自该次提交的完整快照")
+ public R