diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverVO.java index 5b22c7ac..1072b4be 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverVO.java @@ -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-已到期 */ diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportVehicleVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportVehicleVO.java index bd4ab362..97106813 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportVehicleVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TransportVehicleVO.java @@ -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-已到期 */ diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveController.java index e38ba183..35442fb0 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveController.java @@ -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 capability() { + return R.data(mpUserDataScopeSupport.isMpDispatcher()); + } + } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveDriverController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveDriverController.java new file mode 100644 index 00000000..7390edd6 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveDriverController.java @@ -0,0 +1,171 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * 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; + +/** + * 调度端司机档案管理(小程序) + *

+ * 对外完整路径:{@code /api/blade-transport/archive/manage/driver/**} + * (网关 StripPrefix 去掉 {@code blade-transport} 后落入 {@code /archive/manage/driver/**}), + * 同时兼容未去前缀直连。 + *

+ * 为什么不复用管理端的 {@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> 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 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)); + } + } + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveVehicleController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveVehicleController.java new file mode 100644 index 00000000..b6f930a1 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageArchiveVehicleController.java @@ -0,0 +1,169 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * 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; + +/** + * 调度端车辆档案管理(小程序) + *

+ * 对外完整路径:{@code /api/blade-transport/archive/manage/vehicle/**} + * (网关 StripPrefix 去掉 {@code blade-transport} 后落入 {@code /archive/manage/vehicle/**}), + * 同时兼容未去前缀直连。 + *

+ * 与管理端 {@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> 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 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)); + } + } + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml index 2f1b5e2a..1cdc59b7 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/DriverMapper.xml @@ -147,6 +147,11 @@ is_deleted = 0 + + + + AND (driver_name LIKE #{driverKeywordLike} OR mobile LIKE #{driverKeywordLike}) + AND driver_name LIKE #{driverNameLike} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml index 0d5857fd..f261e329 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportVehicleMapper.xml @@ -116,6 +116,11 @@ is_deleted = 0 + + + + AND (plate_no LIKE #{vehicleKeywordLike} OR vin LIKE #{vehicleKeywordLike} OR owner_name LIKE #{vehicleKeywordLike}) +