From d74211fa7ee81a883c2de43d56326910de3dfc20 Mon Sep 17 00:00:00 2001 From: 18980591175 <470162950@qq.com> Date: Sat, 23 May 2026 10:14:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E4=BA=BA=E5=A4=9A=E8=BD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/DriverVehicleBindingApi.md | 242 ++++++++ docs/VehicleThreeInspectApi.md | 113 ++++ ...river_current_page_vehicle_id_20260522.sql | 2 + sql/alter_hot_driver_vehicle_ids_20260522.sql | 14 + .../callback/VehicleThreeInspectCallback.java | 4 + .../HotVehicleThreeInspectController.java | 13 + .../domain/HotVehicleThreeInspect.java | 5 + .../HotVehicleThreeInspectBatchAuditBo.java | 24 + .../domain/bo/HotVehicleThreeInspectBo.java | 5 + .../domain/vo/HotVehicleThreeInspectVo.java | 6 + .../IHotVehicleThreeInspectService.java | 25 + .../HotVehicleThreeInspectServiceImpl.java | 71 +++ .../controller/HotDriverController.java | 56 ++ .../driver/domain/HotDriver.java | 12 +- .../bo/HotDriverBatchBindVehicleBo.java | 25 + .../driver/domain/bo/HotDriverBo.java | 9 +- .../bo/HotDriverCurrentPageVehicleBo.java | 19 + .../driver/domain/vo/HotDriverVo.java | 14 +- .../driver/service/IHotDriverService.java | 25 + .../service/impl/HotDriverServiceImpl.java | 540 ++++++++++++++++-- .../impl/HotVehicleChangeServiceImpl.java | 74 ++- .../domain/vo/DriverBindingVehicleItemVo.java | 44 ++ .../vo/DriverBindingVehicleOptionsVo.java | 30 + .../service/IHotVehicleService.java | 20 + .../service/impl/HotVehicleServiceImpl.java | 215 +++++-- .../impl/HotVehicleInoutCheckServiceImpl.java | 5 + 26 files changed, 1494 insertions(+), 118 deletions(-) create mode 100644 docs/DriverVehicleBindingApi.md create mode 100644 docs/VehicleThreeInspectApi.md create mode 100644 sql/alter_hot_driver_current_page_vehicle_id_20260522.sql create mode 100644 sql/alter_hot_driver_vehicle_ids_20260522.sql create mode 100644 src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBatchAuditBo.java create mode 100644 src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBatchBindVehicleBo.java create mode 100644 src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverCurrentPageVehicleBo.java create mode 100644 src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleItemVo.java create mode 100644 src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleOptionsVo.java diff --git a/docs/DriverVehicleBindingApi.md b/docs/DriverVehicleBindingApi.md new file mode 100644 index 0000000..397abbe --- /dev/null +++ b/docs/DriverVehicleBindingApi.md @@ -0,0 +1,242 @@ +# 驾驶员车辆绑定改动说明 + +## 1. 本次改动概览 + +本次后端围绕驾驶员与车辆绑定,调整并新增以下能力: + +1. 三检流程未完成时,禁止进行车辆换绑操作。 +2. 三检流程未完成时,禁止新增出车检查。 +3. 新增查询接口:返回指定驾驶员已绑定车辆列表,以及所在公司仍可绑定的车辆列表。 +4. 新增查询接口:仅返回指定驾驶员已绑定车辆列表。 +5. 新增覆盖式批量绑定接口:前端传驾驶员ID和车辆ID串,后端先清空该驾驶员原有绑定,再绑定最新车辆列表。 +6. 新增追加式批量绑定接口:前端传驾驶员ID和车辆ID串,后端保留该驾驶员已有绑定,仅追加绑定新车辆。 +7. 驾驶员表保留单值字段 `vehicleId` 作为默认车辆ID。 +8. 驾驶员表新增字段 `vehicleIds`,用于存储已绑定车辆ID集合,多个以英文逗号分隔。 + +## 2. 三检限制规则 + +### 2.1 换绑限制 + +- 生效范围: + - 驾驶员编辑接口触发换绑时 + - 车辆编辑接口修改当前绑定驾驶员时 +- 限制规则: + - 若车辆当前存在未完成三检流程,则禁止换绑 +- 提示文案: + - `当前车辆三检流程未完成,不可进行换绑操作` + +### 2.2 新增出车检查限制 + +- 生效范围: + - 新增车辆进出场检查接口 +- 限制规则: + - 若车辆当前存在未完成三检流程,则不能新增出车检查 +- 提示文案: + - `当前车辆三检流程未完成,不能新增出车检查` + +### 2.3 未完成三检流程判定 + +- 判定口径: + - 查询车辆最新一条三检记录 + - 仅当最新记录满足以下两个条件时,才视为已完成: + - `inspectPhase = 3` + - `flowStatus` 为 `APPROVED` 或 `REJECTED` +- 其他情况均视为三检流程未完成 + +## 3. 查询驾驶员绑定车辆选项接口 + +### 3.1 接口信息 + +- 路径:`GET /driverManagement/driver/bindingVehicleOptions` +- 入参: + - `companyId`:公司ID + - `driverId`:驾驶员ID + +### 3.2 返回结构 + +返回 `data` 包含两个列表: + +- `boundVehicleList`:该驾驶员已绑定车辆列表 +- `bindableVehicleList`:该公司还可以绑定的车辆列表 + +列表项字段如下: + +- `vehicleId`:车辆ID +- `plateNumber`:车牌号 +- `currentDriverId`:当前绑定驾驶员ID +- `currentDriverName`:当前绑定驾驶员姓名 +- `vehicleType`:车辆类型 + +### 3.3 查询规则 + +- 已绑定车辆: + - 查询当前公司全部未删除车辆 + - 取 `currentDriver` 中包含当前 `driverId` 的车辆 +- 可绑定车辆: + - 当前公司 + - 未删除 + - `vehicleStatus = 1` + - `operationStatus = 1` + - `currentDriver` 为空 + +## 4. 驾驶员批量绑定车辆接口 + +### 4.1 接口信息 + +- 路径:`POST /driverManagement/driver/batchBindVehicles` +- 请求体: + +```json +{ + "driverId": "abc123", + "vehicleIds": "1001,1002,1003" +} +``` + +### 4.2 参数说明 + +- `driverId`:必填,驾驶员ID +- `vehicleIds`: + - 可为空 + - 多个车辆ID使用英文逗号分隔 + - 若为空,表示清空该驾驶员当前绑定车辆 + +### 4.3 业务规则 + +接口执行过程如下: + +1. 校验驾驶员是否存在。 +2. 根据驾驶员所属公司校验目标车辆是否存在且归属当前公司。 +3. 若原绑定车辆或目标绑定车辆存在未完成三检流程,则禁止批量绑定。 +4. 先将该驾驶员从原已绑定车辆中解绑。 +5. 再将目标车辆批量绑定到当前驾驶员。 +6. 同步回写驾驶员表中的: + - `vehicleId`:默认车辆ID,取当前绑定车辆中的主车辆 + - `vehicleIds`:全部绑定车辆ID集合 + - `plateNumber`:默认车辆对应车牌号 +7. 同步处理受影响的其他驾驶员车辆关系。 + +### 4.4 返回结果 + +- 成功:返回通用成功响应 +- 失败:抛出业务异常并返回对应提示 + +## 5. 查询驾驶员已绑定车辆列表接口 + +### 5.1 接口信息 + +- 路径:`GET /driverManagement/driver/boundVehicles` +- 入参: + - `companyId`:公司ID + - `driverId`:驾驶员ID + +### 5.2 返回字段 + +返回列表项字段如下: + +- `vehicleId`:车辆ID +- `plateNumber`:车牌号 +- `currentDriverId`:当前绑定驾驶员ID +- `currentDriverName`:当前绑定驾驶员姓名 +- `vehicleType`:车辆类型 + +## 6. 驾驶员追加批量绑定车辆接口 + +### 6.1 接口信息 + +- 路径:`POST /driverManagement/driver/appendBindVehicles` +- 请求体: + +```json +{ + "driverId": "abc123", + "vehicleIds": "1001,1002,1003" +} +``` + +### 6.2 业务规则 + +接口执行过程如下: + +1. 校验驾驶员是否存在。 +2. 根据驾驶员所属公司校验目标车辆是否存在且归属当前公司。 +3. 不取消该驾驶员现有绑定车辆。 +4. 仅将本次新增传入的车辆追加绑定给当前驾驶员。 +5. 若新增车辆当前已绑定其他驾驶员,则转绑到当前驾驶员,并同步受影响驾驶员关系。 +6. 若本次新增车辆存在未完成三检流程,则禁止绑定。 +7. 同步回写驾驶员表中的: + - `vehicleId` + - `vehicleIds` + - `plateNumber` + +## 7. 驾驶员解除绑定接口 + +### 7.1 接口信息 + +- 路径:`POST /driverManagement/driver/unbindVehicles` +- 请求体: + +```json +{ + "driverId": "abc123", + "vehicleIds": "1001,1002" +} +``` + +### 7.2 参数说明 + +- `driverId`:必填,驾驶员ID +- `vehicleIds`: + - 可为空 + - 多个车辆ID使用英文逗号分隔 + - 为空时表示清空该驾驶员当前全部绑定车辆 + - 有值时表示仅解绑指定车辆 + +### 7.3 业务规则 + +接口执行过程如下: + +1. 校验驾驶员是否存在。 +2. 根据驾驶员所属公司校验目标车辆是否存在且归属当前公司。 +3. 校验传入车辆当前确实绑定在该驾驶员名下,否则禁止解绑。 +4. 校验待解绑车辆当前不存在未完成三检流程,否则禁止解绑。 +5. 仅执行解绑,不触发换绑逻辑。 +6. 从该驾驶员当前绑定关系中移除指定车辆,或清空全部绑定。 +7. 同步回写驾驶员表中的: + - `vehicleId` + - `vehicleIds` + - `plateNumber` + +## 8. 绑定模型说明 + +- 当前车辆表实际是一车单人绑定,`currentDriver` 仅保留单个驾驶员ID。 +- 驾驶员表支持一个驾驶员绑定多辆车: + - `vehicleId`:默认车辆ID,单值 + - `vehicleIds`:全部绑定车辆ID集合,多值CSV + - `plateNumber`:默认车辆对应车牌号,单值 +- 因此批量绑定的核心逻辑是: + - 车辆侧单车只绑定一个驾驶员 + - 驾驶员侧允许关联多辆车 + - 移动端与司机端默认取 `vehicleId` 作为当前车辆 + +## 9. 数据库脚本 + +- 脚本文件:`sql/alter_hot_driver_vehicle_ids_20260522.sql` +- 脚本内容: + +```sql +ALTER TABLE hot_driver + ADD COLUMN vehicle_ids VARCHAR(1000) NULL COMMENT '已绑定车辆ID集合,多个以逗号分隔' AFTER vehicle_id; + +UPDATE hot_driver +SET vehicle_ids = NULLIF(TRIM(vehicle_id), '') +WHERE vehicle_ids IS NULL + AND vehicle_id IS NOT NULL; + +UPDATE hot_driver +SET vehicle_id = NULLIF(SUBSTRING_INDEX(vehicle_ids, ',', 1), ''), + plate_number = NULLIF(SUBSTRING_INDEX(plate_number, ',', 1), ''); + +ALTER TABLE hot_driver + DROP COLUMN current_page_vehicle_id; +``` diff --git a/docs/VehicleThreeInspectApi.md b/docs/VehicleThreeInspectApi.md new file mode 100644 index 0000000..4f8b27b --- /dev/null +++ b/docs/VehicleThreeInspectApi.md @@ -0,0 +1,113 @@ +# 车辆三检模块改动说明 + +## 1. 本次改动 + +本次对车辆三检模块新增了以下能力: + +1. 新增字段 `rejectReason`,表示审核不通过原因。 +2. 新增批量审核接口,管理员可以一键审核多条不同的车辆三检内容。 + +## 2. 字段变更 + +### 2.1 新增字段 + +- 字段名:`rejectReason` +- 含义:审核不通过原因 +- 生效场景:仅在审核不通过时有值 + +### 2.2 使用规则 + +- 当审核通过时: + - 后端自动将 `rejectReason` 置空 +- 当审核不通过时: + - 前端需要传入 `rejectReason` + - 若未传,后端会返回业务异常: + - `审核不通过时,不通过原因不能为空` + +### 2.3 数据库说明 + +车辆三检表需要新增字段: + +```sql +ALTER TABLE hot_vehicle_three_inspect +ADD COLUMN reject_reason varchar(500) NULL COMMENT '审核不通过原因'; +``` + +## 3. 单条审核接口调整 + +### 3.1 接口信息 + +- 路径:`POST /securityManagement/vehicleThreeInspect/audit` + +### 3.2 新增请求字段 + +- `rejectReason`:审核不通过原因 + +### 3.3 审核规则 + +- 当前仍以 `hasHiddenDanger` 作为是否通过的判断依据: + - `hasHiddenDanger = 0`:审核通过 + - `hasHiddenDanger = 1`:审核不通过 +- 审核不通过时必须传 `rejectReason` +- 审核通过时 `rejectReason` 不入库 + +## 4. 批量审核接口 + +### 4.1 接口信息 + +- 路径:`POST /securityManagement/vehicleThreeInspect/batchAudit` +- 说明:管理员可一次性审核多条不同三检记录 + +### 4.2 请求体 + +```json +{ + "auditItems": [ + { + "id": 101, + "taskId": "task_001", + "auditTime": "2026-05-22 10:00:00", + "hasHiddenDanger": 0, + "auditResult": "审核通过", + "auditorSignImgUrl": "12345" + }, + { + "id": 102, + "taskId": "task_002", + "auditTime": "2026-05-22 10:05:00", + "hasHiddenDanger": 1, + "auditResult": "发现问题需整改", + "rejectReason": "轮胎磨损超标", + "auditorSignImgUrl": "12345" + } + ] +} +``` + +### 4.3 请求字段说明 + +批量项沿用单条审核字段,核心包括: + +- `id`:三检记录ID +- `taskId`:流程任务ID +- `auditTime`:审核时间 +- `hasHiddenDanger`:是否存在隐患 +- `auditResult`:审核结论 +- `rejectReason`:审核不通过原因 +- `auditorSignImgUrl`:审核人签名 + +### 4.4 处理规则 + +接口会逐条执行以下逻辑: + +1. 校验三检记录ID和流程任务ID。 +2. 校验审核时间不能早于检查时间。 +3. 若传了审核签名,则校验签名归属。 +4. 根据 `hasHiddenDanger` 处理通过/不通过逻辑。 +5. 审核不通过时校验 `rejectReason` 必填。 +6. 更新三检记录并推进对应流程任务。 + +## 5. 影响范围 + +- 详情接口、列表接口、导出接口会返回 `rejectReason` 字段。 +- 审核驳回通知优先展示 `rejectReason`,没有时才回退展示 `auditResult`。 diff --git a/sql/alter_hot_driver_current_page_vehicle_id_20260522.sql b/sql/alter_hot_driver_current_page_vehicle_id_20260522.sql new file mode 100644 index 0000000..0f71c3a --- /dev/null +++ b/sql/alter_hot_driver_current_page_vehicle_id_20260522.sql @@ -0,0 +1,2 @@ +ALTER TABLE hot_driver + ADD COLUMN current_page_vehicle_id VARCHAR(64) NULL COMMENT '当前页面绑定车辆ID' AFTER plate_number; diff --git a/sql/alter_hot_driver_vehicle_ids_20260522.sql b/sql/alter_hot_driver_vehicle_ids_20260522.sql new file mode 100644 index 0000000..b35e1a3 --- /dev/null +++ b/sql/alter_hot_driver_vehicle_ids_20260522.sql @@ -0,0 +1,14 @@ +ALTER TABLE hot_driver + ADD COLUMN vehicle_ids VARCHAR(1000) NULL COMMENT '已绑定车辆ID集合,多个以逗号分隔' AFTER vehicle_id; + +UPDATE hot_driver +SET vehicle_ids = NULLIF(TRIM(vehicle_id), '') +WHERE vehicle_ids IS NULL + AND vehicle_id IS NOT NULL; + +UPDATE hot_driver +SET vehicle_id = NULLIF(SUBSTRING_INDEX(vehicle_ids, ',', 1), ''), + plate_number = NULLIF(SUBSTRING_INDEX(plate_number, ',', 1), ''); + +ALTER TABLE hot_driver + DROP COLUMN current_page_vehicle_id; diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java index b84989e..62b5c1e 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java @@ -135,6 +135,10 @@ public class VehicleThreeInspectCallback implements IFlowCallback { if (success) { return "您的车辆三检【" + phaseName + "】已审核通过,请及时查看。"; } + String rejectReason = StringUtils.trimToEmpty(inspect.getRejectReason()); + if (StringUtils.isNotBlank(rejectReason)) { + return "您的车辆三检【" + phaseName + "】未通过,不通过原因:" + rejectReason; + } String auditResult = StringUtils.trimToEmpty(inspect.getAuditResult()); if (StringUtils.isNotBlank(auditResult)) { return "您的车辆三检【" + phaseName + "】未通过,审核意见:" + auditResult; diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java index 90a1528..334cd4c 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java @@ -1,6 +1,7 @@ package com.hotwj.platform.config.vehicleThreeInspect.controller; import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBatchAuditBo; import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectPrintService; import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; @@ -144,6 +145,18 @@ public class HotVehicleThreeInspectController extends BaseController { return R.ok(); } + /** + * 批量审核车辆三检 + */ + @Log(title = "车辆三检批量审核", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchAudit") + @Operation(summary = "批量审核车辆三检") + public R batchAudit(@Validated @RequestBody HotVehicleThreeInspectBatchAuditBo bo) { + hotVehicleThreeInspectService.batchAudit(bo); + return R.ok(); + } + /** * 删除车辆三检 * diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java index edc637e..bb23334 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java @@ -116,6 +116,11 @@ public class HotVehicleThreeInspect extends BaseEntity { */ private String auditResult; + /** + * 审核不通过原因 + */ + private String rejectReason; + /** * 审核人员签名图片url */ diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBatchAuditBo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBatchAuditBo.java new file mode 100644 index 0000000..1e83d3f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBatchAuditBo.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.config.vehicleThreeInspect.domain.bo; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * 车辆三检批量审核业务对象 + * + * @author shihongwei + * @date 2026-05-22 + */ +@Data +public class HotVehicleThreeInspectBatchAuditBo { + + /** + * 批量审核明细 + */ + @Valid + @NotEmpty(message = "审核明细不能为空") + private List auditItems; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java index 5a05ec5..90d0fd5 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java @@ -126,6 +126,11 @@ public class HotVehicleThreeInspectBo extends BaseEntity { */ private String auditResult; + /** + * 审核不通过原因 + */ + private String rejectReason; + /** * 审核人员签名图片url */ diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java index 00a3f9f..1e5356d 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java @@ -132,6 +132,12 @@ public class HotVehicleThreeInspectVo implements Serializable { @ExcelProperty(value = "审核结论") private String auditResult; + /** + * 审核不通过原因 + */ + @ExcelProperty(value = "审核不通过原因") + private String rejectReason; + /** * 审核人员签名图片url */ diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java index 4d731cf..ea9080a 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java @@ -1,6 +1,7 @@ package com.hotwj.platform.config.vehicleThreeInspect.service; import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBatchAuditBo; import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; import org.dromara.common.mybatis.core.page.PageQuery; @@ -58,6 +59,23 @@ public interface IHotVehicleThreeInspectService { */ Boolean insertByBo(HotVehicleThreeInspectBo bo); + /** + * 判断车辆当前是否存在未完成的三检流程。 + * 未完成的定义为:最新一条三检记录不是已结束的收车检查。 + * + * @param vehicleId 车辆ID + * @return 是否存在未完成三检流程 + */ + boolean hasUnfinishedProcess(String vehicleId); + + /** + * 校验车辆当前不存在未完成的三检流程。 + * + * @param vehicleId 车辆ID + * @param actionMessage 失败提示文案 + */ + void validateNoUnfinishedProcess(String vehicleId, String actionMessage); + /** * 修改车辆三检 * @@ -81,4 +99,11 @@ public interface IHotVehicleThreeInspectService { * @param bo 业务对象 */ void audit(HotVehicleThreeInspectBo bo); + + /** + * 批量审核车辆三检 + * + * @param bo 批量审核业务对象 + */ + void batchAudit(HotVehicleThreeInspectBatchAuditBo bo); } diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java index 284f66a..8567b70 100644 --- a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.hotwj.platform.common.helper.DriverLoginContextHelper; import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBatchAuditBo; import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper; @@ -30,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; /** * 车辆三检Service业务层处理 @@ -60,6 +62,35 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec return hotVehicleMapper.selectAvailableForThreeInspect(companyId); } + @Override + public boolean hasUnfinishedProcess(String vehicleId) { + Long parsedVehicleId = parseVehicleId(vehicleId); + if (parsedVehicleId == null) { + return false; + } + HotVehicleThreeInspect latestInspect = baseMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotVehicleThreeInspect::getVehicleId, parsedVehicleId) + .eq(HotVehicleThreeInspect::getIsDeleted, 0L) + .orderByDesc(HotVehicleThreeInspect::getCreateTime) + .last("LIMIT 1") + ); + if (latestInspect == null) { + return false; + } + boolean finishedPhase = Long.valueOf(3L).equals(latestInspect.getInspectPhase()); + boolean finishedFlow = "APPROVED".equals(latestInspect.getFlowStatus()) + || "REJECTED".equals(latestInspect.getFlowStatus()); + return !(finishedPhase && finishedFlow); + } + + @Override + public void validateNoUnfinishedProcess(String vehicleId, String actionMessage) { + if (hasUnfinishedProcess(vehicleId)) { + throw new ServiceException(actionMessage); + } + } + /** * 查询车辆三检 * @@ -145,6 +176,7 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotVehicleThreeInspect::getDriverName, bo.getDriverName()); lqw.eq(bo.getInspectPhase() != null, HotVehicleThreeInspect::getInspectPhase, bo.getInspectPhase()); lqw.eq(bo.getInspectTime() != null, HotVehicleThreeInspect::getInspectTime, bo.getInspectTime()); + lqw.eq(bo.getInspectId() != null, HotVehicleThreeInspect::getInspectId, bo.getInspectId()); lqw.eq(bo.getAuditorId() != null, HotVehicleThreeInspect::getAuditorId, bo.getAuditorId()); lqw.like(StringUtils.isNotBlank(bo.getAuditorName()), HotVehicleThreeInspect::getAuditorName, bo.getAuditorName()); lqw.eq(StringUtils.isNotBlank(bo.getInspectResultJson()), HotVehicleThreeInspect::getInspectResultJson, bo.getInspectResultJson()); @@ -380,6 +412,21 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec @Override @Transactional(rollbackFor = Exception.class) public void audit(HotVehicleThreeInspectBo bo) { + doAudit(bo); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void batchAudit(HotVehicleThreeInspectBatchAuditBo bo) { + for (HotVehicleThreeInspectBo item : bo.getAuditItems()) { + if (item == null) { + continue; + } + doAudit(item); + } + } + + private void doAudit(HotVehicleThreeInspectBo bo) { // 1. 校验参数 if (bo.getTaskId() == null) { throw new ServiceException("流程任务ID不能为空"); @@ -396,6 +443,7 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec String auditorName = resolveAuditorName(bo); signatureVerifyService.validateSelfSignature(parseSignatureOssId(bo.getAuditorSignImgUrl()), auditorName); } + normalizeRejectReason(bo); // 2. 更新业务数据 HotVehicleThreeInspect update = MapstructUtils.convert(bo, HotVehicleThreeInspect.class); boolean flag = baseMapper.updateById(update) > 0; @@ -417,6 +465,18 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec } + private void normalizeRejectReason(HotVehicleThreeInspectBo bo) { + boolean rejected = Objects.equals(bo.getHasHiddenDanger(), 1L); + if (!rejected) { + bo.setRejectReason(null); + return; + } + bo.setRejectReason(StringUtils.trimToNull(bo.getRejectReason())); + if (StringUtils.isBlank(bo.getRejectReason())) { + throw new ServiceException("审核不通过时,不通过原因不能为空"); + } + } + private void validateAuditTimeNotBeforeInspectTime(HotVehicleThreeInspectBo bo, HotVehicleThreeInspect current) { java.util.Date auditTime = bo.getAuditTime(); if (auditTime == null) { @@ -440,4 +500,15 @@ public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspec } throw new ServiceException("审核人姓名不能为空"); } + + private Long parseVehicleId(String vehicleId) { + if (StringUtils.isBlank(vehicleId)) { + return null; + } + if (!vehicleId.matches("^\\d+$")) { + log.warn("车辆三检校验跳过非数字车辆ID vehicleId={}", vehicleId); + return null; + } + return Long.valueOf(vehicleId); + } } diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java b/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java index 06e5232..ab92d8c 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java @@ -3,6 +3,7 @@ package com.hotwj.platform.driverManagement.driver.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.date.DateUtil; import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBatchBindVehicleBo; import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverPasswordBo; import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; @@ -10,6 +11,8 @@ import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverLedgerExpor import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleItemVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleOptionsVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; import io.swagger.v3.oas.annotations.Operation; @@ -89,6 +92,26 @@ public class HotDriverController extends BaseController { return R.ok(hotDriverService.queryDriverCategoryStat(companyId)); } + /** + * 查询驾驶员已绑定车辆和公司可绑定车辆列表 + */ + @GetMapping("/bindingVehicleOptions") + @Operation(summary = "查询驾驶员已绑定车辆和公司可绑定车辆列表") + public R bindingVehicleOptions(@RequestParam Long companyId, + @RequestParam String driverId) { + return R.ok(hotVehicleService.queryDriverBindingVehicleOptions(companyId, driverId)); + } + + /** + * 查询驾驶员已绑定车辆列表 + */ + @GetMapping("/boundVehicles") + @Operation(summary = "查询驾驶员已绑定车辆列表") + public R> boundVehicles(@RequestParam Long companyId, + @RequestParam String driverId) { + return R.ok(hotVehicleService.queryDriverBoundVehicleList(companyId, driverId)); + } + /** * 导出驾驶员基本信息列表 */ @@ -253,6 +276,39 @@ public class HotDriverController extends BaseController { return toAjax(hotDriverService.updateByBo(bo)); } + /** + * 批量绑定驾驶员车辆 + */ + @Log(title = "驾驶员批量绑定车辆", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchBindVehicles") + @Operation(summary = "批量绑定驾驶员车辆") + public R batchBindVehicles(@Validated @RequestBody HotDriverBatchBindVehicleBo bo) { + return toAjax(hotDriverService.batchBindVehicles(bo)); + } + + /** + * 追加批量绑定驾驶员车辆 + */ + @Log(title = "驾驶员追加批量绑定车辆", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/appendBindVehicles") + @Operation(summary = "追加批量绑定驾驶员车辆") + public R appendBindVehicles(@Validated @RequestBody HotDriverBatchBindVehicleBo bo) { + return toAjax(hotDriverService.appendBindVehicles(bo)); + } + + /** + * 解除驾驶员车辆绑定 + */ + @Log(title = "驾驶员解除车辆绑定", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/unbindVehicles") + @Operation(summary = "解除驾驶员车辆绑定") + public R unbindVehicles(@Validated @RequestBody HotDriverBatchBindVehicleBo bo) { + return toAjax(hotDriverService.unbindVehicles(bo)); + } + /** * 企业负责人修改驾驶员登录密码 * diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java index 5941269..c1a5875 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java @@ -34,17 +34,25 @@ public class HotDriver extends BaseEntity { private Long companyId; /** - * 车辆Id + * 默认车辆ID */ @TableField(updateStrategy = FieldStrategy.ALWAYS) private String vehicleId; /** - * 车牌号 + * 已绑定车辆ID集合,多个以逗号分隔 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String vehicleIds; + + /** + * 默认车牌号 */ @TableField(updateStrategy = FieldStrategy.ALWAYS) private String plateNumber; + + /** * 入职日期 */ diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBatchBindVehicleBo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBatchBindVehicleBo.java new file mode 100644 index 0000000..0150ae5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBatchBindVehicleBo.java @@ -0,0 +1,25 @@ +package com.hotwj.platform.driverManagement.driver.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 驾驶员批量绑定车辆请求对象 + * + * @author shihongwei + * @date 2026-05-22 + */ +@Data +public class HotDriverBatchBindVehicleBo { + + /** + * 驾驶员ID + */ + @NotBlank(message = "驾驶员ID不能为空") + private String driverId; + + /** + * 车辆ID串,多个以逗号分隔;为空时表示清空当前绑定 + */ + private String vehicleIds; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java index 3e975ac..a710ea1 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java @@ -42,12 +42,17 @@ public class HotDriverBo extends BaseEntity { private Long companyId; /** - * 车辆Id + * 默认车辆ID */ private String vehicleId; /** - * 车牌号 + * 已绑定车辆ID集合,多个以逗号分隔 + */ + private String vehicleIds; + + /** + * 默认车牌号 */ private String plateNumber; diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverCurrentPageVehicleBo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverCurrentPageVehicleBo.java new file mode 100644 index 0000000..f1b85a4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverCurrentPageVehicleBo.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.driverManagement.driver.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 已废弃的历史请求对象,保留文件仅用于兼容历史代码引用。 + */ +@Data +@Deprecated +public class HotDriverCurrentPageVehicleBo { + + /** + * 驾驶员ID + */ + @NotBlank(message = "驾驶员ID不能为空") + private String driverId; + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java index 272810b..30c113d 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java @@ -41,15 +41,25 @@ public class HotDriverVo implements Serializable { private Long companyId; /** - * 车辆Id + * 默认车辆ID */ private String vehicleId; /** - * 车牌号 + * 已绑定车辆ID集合,多个以逗号分隔 + */ + private String vehicleIds; + + /** + * 默认车牌号 */ private String plateNumber; + /** + * vehicleIds 对应的车牌号集合,多个以逗号分隔 + */ + private String vehiclePlateNumbers; + /** * 入职日期 */ diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java b/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java index c028238..90c3829 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java @@ -1,6 +1,7 @@ package com.hotwj.platform.driverManagement.driver.service; import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBatchBindVehicleBo; import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; @@ -59,6 +60,30 @@ public interface IHotDriverService { */ Boolean updateByBo(HotDriverBo bo); + /** + * 批量绑定驾驶员车辆 + * + * @param bo 绑定信息 + * @return 是否成功 + */ + Boolean batchBindVehicles(HotDriverBatchBindVehicleBo bo); + + /** + * 追加批量绑定驾驶员车辆 + * + * @param bo 绑定信息 + * @return 是否成功 + */ + Boolean appendBindVehicles(HotDriverBatchBindVehicleBo bo); + + /** + * 解除驾驶员车辆绑定 + * + * @param bo 解绑信息 + * @return 是否成功 + */ + Boolean unbindVehicles(HotDriverBatchBindVehicleBo bo); + /** * 审核驾驶员账号 * diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java index a03e6da..94bb84e 100644 --- a/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.hotwj.platform.common.utils.PasswordUtils; import com.hotwj.platform.driverManagement.driver.domain.HotDriver; import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBatchBindVehicleBo; import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; @@ -36,6 +37,7 @@ import com.hotwj.platform.driverManagement.inviteLog.mapper.HotDriverInviteLogMa import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; @@ -93,6 +95,7 @@ public class HotDriverServiceImpl implements IHotDriverService { private final HotDriverSkillAssessmentMapper skillAssessmentMapper; private final HotDriverAnnualAssessmentMapper annualAssessmentMapper; private final IHotNoticeSignDocumentService noticeSignDocumentService; + private final IHotVehicleThreeInspectService hotVehicleThreeInspectService; /** * 查询驾驶员基本信息 @@ -102,7 +105,9 @@ public class HotDriverServiceImpl implements IHotDriverService { */ @Override public HotDriverVo queryById(String id) { - return baseMapper.selectVoById(id); + HotDriverVo vo = baseMapper.selectVoById(id); + fillVehiclePlateNumbers(Collections.singletonList(vo)); + return vo; } @Override @@ -209,6 +214,7 @@ public class HotDriverServiceImpl implements IHotDriverService { public TableDataInfo queryPageList(HotDriverBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + fillVehiclePlateNumbers(result.getRecords()); return TableDataInfo.build(result); } @@ -221,7 +227,42 @@ public class HotDriverServiceImpl implements IHotDriverService { @Override public List queryList(HotDriverBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + List list = baseMapper.selectVoList(lqw); + fillVehiclePlateNumbers(list); + return list; + } + + private void fillVehiclePlateNumbers(Collection drivers) { + if (CollUtil.isEmpty(drivers)) { + return; + } + Set vehicleIds = new LinkedHashSet<>(); + for (HotDriverVo driver : drivers) { + if (driver == null) { + continue; + } + vehicleIds.addAll(splitCsv(driver.getVehicleIds())); + } + if (CollUtil.isEmpty(vehicleIds)) { + return; + } + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .in(HotVehicle::getId, vehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + Map vehiclePlateMap = vehicles.stream() + .filter(Objects::nonNull) + .filter(vehicle -> StringUtils.isNotBlank(vehicle.getId())) + .collect(Collectors.toMap(HotVehicle::getId, HotVehicle::getPlateNumber, (left, right) -> left)); + for (HotDriverVo driver : drivers) { + if (driver == null) { + continue; + } + String joinedPlateNumbers = splitCsv(driver.getVehicleIds()).stream() + .map(vehiclePlateMap::get) + .filter(StringUtils::isNotBlank) + .collect(Collectors.joining(",")); + driver.setVehiclePlateNumbers(StringUtils.isNotBlank(joinedPlateNumbers) ? joinedPlateNumbers : null); + } } private LambdaQueryWrapper buildQueryWrapper(HotDriverBo bo) { @@ -737,54 +778,21 @@ public class HotDriverServiceImpl implements IHotDriverService { removeDriverPortFromUser(oldDriver.getPhone(), oldDriver.getCompanyId()); } - // 处理车牌号变更:同步车辆信息的当前驾驶员 - String oldPlate = oldDriver != null ? oldDriver.getPlateNumber() : null; - String newPlate = update.getPlateNumber(); - if (!StringUtils.equals(oldPlate, newPlate)) { - // 1. 如果旧车牌存在,从旧车辆的当前驾驶员列表中移除当前司机 - if (StringUtils.isNotBlank(oldPlate)) { - HotVehicle oldVehicle = vehicleMapper.selectOne(new LambdaQueryWrapper() - .eq(HotVehicle::getPlateNumber, oldPlate) - .eq(HotVehicle::getCompanyId, update.getCompanyId())); - - if (oldVehicle != null && StringUtils.isNotBlank(oldVehicle.getCurrentDriver())) { - List driverList = new ArrayList<>(Arrays.asList(oldVehicle.getCurrentDriver().split(","))); - if (driverList.remove(driverId)) { - String newDriverStr = String.join(",", driverList); - // 如果结果为空字符串,设为null或空 - vehicleMapper.update(null, - new LambdaUpdateWrapper() - .set(HotVehicle::getCurrentDriver, StringUtils.isBlank(newDriverStr) ? null : newDriverStr) - .eq(HotVehicle::getId, oldVehicle.getId())); - } - } - } - - // 2. 如果新车牌存在,将当前司机追加到新车辆的当前驾驶员列表 - if (StringUtils.isNotBlank(newPlate)) { - HotVehicle newVehicle = vehicleMapper.selectOne(new LambdaQueryWrapper() - .eq(HotVehicle::getPlateNumber, newPlate) - .eq(HotVehicle::getCompanyId, update.getCompanyId())); - - if (newVehicle != null) { - String currentDriverStr = newVehicle.getCurrentDriver(); - List driverList = StringUtils.isBlank(currentDriverStr) - ? new ArrayList<>() - : new ArrayList<>(Arrays.asList(currentDriverStr.split(","))); - - // 避免重复添加 - if (!driverList.contains(driverId)) { - driverList.add(driverId); - String newDriverStr = String.join(",", driverList); - vehicleMapper.update(null, - new LambdaUpdateWrapper() - .set(HotVehicle::getCurrentDriver, newDriverStr) - .eq(HotVehicle::getId, newVehicle.getId())); - } - } - } + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driverId); + if (oldDriver != null) { + impactedDriverIds.addAll(findOtherDriversBoundToVehicles(companyId, getBoundVehicleIds(oldDriver), driverId)); } + boolean bindingSpecified = bo.getVehicleIds() != null || bo.getVehicleId() != null || bo.getPlateNumber() != null; + if (bindingSpecified) { + List targetVehicles = resolveDriverTargetVehicles(bo, companyId); + validateVehiclesCanChangeBinding(oldDriver == null ? null : getBoundVehicleIds(oldDriver), targetVehicles); + impactedDriverIds.addAll(syncVehicleBindingsForDriver(driverId, companyId, targetVehicles)); + } + + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + // 同步最新的驾驶员信息到用户表(包含状态变更、基本信息变更) HotDriver newDriver = baseMapper.selectById(driverId); syncDriverToSysUser(newDriver, null); @@ -833,6 +841,137 @@ public class HotDriverServiceImpl implements IHotDriverService { return flag; } + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchBindVehicles(HotDriverBatchBindVehicleBo bo) { + HotDriver driver = baseMapper.selectById(bo.getDriverId()); + if (driver == null || Long.valueOf(1L).equals(driver.getIsDeleted())) { + throw new ServiceException("驾驶员不存在"); + } + Long companyId = driver.getCompanyId(); + if (companyId == null) { + throw new ServiceException("驾驶员所属公司不能为空"); + } + + List currentVehicleIds = getBoundVehicleIds(driver); + List targetVehicleIds = splitCsv(bo.getVehicleIds()); + List targetVehicles = new ArrayList<>(); + if (CollUtil.isNotEmpty(targetVehicleIds)) { + targetVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, targetVehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(targetVehicleIds, targetVehicles, true); + } + + validateVehiclesCanChangeBinding(currentVehicleIds, targetVehicles); + + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driver.getId()); + impactedDriverIds.addAll(findOtherDriversBoundToVehicles(companyId, currentVehicleIds, driver.getId())); + impactedDriverIds.addAll(syncVehicleBindingsForDriver(driver.getId(), companyId, targetVehicles)); + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean appendBindVehicles(HotDriverBatchBindVehicleBo bo) { + HotDriver driver = baseMapper.selectById(bo.getDriverId()); + if (driver == null || Long.valueOf(1L).equals(driver.getIsDeleted())) { + throw new ServiceException("驾驶员不存在"); + } + Long companyId = driver.getCompanyId(); + if (companyId == null) { + throw new ServiceException("驾驶员所属公司不能为空"); + } + + List currentVehicleIds = getBoundVehicleIds(driver); + List appendVehicleIds = splitCsv(bo.getVehicleIds()); + if (CollUtil.isEmpty(appendVehicleIds)) { + return true; + } + + List appendVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, appendVehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(appendVehicleIds, appendVehicles, true); + validateAppendVehiclesCanBind(appendVehicles, currentVehicleIds); + + Set mergedVehicleIds = new LinkedHashSet<>(currentVehicleIds); + mergedVehicleIds.addAll(appendVehicleIds); + List targetVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, mergedVehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(new ArrayList<>(mergedVehicleIds), targetVehicles, true); + + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driver.getId()); + impactedDriverIds.addAll(findOtherDriversBoundToVehicles(companyId, appendVehicleIds, driver.getId())); + impactedDriverIds.addAll(syncVehicleBindingsForDriver(driver.getId(), companyId, targetVehicles)); + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean unbindVehicles(HotDriverBatchBindVehicleBo bo) { + HotDriver driver = baseMapper.selectById(bo.getDriverId()); + if (driver == null || Long.valueOf(1L).equals(driver.getIsDeleted())) { + throw new ServiceException("驾驶员不存在"); + } + Long companyId = driver.getCompanyId(); + if (companyId == null) { + throw new ServiceException("驾驶员所属公司不能为空"); + } + + List currentVehicleIds = getBoundVehicleIds(driver); + List unbindVehicleIds = splitCsv(bo.getVehicleIds()); + if (CollUtil.isEmpty(currentVehicleIds)) { + if (CollUtil.isNotEmpty(unbindVehicleIds)) { + throw new ServiceException("该驾驶员当前无绑定车辆,不能解绑"); + } + return true; + } + List selectedUnbindVehicleIds = CollUtil.isEmpty(unbindVehicleIds) + ? new ArrayList<>(currentVehicleIds) + : new ArrayList<>(new LinkedHashSet<>(unbindVehicleIds)); + List selectedUnbindVehicles = new ArrayList<>(); + if (CollUtil.isNotEmpty(selectedUnbindVehicleIds)) { + selectedUnbindVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, selectedUnbindVehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(selectedUnbindVehicleIds, selectedUnbindVehicles, true); + validateVehiclesBelongToDriverForUnbind(driver.getId(), currentVehicleIds, selectedUnbindVehicles); + validateVehiclesCanUnbind(selectedUnbindVehicles); + } + + Set remainingVehicleIds = new LinkedHashSet<>(currentVehicleIds); + if (CollUtil.isEmpty(selectedUnbindVehicleIds)) { + remainingVehicleIds.clear(); + } else { + remainingVehicleIds.removeAll(selectedUnbindVehicleIds); + } + + List targetVehicles = new ArrayList<>(); + if (CollUtil.isNotEmpty(remainingVehicleIds)) { + targetVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, remainingVehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(new ArrayList<>(remainingVehicleIds), targetVehicles, true); + } + + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driver.getId()); + impactedDriverIds.addAll(syncVehicleBindingsForDriver(driver.getId(), companyId, targetVehicles)); + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + return true; + } + /** * 移除用户在特定企业的司机端登录权限 */ @@ -933,6 +1072,292 @@ public class HotDriverServiceImpl implements IHotDriverService { } } + private List resolveDriverTargetVehicles(HotDriverBo bo, Long companyId) { + List vehicleIds = splitCsv(bo.getVehicleIds()); + if (CollUtil.isEmpty(vehicleIds)) { + vehicleIds = splitCsv(bo.getVehicleId()); + } + if (CollUtil.isNotEmpty(vehicleIds)) { + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, vehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(vehicleIds, vehicles, true); + return vehicles; + } + + List plateNumbers = splitCsv(bo.getPlateNumber()); + if (CollUtil.isEmpty(plateNumbers)) { + return new ArrayList<>(); + } + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getPlateNumber, plateNumbers) + .eq(HotVehicle::getIsDeleted, 0L)); + validateVehicleSelection(plateNumbers, vehicles, false); + return vehicles; + } + + private void validateVehicleSelection(List expectedValues, List vehicles, boolean useVehicleId) { + Set actualValues = vehicles.stream() + .map(vehicle -> useVehicleId ? vehicle.getId() : vehicle.getPlateNumber()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + List missingValues = expectedValues.stream() + .filter(value -> !actualValues.contains(value)) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(missingValues)) { + throw new ServiceException(useVehicleId + ? "存在未找到的车辆ID:" + String.join(",", missingValues) + : "存在未找到的车牌号:" + String.join(",", missingValues)); + } + } + + private void validateVehiclesCanChangeBinding(Collection currentVehicleIds, Collection targetVehicles) { + Set currentIds = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(currentVehicleIds)) { + currentIds.addAll(currentVehicleIds); + } + Set targetIds = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(targetVehicles)) { + targetIds.addAll(targetVehicles.stream() + .map(HotVehicle::getId) + .filter(StringUtils::isNotBlank) + .toList()); + } + if (currentIds.equals(targetIds)) { + return; + } + Set relatedVehicleIds = new LinkedHashSet<>(currentIds); + relatedVehicleIds.addAll(targetIds); + for (String vehicleId : relatedVehicleIds) { + hotVehicleThreeInspectService.validateNoUnfinishedProcess(vehicleId, "当前车辆三检流程未完成,不可进行换绑操作"); + } + } + + private void validateAppendVehiclesCanBind(Collection appendVehicles, Collection currentVehicleIds) { + Set currentIds = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(currentVehicleIds)) { + currentIds.addAll(currentVehicleIds); + } + if (CollUtil.isEmpty(appendVehicles)) { + return; + } + for (HotVehicle vehicle : appendVehicles) { + if (vehicle == null || StringUtils.isBlank(vehicle.getId()) || currentIds.contains(vehicle.getId())) { + continue; + } + hotVehicleThreeInspectService.validateNoUnfinishedProcess(vehicle.getId(), "当前车辆三检流程未完成,不可进行换绑操作"); + } + } + + private void validateVehiclesBelongToDriverForUnbind(String driverId, + Collection currentVehicleIds, + Collection vehicles) { + Set currentIds = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(currentVehicleIds)) { + currentIds.addAll(currentVehicleIds); + } + List invalidVehicleIds = new ArrayList<>(); + for (HotVehicle vehicle : vehicles) { + if (vehicle == null || StringUtils.isBlank(vehicle.getId())) { + continue; + } + boolean existsInDriverBinding = currentIds.contains(vehicle.getId()); + boolean existsInVehicleBinding = splitCsv(vehicle.getCurrentDriver()).contains(driverId); + if (!existsInDriverBinding || !existsInVehicleBinding) { + invalidVehicleIds.add(vehicle.getId()); + } + } + if (CollUtil.isNotEmpty(invalidVehicleIds)) { + throw new ServiceException("存在未绑定到该驾驶员的车辆,不能解绑:" + String.join(",", invalidVehicleIds)); + } + } + + private void validateVehiclesCanUnbind(Collection vehicles) { + if (CollUtil.isEmpty(vehicles)) { + return; + } + for (HotVehicle vehicle : vehicles) { + if (vehicle == null || StringUtils.isBlank(vehicle.getId())) { + continue; + } + hotVehicleThreeInspectService.validateNoUnfinishedProcess(vehicle.getId(), "当前车辆三检流程未完成,不可解除绑定操作"); + } + } + + private Set syncVehicleBindingsForDriver(String driverId, Long companyId, List targetVehicles) { + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driverId); + if (StringUtils.isBlank(driverId) || companyId == null) { + return impactedDriverIds; + } + + Set targetVehicleIds = targetVehicles.stream() + .map(HotVehicle::getId) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + List companyVehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L)); + + for (HotVehicle vehicle : companyVehicles) { + List currentDrivers = splitCsv(vehicle.getCurrentDriver()); + boolean currentlyBoundToDriver = currentDrivers.contains(driverId); + boolean shouldBindToDriver = targetVehicleIds.contains(vehicle.getId()); + + if (!currentlyBoundToDriver && !shouldBindToDriver) { + continue; + } + + if (shouldBindToDriver) { + impactedDriverIds.addAll(currentDrivers); + if (!currentlyBoundToDriver || currentDrivers.size() != 1) { + vehicleMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicle::getCurrentDriver, driverId) + .eq(HotVehicle::getId, vehicle.getId()) + .eq(HotVehicle::getCompanyId, companyId)); + } + continue; + } + + List remainingDrivers = new ArrayList<>(currentDrivers); + remainingDrivers.removeIf(driverId::equals); + impactedDriverIds.addAll(remainingDrivers); + vehicleMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicle::getCurrentDriver, normalizeSingleBindingValue(remainingDrivers)) + .eq(HotVehicle::getId, vehicle.getId()) + .eq(HotVehicle::getCompanyId, companyId)); + } + return impactedDriverIds; + } + + private Set findOtherDriversBoundToVehicles(Long companyId, Collection vehicleIds, String excludeDriverId) { + Set driverIds = new LinkedHashSet<>(); + if (companyId == null || CollUtil.isEmpty(vehicleIds)) { + return driverIds; + } + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getId, vehicleIds) + .eq(HotVehicle::getIsDeleted, 0L)); + for (HotVehicle vehicle : vehicles) { + for (String driverId : splitCsv(vehicle.getCurrentDriver())) { + if (!StringUtils.equals(driverId, excludeDriverId)) { + driverIds.add(driverId); + } + } + } + return driverIds; + } + + private void syncDriverVehicleRelationsByDriverIds(Long companyId, Collection driverIds) { + if (companyId == null || CollUtil.isEmpty(driverIds)) { + return; + } + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L)); + for (String driverId : driverIds) { + if (StringUtils.isBlank(driverId)) { + continue; + } + List vehicleIds = new ArrayList<>(); + List boundVehicles = new ArrayList<>(); + for (HotVehicle vehicle : vehicles) { + if (!splitCsv(vehicle.getCurrentDriver()).contains(driverId)) { + continue; + } + boundVehicles.add(vehicle); + vehicleIds.add(vehicle.getId()); + } + HotDriver driver = baseMapper.selectById(driverId); + String primaryVehicleId = resolvePrimaryVehicleId(driver, vehicleIds); + String primaryPlateNumber = resolvePrimaryPlateNumber(primaryVehicleId, boundVehicles); + driverMapper.update(null, new LambdaUpdateWrapper() + .set(HotDriver::getVehicleId, primaryVehicleId) + .set(HotDriver::getVehicleIds, joinCsv(vehicleIds)) + .set(HotDriver::getPlateNumber, primaryPlateNumber) + .eq(HotDriver::getId, driverId) + .eq(HotDriver::getCompanyId, companyId)); + } + } + + private List getBoundVehicleIds(HotDriver driver) { + if (driver == null) { + return new ArrayList<>(); + } + List vehicleIds = splitCsv(driver.getVehicleIds()); + if (CollUtil.isNotEmpty(vehicleIds)) { + return vehicleIds; + } + return splitCsv(driver.getVehicleId()); + } + + private String resolvePrimaryVehicleId(HotDriver driver, Collection boundVehicleIds) { + if (CollUtil.isEmpty(boundVehicleIds)) { + return null; + } + if (driver != null && StringUtils.isNotBlank(driver.getVehicleId()) && boundVehicleIds.contains(driver.getVehicleId())) { + return driver.getVehicleId(); + } + return boundVehicleIds.stream() + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + + private String resolvePrimaryPlateNumber(String primaryVehicleId, Collection boundVehicles) { + if (StringUtils.isBlank(primaryVehicleId) || CollUtil.isEmpty(boundVehicles)) { + return null; + } + return boundVehicles.stream() + .filter(vehicle -> StringUtils.equals(primaryVehicleId, vehicle.getId())) + .map(HotVehicle::getPlateNumber) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + + private List splitCsv(String value) { + if (StringUtils.isBlank(value)) { + return new ArrayList<>(); + } + return Arrays.stream(value.split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + private String joinCsv(Collection values) { + if (CollUtil.isEmpty(values)) { + return null; + } + return values.stream() + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.joining(",")); + } + + private String normalizeNullableText(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + + private String normalizeSingleBindingValue(Collection values) { + if (CollUtil.isEmpty(values)) { + return null; + } + return values.stream() + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + /** * 保存驾驶员家庭成员 */ @@ -997,6 +1422,25 @@ public class HotDriverServiceImpl implements IHotDriverService { return; } + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.add(driverId); + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L)); + for (HotVehicle vehicle : vehicles) { + List driverIds = splitCsv(vehicle.getCurrentDriver()); + if (!driverIds.contains(driverId)) { + continue; + } + driverIds.removeIf(driverId::equals); + impactedDriverIds.addAll(driverIds); + vehicleMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicle::getCurrentDriver, normalizeSingleBindingValue(driverIds)) + .eq(HotVehicle::getId, vehicle.getId()) + .eq(HotVehicle::getCompanyId, companyId)); + } + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + // 2. 将该驾驶员从所有组中移除 // 查找包含该驾驶员的所有组 List groups = groupMapper.selectList( diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java index 6885488..c874787 100644 --- a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java @@ -165,15 +165,79 @@ public class HotVehicleChangeServiceImpl implements IHotVehicleChangeService { .filter(StringUtils::isNotBlank) .collect(Collectors.toList()); if (!driverIds.isEmpty()) { - driverMapper.update(null, new LambdaUpdateWrapper() - .set(HotDriver::getVehicleId, null) - .set(HotDriver::getPlateNumber, null) - .in(HotDriver::getId, driverIds) - .eq(HotDriver::getCompanyId, bo.getCompanyId())); + for (String driverId : driverIds) { + recalculateDriverVehicleRelations(driverId, bo.getCompanyId()); + } } } } + private void recalculateDriverVehicleRelations(String driverId, Long companyId) { + if (StringUtils.isBlank(driverId) || companyId == null) { + return; + } + List vehicles = vehicleMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L)); + List vehicleIds = new ArrayList<>(); + List boundVehicles = new ArrayList<>(); + for (HotVehicle item : vehicles) { + List currentDrivers = Arrays.stream(StringUtils.defaultString(item.getCurrentDriver()).split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + if (!currentDrivers.contains(driverId)) { + continue; + } + boundVehicles.add(item); + vehicleIds.add(item.getId()); + } + HotDriver driver = driverMapper.selectById(driverId); + String primaryVehicleId = resolvePrimaryVehicleId(driver, vehicleIds); + String primaryPlateNumber = resolvePrimaryPlateNumber(primaryVehicleId, boundVehicles); + driverMapper.update(null, new LambdaUpdateWrapper() + .set(HotDriver::getVehicleId, primaryVehicleId) + .set(HotDriver::getVehicleIds, joinCsv(vehicleIds)) + .set(HotDriver::getPlateNumber, primaryPlateNumber) + .eq(HotDriver::getId, driverId) + .eq(HotDriver::getCompanyId, companyId)); + } + + private String joinCsv(Collection values) { + if (values == null || values.isEmpty()) { + return null; + } + return values.stream() + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.joining(",")); + } + + private String resolvePrimaryVehicleId(HotDriver driver, Collection boundVehicleIds) { + if (boundVehicleIds == null || boundVehicleIds.isEmpty()) { + return null; + } + if (driver != null && StringUtils.isNotBlank(driver.getVehicleId()) && boundVehicleIds.contains(driver.getVehicleId())) { + return driver.getVehicleId(); + } + return boundVehicleIds.stream() + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + + private String resolvePrimaryPlateNumber(String primaryVehicleId, Collection boundVehicles) { + if (StringUtils.isBlank(primaryVehicleId) || boundVehicles == null || boundVehicles.isEmpty()) { + return null; + } + return boundVehicles.stream() + .filter(vehicle -> StringUtils.equals(primaryVehicleId, vehicle.getId())) + .map(HotVehicle::getPlateNumber) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + /** * 校验并批量删除车辆变更记录信息 * diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleItemVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleItemVo.java new file mode 100644 index 0000000..728b90c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleItemVo.java @@ -0,0 +1,44 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 驾驶员换绑车辆列表项 + * + * @author shihongwei + * @date 2026-05-22 + */ +@Data +public class DriverBindingVehicleItemVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 当前绑定驾驶员ID + */ + private String currentDriverId; + + /** + * 当前绑定驾驶员姓名 + */ + private String currentDriverName; + + /** + * 车辆类型 + */ + private String vehicleType; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleOptionsVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleOptionsVo.java new file mode 100644 index 0000000..882951d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/DriverBindingVehicleOptionsVo.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 驾驶员换绑车辆查询结果 + * + * @author shihongwei + * @date 2026-05-22 + */ +@Data +public class DriverBindingVehicleOptionsVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 驾驶员已绑定车辆列表 + */ + private List boundVehicleList; + + /** + * 公司可绑定车辆列表 + */ + private List bindableVehicleList; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java index bca1e8c..8866dd5 100644 --- a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java @@ -2,6 +2,8 @@ package com.hotwj.platform.resourceManagement.vehicleManagement.service; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleItemVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleOptionsVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; @@ -70,6 +72,24 @@ public interface IHotVehicleService { */ List queryBindableList(HotVehicleBo bo); + /** + * 查询驾驶员已绑定车辆和当前公司可绑定车辆列表 + * + * @param companyId 公司ID + * @param driverId 驾驶员ID + * @return 查询结果 + */ + DriverBindingVehicleOptionsVo queryDriverBindingVehicleOptions(Long companyId, String driverId); + + /** + * 查询驾驶员已绑定车辆列表 + * + * @param companyId 公司ID + * @param driverId 驾驶员ID + * @return 已绑定车辆列表 + */ + List queryDriverBoundVehicleList(Long companyId, String driverId); + /** * 查询符合条件的公司车辆信息列表 * diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java index 5e7f5b0..a5ef99b 100644 --- a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; import com.hotwj.platform.driverManagement.driver.domain.HotDriver; import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; @@ -27,6 +28,8 @@ import com.hotwj.platform.resourceManagement.vehicleInsurance.mapper.HotVehicleI import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleItemVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.DriverBindingVehicleOptionsVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo; import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; @@ -66,6 +69,7 @@ public class HotVehicleServiceImpl implements IHotVehicleService { private final HotVehicleRepairMapper repairMapper; private final HotVehicleCertReissueMapper certReissueMapper; private final DictService dictService; + private final IHotVehicleThreeInspectService hotVehicleThreeInspectService; private static final Set CLEAR_DRIVER_OPERATION_STATUS = new HashSet<>(Arrays.asList(3L, 4L, 5L)); /** @@ -104,8 +108,8 @@ public class HotVehicleServiceImpl implements IHotVehicleService { // 验证是否存在这个车牌号 boolean exists = baseMapper.selectCount(new LambdaQueryWrapper() - .eq(HotVehicle::getPlateNumber, vo.getPlateNumber()) - .eq(HotVehicle::getCompanyId, companyId)) > 0; + .eq(HotVehicle::getPlateNumber, vo.getPlateNumber()) + .eq(HotVehicle::getCompanyId, companyId)) > 0; if (exists) { failureNum++; @@ -135,8 +139,8 @@ public class HotVehicleServiceImpl implements IHotVehicleService { } catch (ConstraintViolationException e) { failureNum++; String msg = e.getConstraintViolations().stream() - .map(ConstraintViolation::getMessage) - .collect(Collectors.joining(", ")); + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", ")); failureMsg.append("
" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 导入失败:" + msg); } catch (Exception e) { failureNum++; @@ -221,6 +225,57 @@ public class HotVehicleServiceImpl implements IHotVehicleService { return baseMapper.selectVoList(lqw); } + @Override + public DriverBindingVehicleOptionsVo queryDriverBindingVehicleOptions(Long companyId, String driverId) { + DriverBindingVehicleOptionsVo result = new DriverBindingVehicleOptionsVo(); + if (companyId == null || StringUtils.isBlank(driverId)) { + result.setBoundVehicleList(Collections.emptyList()); + result.setBindableVehicleList(Collections.emptyList()); + return result; + } + + List companyVehicles = baseMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L) + .orderByAsc(HotVehicle::getArchiveNo) + .orderByAsc(HotVehicle::getPlateNumber)); + + List boundVehicleList = new ArrayList<>(); + List bindableVehicleList = new ArrayList<>(); + for (HotVehicle vehicle : companyVehicles) { + List currentDriverIds = splitCsv(vehicle.getCurrentDriver()); + if (currentDriverIds.contains(driverId)) { + boundVehicleList.add(toDriverBindingVehicleItem(vehicle)); + continue; + } + if (isBindableVehicle(vehicle)) { + bindableVehicleList.add(toDriverBindingVehicleItem(vehicle)); + } + } + result.setBoundVehicleList(boundVehicleList); + result.setBindableVehicleList(bindableVehicleList); + return result; + } + + @Override + public List queryDriverBoundVehicleList(Long companyId, String driverId) { + if (companyId == null || StringUtils.isBlank(driverId)) { + return Collections.emptyList(); + } + List companyVehicles = baseMapper.selectList(Wrappers.lambdaQuery() + .eq(HotVehicle::getCompanyId, companyId) + .eq(HotVehicle::getIsDeleted, 0L) + .orderByAsc(HotVehicle::getArchiveNo) + .orderByAsc(HotVehicle::getPlateNumber)); + List boundVehicleList = new ArrayList<>(); + for (HotVehicle vehicle : companyVehicles) { + if (splitCsv(vehicle.getCurrentDriver()).contains(driverId)) { + boundVehicleList.add(toDriverBindingVehicleItem(vehicle)); + } + } + return boundVehicleList; + } + /** * 查询符合条件的公司车辆信息列表 * @@ -367,7 +422,9 @@ public class HotVehicleServiceImpl implements IHotVehicleService { lqw.eq(bo.getIsTrailer() != null, HotVehicle::getIsTrailer, bo.getIsTrailer()); lqw.eq(bo.getVehicleStatus() != null, HotVehicle::getVehicleStatus, bo.getVehicleStatus()); lqw.in(HotVehicle::getOperationStatus, Arrays.asList(bo.getOperationStatus().split(","))); - lqw.eq(StringUtils.isNotBlank(bo.getCurrentDriver()), HotVehicle::getCurrentDriver, bo.getCurrentDriver()); + lqw.and(wrapper -> wrapper.isNull(HotVehicle::getCurrentDriver) + .or() + .eq(HotVehicle::getCurrentDriver, "")); lqw.eq(StringUtils.isNotBlank(bo.getBodyImageUrls()), HotVehicle::getBodyImageUrls, bo.getBodyImageUrls()); lqw.eq(StringUtils.isNotBlank(bo.getOtherAttachmentUrls()), HotVehicle::getOtherAttachmentUrls, bo.getOtherAttachmentUrls()); lqw.eq(StringUtils.isNotBlank(bo.getColor()), HotVehicle::getColor, bo.getColor()); @@ -525,10 +582,14 @@ public class HotVehicleServiceImpl implements IHotVehicleService { @Override @Transactional(rollbackFor = Exception.class) public Boolean updateByBo(HotVehicleBo bo) { + validateSingleCurrentDriverBinding(bo.getCurrentDriver()); // 获取旧数据用于比对 HotVehicle oldVehicle = baseMapper.selectById(bo.getId()); HotVehicle update = MapstructUtils.convert(bo, HotVehicle.class); + String normalizedCurrentDriver = normalizeSingleBindingValue(update.getCurrentDriver()); + update.setCurrentDriver(normalizedCurrentDriver); + bo.setCurrentDriver(normalizedCurrentDriver); if (update.getOperationStatus() != null && CLEAR_DRIVER_OPERATION_STATUS.contains(update.getOperationStatus())) { update.setCurrentDriver(null); bo.setCurrentDriver(null); @@ -539,59 +600,18 @@ public class HotVehicleServiceImpl implements IHotVehicleService { String vehicleId = update.getId(); Long companyId = update.getCompanyId(); - // 处理驾驶员变更:同步驾驶员信息的车牌号 - String oldDriverId = oldVehicle != null ? oldVehicle.getCurrentDriver() : null; - String newDriverId = update.getCurrentDriver(); + // 处理驾驶员变更:车辆侧只允许单个驾驶员,驾驶员侧回写多车列表 + List oldDriverIds = splitCsv(oldVehicle != null ? oldVehicle.getCurrentDriver() : null); + List newDriverIds = splitCsv(update.getCurrentDriver()); + String oldDriverId = oldDriverIds.isEmpty() ? null : oldDriverIds.get(0); + String newDriverId = newDriverIds.isEmpty() ? null : newDriverIds.get(0); String oldPlate = oldVehicle != null ? oldVehicle.getPlateNumber() : null; String newPlate = update.getPlateNumber(); + boolean bindingChanged = !oldDriverIds.equals(newDriverIds) || !StringUtils.equals(oldPlate, newPlate); + boolean currentDriverChanged = !oldDriverIds.equals(newDriverIds); - // 如果驾驶员发生变化 或 车牌号发生变化 - if (!StringUtils.equals(oldDriverId, newDriverId) || !StringUtils.equals(oldPlate, newPlate)) { - List oldDrivers = StringUtils.isBlank(oldDriverId) ? new ArrayList<>() : Arrays.asList(oldDriverId.split(",")); - List newDrivers = StringUtils.isBlank(newDriverId) ? new ArrayList<>() : Arrays.asList(newDriverId.split(",")); - - // 1. 处理移除的驾驶员 (在旧列表中但不在新列表中 -> 清除车牌关联) - List removedDrivers = oldDrivers.stream() - .filter(d -> !newDrivers.contains(d)) - .collect(Collectors.toList()); - - if (CollUtil.isNotEmpty(removedDrivers)) { - driverMapper.update(null, - new LambdaUpdateWrapper() - .set(HotDriver::getPlateNumber, null) - .set(HotDriver::getVehicleId, null) - .in(HotDriver::getId, removedDrivers) - .eq(HotDriver::getCompanyId, companyId)); - } - - // 2. 处理当前的驾驶员 (无论是新增的还是保留的,都统一更新为当前车辆信息) - if (CollUtil.isNotEmpty(newDrivers)) { - driverMapper.update(null, - new LambdaUpdateWrapper() - .set(HotDriver::getPlateNumber, newPlate) - .set(HotDriver::getVehicleId, vehicleId) - .in(HotDriver::getId, newDrivers) - .eq(HotDriver::getCompanyId, companyId)); - // 2.1 对新增绑定的驾驶员发送系统消息 - List addedDrivers = newDrivers.stream() - .filter(d -> !oldDrivers.contains(d)) - .collect(Collectors.toList()); - if (CollUtil.isNotEmpty(addedDrivers) && StringUtils.isNotBlank(newPlate)) { - HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); - bos.setLevel("普通"); - bos.setContent("您已经绑定车辆【" + newPlate + "】。"); - bos.setSourceType("驾驶员管理"); - bos.setSenderType("SYSTEM"); - bos.setReceiverType("驾驶员"); - bos.setReceiverIds(addedDrivers); - bos.setIsDeleted(0L); - try { - notificationService.insertByBo(bos); - } catch (Exception e) { - log.warn("发送驾驶员绑定车辆通知失败 companyId={} vehicleId={} plate={}", companyId, vehicleId, newPlate, e); - } - } - } + if (currentDriverChanged) { + hotVehicleThreeInspectService.validateNoUnfinishedProcess(update.getId(), "当前车辆三检流程未完成,不可进行换绑操作"); } // 更新合同信息 @@ -667,6 +687,29 @@ public class HotVehicleServiceImpl implements IHotVehicleService { } if (flag) { + if (bindingChanged && companyId != null) { + Set impactedDriverIds = new LinkedHashSet<>(); + impactedDriverIds.addAll(oldDriverIds); + impactedDriverIds.addAll(newDriverIds); + syncDriverVehicleRelationsByDriverIds(companyId, impactedDriverIds); + } + + if (StringUtils.isNotBlank(newDriverId) && !oldDriverIds.contains(newDriverId) && StringUtils.isNotBlank(newPlate)) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您已经绑定车辆【" + newPlate + "】。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(Collections.singletonList(newDriverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员绑定车辆通知失败 companyId={} vehicleId={} plate={}", companyId, vehicleId, newPlate, e); + } + } + Long oldVehicleStatus = oldVehicle != null ? oldVehicle.getVehicleStatus() : null; Long newVehicleStatus = update.getVehicleStatus(); Long oldOperationStatus = oldVehicle != null ? oldVehicle.getOperationStatus() : null; @@ -753,6 +796,31 @@ public class HotVehicleServiceImpl implements IHotVehicleService { } } + private boolean isBindableVehicle(HotVehicle vehicle) { + return vehicle != null + && Long.valueOf(1L).equals(vehicle.getVehicleStatus()) + && Long.valueOf(1L).equals(vehicle.getOperationStatus()) + && StringUtils.isBlank(vehicle.getCurrentDriver()); + } + + private DriverBindingVehicleItemVo toDriverBindingVehicleItem(HotVehicle vehicle) { + DriverBindingVehicleItemVo item = new DriverBindingVehicleItemVo(); + item.setVehicleId(vehicle.getId()); + item.setPlateNumber(vehicle.getPlateNumber()); + item.setCurrentDriverId(normalizeSingleBindingValue(vehicle.getCurrentDriver())); + item.setCurrentDriverName(resolveCurrentDriverName(item.getCurrentDriverId())); + item.setVehicleType(vehicle.getVehicleType()); + return item; + } + + private String resolveCurrentDriverName(String driverId) { + if (StringUtils.isBlank(driverId)) { + return null; + } + HotDriver driver = driverMapper.selectById(driverId); + return driver == null ? null : driver.getName(); + } + private String normalizeSingleBindingValue(String value) { List values = splitCsv(value); return values.isEmpty() ? null : values.get(0); @@ -791,22 +859,51 @@ public class HotVehicleServiceImpl implements IHotVehicleService { continue; } List vehicleIds = new ArrayList<>(); - List plateNumbers = new ArrayList<>(); + List boundVehicles = new ArrayList<>(); for (HotVehicle vehicle : vehicles) { if (!splitCsv(vehicle.getCurrentDriver()).contains(driverId)) { continue; } + boundVehicles.add(vehicle); vehicleIds.add(vehicle.getId()); - plateNumbers.add(vehicle.getPlateNumber()); } + HotDriver driver = driverMapper.selectById(driverId); + String primaryVehicleId = resolvePrimaryVehicleId(driver, vehicleIds); + String primaryPlateNumber = resolvePrimaryPlateNumber(primaryVehicleId, boundVehicles); driverMapper.update(null, new LambdaUpdateWrapper() - .set(HotDriver::getVehicleId, joinCsv(vehicleIds)) - .set(HotDriver::getPlateNumber, joinCsv(plateNumbers)) + .set(HotDriver::getVehicleId, primaryVehicleId) + .set(HotDriver::getVehicleIds, joinCsv(vehicleIds)) + .set(HotDriver::getPlateNumber, primaryPlateNumber) .eq(HotDriver::getId, driverId) .eq(HotDriver::getCompanyId, companyId)); } } + private String resolvePrimaryVehicleId(HotDriver driver, Collection boundVehicleIds) { + if (CollUtil.isEmpty(boundVehicleIds)) { + return null; + } + if (driver != null && StringUtils.isNotBlank(driver.getVehicleId()) && boundVehicleIds.contains(driver.getVehicleId())) { + return driver.getVehicleId(); + } + return boundVehicleIds.stream() + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + + private String resolvePrimaryPlateNumber(String primaryVehicleId, Collection boundVehicles) { + if (StringUtils.isBlank(primaryVehicleId) || CollUtil.isEmpty(boundVehicles)) { + return null; + } + return boundVehicles.stream() + .filter(vehicle -> StringUtils.equals(primaryVehicleId, vehicle.getId())) + .map(HotVehicle::getPlateNumber) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(null); + } + /** * 校验并批量删除公司车辆信息信息 * diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java index 9b4f753..6d31163 100644 --- a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java @@ -3,6 +3,7 @@ package com.hotwj.platform.securityManagement.vehicleInoutCheck.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; @@ -47,6 +48,7 @@ public class HotVehicleInoutCheckServiceImpl implements IHotVehicleInoutCheckSer private final HotVehicleMapper vehicleMapper; private final HotCompanySafetyManagerMapper safetyManagerMapper; private final IHotSystemNotificationService notificationService; + private final IHotVehicleThreeInspectService hotVehicleThreeInspectService; /** * 查询车辆进出场检查 @@ -123,6 +125,9 @@ public class HotVehicleInoutCheckServiceImpl implements IHotVehicleInoutCheckSer bo.setCheckDate(new Date()); } + hotVehicleThreeInspectService.validateNoUnfinishedProcess(String.valueOf(bo.getVehicleId()), + "当前车辆三检流程未完成,不能新增出车检查"); + // 填充冗余字段(车牌号、检查人员姓名) fillRedundantFields(bo);