✨ 新增小程序端司机与车辆档案管理接口
- /archive/manage/driver|vehicle 的 page/detail/submit/status 八个接口,仅登录态 + 显式 admin 校验, 权限不足回业务 403(小程序把 401 当会话失效,不能回 401) - 分页支持 keyword(司机:姓名/手机号;车辆:车牌/VIN/所有人)与 expireStatus、status - 停用真正生效:/driver/vehicles 只回启用车辆、停用车辆不可提交车辆认证、 派单检索排除停用司机、选车列表改查车辆档案表并排除停用车辆 - 管理端保存不再重置车辆认证状态(此前每存一次就造出「审核中但没有待审记录」的死状态)
This commit is contained in:
+6
@@ -48,6 +48,12 @@ public class DriverVO extends Driver {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 搜索关键字:同时模糊匹配姓名与手机号(小程序列表用,管理端不传)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "搜索关键字(姓名 / 手机号)")
|
||||
private String keyword;
|
||||
/**
|
||||
* 证件有效期状态:within30-30天内到期,expired-已到期
|
||||
*/
|
||||
|
||||
+6
@@ -48,6 +48,12 @@ public class TransportVehicleVO extends TransportVehicle {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 搜索关键字:同时模糊匹配车牌、VIN 与所有人(小程序列表用,管理端不传)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "搜索关键字(车牌 / VIN / 所有人)")
|
||||
private String keyword;
|
||||
/**
|
||||
* 证件有效期状态:within30-30天内到期,expired-已到期
|
||||
*/
|
||||
|
||||
+10
@@ -30,6 +30,7 @@ import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.vo.ArchiveManageStatsVO;
|
||||
import org.springblade.transport.service.IManageArchiveService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -53,6 +54,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
public class ManageArchiveController extends BladeController {
|
||||
|
||||
private final IManageArchiveService manageArchiveService;
|
||||
private final MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
|
||||
@GetMapping("/stats")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@@ -61,4 +63,12 @@ public class ManageArchiveController extends BladeController {
|
||||
return R.data(manageArchiveService.stats());
|
||||
}
|
||||
|
||||
// 本接口不挂 @PreAuth;无权限返回 false,不抛 403,供小程序决定空态。
|
||||
@GetMapping("/capability")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "档案管理能力判定", description = "当前登录是否为调度端管理员;false 时小程序显示无权限空态,不踢登录")
|
||||
public R<Boolean> capability() {
|
||||
return R.data(mpUserDataScopeSupport.isMpDispatcher());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.entity.Driver;
|
||||
import org.springblade.transport.pojo.vo.DriverVO;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
import org.springblade.transport.wrapper.DriverWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
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.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 调度端司机档案管理(小程序)
|
||||
* <p>
|
||||
* 对外完整路径:{@code /api/blade-transport/archive/manage/driver/**}
|
||||
* (网关 StripPrefix 去掉 {@code blade-transport} 后落入 {@code /archive/manage/driver/**}),
|
||||
* 同时兼容未去前缀直连。
|
||||
* <p>
|
||||
* 为什么不复用管理端的 {@code /driver/**}:那些接口挂在 {@code @PreAuth(menu = "driver")} 下,
|
||||
* 小程序令牌的 roleId 是占位值 "0"、查不到 blade_role_menu,对任何小程序账号都必然被拒。
|
||||
* 这里不挂 {@code @PreAuth},改成方法内显式判定管理员身份,且**权限不足必须回业务 403**:
|
||||
* 小程序请求层把 401 一律当会话失效(清令牌 + 回登录页),回 401 会把越权访问变成被踢下线。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping({"/archive/manage/driver", "/blade-transport/archive/manage/driver"})
|
||||
@Tag(name = "调度端司机档案", description = "小程序调度端司机管理")
|
||||
public class ManageArchiveDriverController extends BladeController {
|
||||
|
||||
private static final int DEFAULT_CURRENT = 1;
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
private static final int MAX_SIZE = 100;
|
||||
|
||||
/**
|
||||
* 权限不足的业务码。小程序请求层把 401 当会话失效,接口一律不得回 401。
|
||||
*/
|
||||
private static final int FORBIDDEN_CODE = 403;
|
||||
|
||||
private static final String FORBIDDEN_MSG = "仅调度端管理员可管理司机档案";
|
||||
|
||||
private final IDriverService driverService;
|
||||
private final MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "司机档案分页", description = "keyword 同时模糊匹配姓名与手机号;status 不传为启用停用全量;expireStatus 取 within30 / expired")
|
||||
public R<IPage<DriverVO>> page(DriverVO driver, Query query) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
fillExpiryDate(driver);
|
||||
return R.data(driverService.selectDriverPage(Condition.getPage(normalizeQuery(query)), driver));
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "司机档案详情")
|
||||
public R<DriverVO> detail(@Parameter(description = "司机主键", required = true) @RequestParam Long id) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
Driver query = new Driver();
|
||||
query.setId(id);
|
||||
return R.data(DriverWrapper.build().entityVO(driverService.getOne(Condition.getQueryWrapper(query))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改司机档案", description = "直接写档案、立即生效,不生成待审快照")
|
||||
public R submit(@Valid @RequestBody Driver driver) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
// 生效指针只由认证审核流水写(通过才建立生效快照),不接受管理端写入
|
||||
driver.setEffectiveCertRecordId(null);
|
||||
return R.status(driverService.submit(driver));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启停用
|
||||
*/
|
||||
@PostMapping("/status")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "启用 / 停用司机", description = "status:1 启用,2 停用。停用司机不进调度检索,但仍可登录")
|
||||
public R status(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "状态:1 启用,2 停用", required = true) @RequestParam Integer status) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
return R.status(driverService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
private boolean canManage() {
|
||||
return mpUserDataScopeSupport.isMpDispatcher();
|
||||
}
|
||||
|
||||
private Query normalizeQuery(Query query) {
|
||||
if (query == null) {
|
||||
query = new Query();
|
||||
}
|
||||
if (query.getCurrent() == null || query.getCurrent() < DEFAULT_CURRENT) {
|
||||
query.setCurrent(DEFAULT_CURRENT);
|
||||
}
|
||||
if (query.getSize() == null || query.getSize() <= 0) {
|
||||
query.setSize(DEFAULT_SIZE);
|
||||
}
|
||||
if (query.getSize() > MAX_SIZE) {
|
||||
query.setSize(MAX_SIZE);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private void fillExpiryDate(DriverVO driver) {
|
||||
if (driver.getToday() == null) {
|
||||
driver.setToday(LocalDate.now());
|
||||
}
|
||||
if (driver.getWarningDate() == null) {
|
||||
driver.setWarningDate(driver.getToday().plusDays(30));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||
import org.springblade.transport.pojo.vo.TransportVehicleVO;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
import org.springblade.transport.wrapper.TransportVehicleWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
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.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 调度端车辆档案管理(小程序)
|
||||
* <p>
|
||||
* 对外完整路径:{@code /api/blade-transport/archive/manage/vehicle/**}
|
||||
* (网关 StripPrefix 去掉 {@code blade-transport} 后落入 {@code /archive/manage/vehicle/**}),
|
||||
* 同时兼容未去前缀直连。
|
||||
* <p>
|
||||
* 与管理端 {@code /transport-vehicle/**} 的关系、以及为什么必须回业务 403 而不是 401,
|
||||
* 见 {@link ManageArchiveDriverController} 的类注释。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping({"/archive/manage/vehicle", "/blade-transport/archive/manage/vehicle"})
|
||||
@Tag(name = "调度端车辆档案", description = "小程序调度端车辆管理")
|
||||
public class ManageArchiveVehicleController extends BladeController {
|
||||
|
||||
private static final int DEFAULT_CURRENT = 1;
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
private static final int MAX_SIZE = 100;
|
||||
|
||||
/**
|
||||
* 权限不足的业务码。小程序请求层把 401 当会话失效,接口一律不得回 401。
|
||||
*/
|
||||
private static final int FORBIDDEN_CODE = 403;
|
||||
|
||||
private static final String FORBIDDEN_MSG = "仅调度端管理员可管理车辆档案";
|
||||
|
||||
private final ITransportVehicleService transportVehicleService;
|
||||
private final MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "车辆档案分页", description = "keyword 同时模糊匹配车牌、VIN 与所有人;status 不传为启用停用全量;expireStatus 取 within30 / expired")
|
||||
public R<IPage<TransportVehicleVO>> page(TransportVehicleVO vehicle, Query query) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
fillExpiryDate(vehicle);
|
||||
return R.data(transportVehicleService.selectTransportVehiclePage(Condition.getPage(normalizeQuery(query)), vehicle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "车辆档案详情")
|
||||
public R<TransportVehicleVO> detail(@Parameter(description = "车辆主键", required = true) @RequestParam Long id) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
TransportVehicle query = new TransportVehicle();
|
||||
query.setId(id);
|
||||
return R.data(TransportVehicleWrapper.build().entityVO(transportVehicleService.getOne(Condition.getQueryWrapper(query))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改车辆档案", description = "直接写档案、立即生效,不生成待审快照,也不改动认证状态")
|
||||
public R submit(@Valid @RequestBody TransportVehicle vehicle) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
// 生效指针只由认证审核流水写(通过才建立生效快照),不接受管理端写入
|
||||
vehicle.setEffectiveCertRecordId(null);
|
||||
return R.status(transportVehicleService.submit(vehicle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启停用
|
||||
*/
|
||||
@PostMapping("/status")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "启用 / 停用车辆", description = "status:1 启用,2 停用。停用车辆不进车辆检索、司机端车辆列表不再出现")
|
||||
public R status(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "状态:1 启用,2 停用", required = true) @RequestParam Integer status) {
|
||||
if (!canManage()) {
|
||||
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
|
||||
}
|
||||
return R.status(transportVehicleService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
private boolean canManage() {
|
||||
return mpUserDataScopeSupport.isMpDispatcher();
|
||||
}
|
||||
|
||||
private Query normalizeQuery(Query query) {
|
||||
if (query == null) {
|
||||
query = new Query();
|
||||
}
|
||||
if (query.getCurrent() == null || query.getCurrent() < DEFAULT_CURRENT) {
|
||||
query.setCurrent(DEFAULT_CURRENT);
|
||||
}
|
||||
if (query.getSize() == null || query.getSize() <= 0) {
|
||||
query.setSize(DEFAULT_SIZE);
|
||||
}
|
||||
if (query.getSize() > MAX_SIZE) {
|
||||
query.setSize(MAX_SIZE);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private void fillExpiryDate(TransportVehicleVO vehicle) {
|
||||
if (vehicle.getToday() == null) {
|
||||
vehicle.setToday(LocalDate.now());
|
||||
}
|
||||
if (vehicle.getWarningDate() == null) {
|
||||
vehicle.setWarningDate(vehicle.getToday().plusDays(30));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -147,6 +147,11 @@
|
||||
|
||||
<sql id="QueryCondition">
|
||||
is_deleted = 0
|
||||
<!-- 小程序列表的单框搜索:姓名或手机号命中其一即可。管理端不传 keyword,条件不生效。 -->
|
||||
<if test="driver.keyword != null and driver.keyword != ''">
|
||||
<bind name="driverKeywordLike" value="'%' + driver.keyword + '%'"/>
|
||||
AND (driver_name LIKE #{driverKeywordLike} OR mobile LIKE #{driverKeywordLike})
|
||||
</if>
|
||||
<if test="driver.driverName != null and driver.driverName != ''">
|
||||
<bind name="driverNameLike" value="'%' + driver.driverName + '%'"/>
|
||||
AND driver_name LIKE #{driverNameLike}
|
||||
|
||||
+5
@@ -116,6 +116,11 @@
|
||||
|
||||
<sql id="QueryCondition">
|
||||
is_deleted = 0
|
||||
<!-- 小程序列表的单框搜索:车牌、VIN 或所有人命中其一即可。管理端不传 keyword,条件不生效。 -->
|
||||
<if test="vehicle.keyword != null and vehicle.keyword != ''">
|
||||
<bind name="vehicleKeywordLike" value="'%' + vehicle.keyword + '%'"/>
|
||||
AND (plate_no LIKE #{vehicleKeywordLike} OR vin LIKE #{vehicleKeywordLike} OR owner_name LIKE #{vehicleKeywordLike})
|
||||
</if>
|
||||
<if test="vehicle.organizationName != null and vehicle.organizationName != ''">
|
||||
<!-- 「未分配组织」哨兵值定义在 TransportBusiConstant.UNASSIGNED_ORG_FILTER 与前端
|
||||
tms-erp-web/src/mixins/organization-search.js,改这里必须同步常量与 DriverMapper.xml 的同名分支。
|
||||
|
||||
+8
@@ -52,6 +52,12 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
public class DriverAppServiceImpl implements IDriverAppService {
|
||||
|
||||
/**
|
||||
* 启用状态。框架基类只有 status 列与 BladeConstant.DB_STATUS_NORMAL = 1,没有枚举;
|
||||
* 2 是「停用」,业务侧一律写字面量常量。
|
||||
*/
|
||||
private static final int ENABLED_STATUS = 1;
|
||||
|
||||
private final IDriverService driverService;
|
||||
private final ITransportVehicleService transportVehicleService;
|
||||
|
||||
@@ -74,6 +80,8 @@ public class DriverAppServiceImpl implements IDriverAppService {
|
||||
// 精确 + 规范化双条件匹配,统一走共享实现(避免各调用点各自实现而漏掉写法差异)
|
||||
LambdaQueryWrapper<TransportVehicle> plateQuery = Wrappers.lambdaQuery();
|
||||
VehiclePlateSupport.applyPlateMatch(plateQuery, plates);
|
||||
// 停用车辆不再出现在司机端:管理员停用即意味着这辆车不该再被司机拿去接单
|
||||
plateQuery.eq(TransportVehicle::getStatus, ENABLED_STATUS);
|
||||
List<TransportVehicle> vehicles = transportVehicleService.list(
|
||||
plateQuery.orderByDesc(TransportVehicle::getUpdateTime));
|
||||
// 去重(精确与规范化可能命中同一条)
|
||||
|
||||
+18
@@ -271,6 +271,10 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
if (!VehiclePlateSupport.isBound(requireCurrentDriver().getDrivingVehicle(), vehicle.getPlateNo())) {
|
||||
throw new ServiceException("无权查看该车辆的认证信息");
|
||||
}
|
||||
// 停用车辆不再对司机端开放:司机列表里已经不显示它,详情/认证入口也不该能进
|
||||
if (Func.isNotEmpty(vehicle.getStatus()) && vehicle.getStatus() != 1) {
|
||||
throw new ServiceException("该车辆已停用,暂不可查看,请联系调度启用");
|
||||
}
|
||||
TransportCertRecord effective = findEffectiveRecord(vehicle.getEffectiveCertRecordId());
|
||||
return effective == null
|
||||
? null
|
||||
@@ -408,6 +412,7 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
if (!VehiclePlateSupport.isBound(driver.getDrivingVehicle(), vehicle.getPlateNo())) {
|
||||
throw new ServiceException("无权操作该车辆的认证");
|
||||
}
|
||||
requireEnabledVehicle(vehicle);
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
@@ -428,11 +433,24 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
||||
// 已登记但不在本人名下,可能属于其他司机 —— 拒绝,不能顺手改归属
|
||||
throw new ServiceException("该车牌已归属其他司机,请联系管理员");
|
||||
}
|
||||
requireEnabledVehicle(existing);
|
||||
return existing;
|
||||
}
|
||||
return createVehicleForDriver(driver, request, normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用车辆不接受司机的认证提交
|
||||
* <p>
|
||||
* 停用是调度端的管理动作(车辆停驶、报废待办),司机端列表已不再显示停用车;
|
||||
* 若这里放行,司机仍能凭车牌把停用车提交认证,等于绕过停用。
|
||||
*/
|
||||
private void requireEnabledVehicle(TransportVehicle vehicle) {
|
||||
if (vehicle != null && Func.isNotEmpty(vehicle.getStatus()) && vehicle.getStatus() != 1) {
|
||||
throw new ServiceException("该车辆已停用,请联系调度启用后再认证");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按车牌(容忍写法差异)查已登记的车辆
|
||||
*
|
||||
|
||||
+57
-13
@@ -38,6 +38,7 @@ import org.springblade.transport.pojo.entity.Driver;
|
||||
import org.springblade.transport.pojo.entity.ExceptionDisposal;
|
||||
import org.springblade.transport.pojo.entity.LoadingManage;
|
||||
import org.springblade.transport.pojo.entity.RiskDisposal;
|
||||
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.vo.AdminDriverOptionVO;
|
||||
import org.springblade.transport.pojo.vo.AdminHomeBadgesVO;
|
||||
@@ -58,6 +59,7 @@ import org.springblade.transport.service.IExceptionDisposalService;
|
||||
import org.springblade.transport.service.ILoadingManageService;
|
||||
import org.springblade.transport.service.IManageWaybillService;
|
||||
import org.springblade.transport.service.IRiskDisposalService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.service.ITripQueryService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
@@ -122,6 +124,9 @@ public class ManageWaybillServiceImpl implements IManageWaybillService {
|
||||
private static final String TRANSPORT_COMMON = "common";
|
||||
private static final String TRANSPORT_LOAD = "load";
|
||||
|
||||
/** 启用状态(base_entity.status):停用的司机不进派单检索、停用的车不进选车列表 */
|
||||
private static final int ENABLED_STATUS = 1;
|
||||
|
||||
private static final int FEED_LIMIT = 20;
|
||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||
private static final int MAX_PAGE_SIZE = 50;
|
||||
@@ -141,6 +146,7 @@ public class ManageWaybillServiceImpl implements IManageWaybillService {
|
||||
private final IDriverWaybillService driverWaybillService;
|
||||
private final ITripQueryService tripQueryService;
|
||||
private final ILoadingManageService loadingManageService;
|
||||
private final ITransportVehicleService transportVehicleService;
|
||||
private final MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
|
||||
@Override
|
||||
@@ -859,6 +865,8 @@ public class ManageWaybillServiceImpl implements IManageWaybillService {
|
||||
String key = Func.toStr(keyword, "").trim();
|
||||
LambdaQueryWrapper<Driver> wrapper = Wrappers.<Driver>lambdaQuery()
|
||||
.eq(Driver::getIsDeleted, 0)
|
||||
// 停用司机不可派单:不进调度检索。停用只拦派单,不拦登录(司机仍能进小程序看自己的运单)
|
||||
.eq(Driver::getStatus, ENABLED_STATUS)
|
||||
.orderByDesc(Driver::getUpdateTime)
|
||||
.last("LIMIT 20");
|
||||
if (Func.isNotBlank(key)) {
|
||||
@@ -877,27 +885,63 @@ public class ManageWaybillServiceImpl implements IManageWaybillService {
|
||||
@Override
|
||||
public List<AdminVehicleOptionVO> searchVehicles(String keyword) {
|
||||
String key = Func.toStr(keyword, "").trim();
|
||||
LambdaQueryWrapper<Driver> wrapper = Wrappers.<Driver>lambdaQuery()
|
||||
.eq(Driver::getIsDeleted, 0)
|
||||
.isNotNull(Driver::getDrivingVehicle)
|
||||
.ne(Driver::getDrivingVehicle, "")
|
||||
.orderByDesc(Driver::getUpdateTime)
|
||||
// 权威来源是车辆档案表:此前查的是司机表的 driving_vehicle 文本,既看不见没有司机绑定的车,
|
||||
// 也无从判断「是否停用」——停用的车会照样出现在派单选车列表里。
|
||||
LambdaQueryWrapper<TransportVehicle> wrapper = Wrappers.<TransportVehicle>lambdaQuery()
|
||||
.eq(TransportVehicle::getIsDeleted, 0)
|
||||
.eq(TransportVehicle::getStatus, ENABLED_STATUS)
|
||||
.orderByDesc(TransportVehicle::getUpdateTime)
|
||||
.last("LIMIT 30");
|
||||
if (Func.isNotBlank(key)) {
|
||||
wrapper.like(Driver::getDrivingVehicle, key);
|
||||
wrapper.like(TransportVehicle::getPlateNo, key);
|
||||
}
|
||||
java.util.LinkedHashMap<String, AdminVehicleOptionVO> map = new java.util.LinkedHashMap<>();
|
||||
for (Driver d : driverService.list(wrapper)) {
|
||||
String plate = Func.toStr(d.getDrivingVehicle(), "").trim();
|
||||
if (Func.isBlank(plate) || map.containsKey(plate)) {
|
||||
List<TransportVehicle> vehicles = transportVehicleService.list(wrapper);
|
||||
if (Func.isEmpty(vehicles)) {
|
||||
return List.of();
|
||||
}
|
||||
Map<String, String> driverNameByPlate = driverNamesByPlate(vehicles);
|
||||
List<AdminVehicleOptionVO> options = new ArrayList<>();
|
||||
for (TransportVehicle vehicle : vehicles) {
|
||||
String plate = Func.toStr(vehicle.getPlateNo(), "").trim();
|
||||
if (Func.isBlank(plate)) {
|
||||
continue;
|
||||
}
|
||||
AdminVehicleOptionVO vo = new AdminVehicleOptionVO();
|
||||
vo.setVehicleNo(plate);
|
||||
vo.setDriverName(Func.toStr(d.getDriverName(), ""));
|
||||
map.put(plate, vo);
|
||||
vo.setDriverName(Func.toStr(driverNameByPlate.get(plate.toUpperCase()), ""));
|
||||
options.add(vo);
|
||||
}
|
||||
return new java.util.ArrayList<>(map.values());
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 车牌 → 绑定司机姓名
|
||||
* <p>
|
||||
* 绑定关系是司机侧按车牌文本声明的({@code driver.driving_vehicle}),车辆行不存司机,
|
||||
* 所以选车列表要回到司机表补名字;多车牌串的司机匹配不到,这条既有缺陷不在本次改动内。
|
||||
*/
|
||||
private Map<String, String> driverNamesByPlate(List<TransportVehicle> vehicles) {
|
||||
List<String> plates = vehicles.stream()
|
||||
.map(TransportVehicle::getPlateNo)
|
||||
.filter(Func::isNotEmpty)
|
||||
.map(String::toUpperCase)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (plates.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
List<Driver> drivers = driverService.list(Wrappers.<Driver>lambdaQuery()
|
||||
.eq(Driver::getIsDeleted, 0)
|
||||
.in(Driver::getDrivingVehicle, plates)
|
||||
.select(Driver::getDriverName, Driver::getDrivingVehicle));
|
||||
Map<String, String> names = new LinkedHashMap<>();
|
||||
for (Driver driver : drivers) {
|
||||
if (Func.isEmpty(driver.getDrivingVehicle()) || Func.isEmpty(driver.getDriverName())) {
|
||||
continue;
|
||||
}
|
||||
names.putIfAbsent(driver.getDrivingVehicle().toUpperCase(), driver.getDriverName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/** 运输方式字典值 → 展示文案 */
|
||||
|
||||
+3
-3
@@ -71,7 +71,6 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
|
||||
private static final int REMARK_MAX_LENGTH = 200;
|
||||
private static final int DEFAULT_ENABLED_STATUS = 1;
|
||||
private static final int DEFAULT_FALSE = 0;
|
||||
private static final int CERTIFICATION_PENDING = 0;
|
||||
private static final int CERTIFICATION_APPROVED = 1;
|
||||
private static final int CERTIFICATION_REJECTED = 2;
|
||||
|
||||
@@ -133,8 +132,9 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
|
||||
prepare(vehicle);
|
||||
validate(vehicle);
|
||||
checkUniquePlateNo(vehicle);
|
||||
vehicle.setCertificationStatus(CERTIFICATION_PENDING);
|
||||
vehicle.setCertificationRejectReason(null);
|
||||
// 保存档案不碰认证状态:认证状态与生效快照只由认证审核流水维护。
|
||||
// 这里曾强制置 certification_status = 0(审核中),于是管理端每存一次档案,
|
||||
// 已认证车辆就被改回「审核中」,而审核页并没有对应的待审记录——一个退不出去的死状态。
|
||||
return saveOrUpdate(vehicle);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user