🐛 认证审核放行小程序调度端管理身份
This commit is contained in:
+9
-1
@@ -50,6 +50,7 @@ import org.springblade.transport.service.ITransportCertRecordService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.support.CertAuditPermissions;
|
||||
import org.springblade.transport.support.CertificationRuleSupport;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
import org.springblade.transport.wrapper.TransportCertRecordWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -79,6 +80,7 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
||||
private final ITransportVehicleService transportVehicleService;
|
||||
private final IDriverService driverService;
|
||||
private final IPermissionHandler permissionHandler;
|
||||
private final MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
|
||||
@Override
|
||||
public IPage<TransportCertRecordVO> pendingPage(String bizType, String bizKey, String driverName, String driverMobile,
|
||||
@@ -185,8 +187,14 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
||||
|
||||
@Override
|
||||
public CertAuditCapabilityVO capability() {
|
||||
// 使用与三个审核接口 @PreAuth 相同的菜单码常量;表达式由框架求值,此处直接查菜单。
|
||||
// 与三个审核接口 @PreAuth 同一口径:小程序调度端管理身份(token roleName=admin)查不到菜单码,直接放行;
|
||||
// 其余账号查菜单。
|
||||
CertAuditCapabilityVO capability = new CertAuditCapabilityVO();
|
||||
if (mpUserDataScopeSupport.isMpDispatcher()) {
|
||||
capability.setCanView(true);
|
||||
capability.setCanAudit(true);
|
||||
return capability;
|
||||
}
|
||||
capability.setCanView(permissionHandler.hasMenu(CertAuditPermissions.DRIVER_VIEW_MENU)
|
||||
|| permissionHandler.hasMenu(CertAuditPermissions.VEHICLE_VIEW_MENU));
|
||||
capability.setCanAudit(permissionHandler.hasMenu(CertAuditPermissions.DRIVER_AUDIT_MENU)
|
||||
|
||||
+18
-4
@@ -34,6 +34,10 @@ package org.springblade.transport.support;
|
||||
* {@code AUDIT_VIEW} 与 {@code AUDIT_ACTION} 由上述常量拼接,结果仍是编译期常量。
|
||||
* 三个审核接口的 {@code @PreAuth} 与 {@code CertAuditServiceImpl#capability()} 使用同一组菜单码常量。
|
||||
* <p>
|
||||
* 小程序调度端管理身份不走菜单码:小程序登录不经过 {@code blade_user},token 的 {@code roleId}
|
||||
* 是占位值 {@code "0"}、{@code roleName} 取 {@code blade_mp_user.identities}(调度端管理员为 {@code admin}),
|
||||
* 查不到 {@code blade_role_menu},因此两个表达式末尾各放行一个角色名 {@code admin}。
|
||||
* <p>
|
||||
* 表达式由框架交给 SpEL 求值,root object 是 {@code AuthFun},函数名须与它的公开方法一致({@code hasMenu})。
|
||||
* {@code CertAuditPermissionsTest} 会解析并求值这两个表达式。
|
||||
*
|
||||
@@ -62,18 +66,28 @@ public final class CertAuditPermissions {
|
||||
public static final String VEHICLE_AUDIT_MENU = "cert_certification_audit_vehicle_audit";
|
||||
|
||||
/**
|
||||
* 待审列表与审核详情:司机或车辆任一查看菜单即可
|
||||
* 小程序调度端管理身份的角色名
|
||||
* <p>
|
||||
* 取值来自 {@code blade_mp_user.identities},由登录时拼进 token 的 {@code roleName};
|
||||
* 不是 {@code blade_role} 里的角色别名。
|
||||
*/
|
||||
public static final String MP_DISPATCHER_ROLE = "admin";
|
||||
|
||||
/**
|
||||
* 待审列表与审核详情:司机或车辆任一查看菜单,或小程序调度端管理身份
|
||||
* <p>
|
||||
* 由上面两个查看菜单码拼成,结果仍是编译期常量,{@code @PreAuth} 可以直接引用。
|
||||
*/
|
||||
public static final String AUDIT_VIEW =
|
||||
"hasMenu('" + DRIVER_VIEW_MENU + "') or hasMenu('" + VEHICLE_VIEW_MENU + "')";
|
||||
"hasMenu('" + DRIVER_VIEW_MENU + "') or hasMenu('" + VEHICLE_VIEW_MENU + "')"
|
||||
+ " or hasRole('" + MP_DISPATCHER_ROLE + "')";
|
||||
|
||||
/**
|
||||
* 审核动作:司机或车辆任一「审核」按钮权限即可
|
||||
* 审核动作:司机或车辆任一「审核」按钮权限,或小程序调度端管理身份
|
||||
*/
|
||||
public static final String AUDIT_ACTION =
|
||||
"hasMenu('" + DRIVER_AUDIT_MENU + "') or hasMenu('" + VEHICLE_AUDIT_MENU + "')";
|
||||
"hasMenu('" + DRIVER_AUDIT_MENU + "') or hasMenu('" + VEHICLE_AUDIT_MENU + "')"
|
||||
+ " or hasRole('" + MP_DISPATCHER_ROLE + "')";
|
||||
|
||||
private CertAuditPermissions() {
|
||||
}
|
||||
|
||||
+30
-1
@@ -21,6 +21,7 @@ import org.springblade.transport.pojo.vo.CertAuditDetailVO;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.service.ITransportCertRecordService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -31,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -57,6 +59,7 @@ class CertAuditServiceImplTest {
|
||||
private ITransportVehicleService transportVehicleService;
|
||||
private IDriverService driverService;
|
||||
private IPermissionHandler permissionHandler;
|
||||
private MpUserDataScopeSupport mpUserDataScopeSupport;
|
||||
private CertAuditServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
@@ -76,8 +79,9 @@ class CertAuditServiceImplTest {
|
||||
transportVehicleService = mock(ITransportVehicleService.class);
|
||||
driverService = mock(IDriverService.class);
|
||||
permissionHandler = mock(IPermissionHandler.class);
|
||||
mpUserDataScopeSupport = mock(MpUserDataScopeSupport.class);
|
||||
service = new CertAuditServiceImpl(certRecordService, certRecordMapper, transportVehicleService, driverService,
|
||||
permissionHandler);
|
||||
permissionHandler, mpUserDataScopeSupport);
|
||||
// 目标对象的写入默认成功;「写入一行都没命中」由专门的用例覆盖
|
||||
when(transportVehicleService.updateById(any(TransportVehicle.class))).thenReturn(true);
|
||||
when(driverService.updateById(any(Driver.class))).thenReturn(true);
|
||||
@@ -431,6 +435,31 @@ class CertAuditServiceImplTest {
|
||||
assertTrue(capability.getCanAudit());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("能力判定:小程序调度端管理身份查不到菜单码,查看与审核都放行")
|
||||
void capabilityForMpDispatcher() {
|
||||
// 小程序账号的 token roleId 是占位 "0",菜单表里查不到任何一条,只能按身份放行
|
||||
when(mpUserDataScopeSupport.isMpDispatcher()).thenReturn(true);
|
||||
|
||||
CertAuditCapabilityVO capability = service.capability();
|
||||
|
||||
assertTrue(capability.getCanView(), "调度端管理员应能进审核页");
|
||||
assertTrue(capability.getCanAudit(), "调度端管理员应能执行审核");
|
||||
verify(permissionHandler, never()).hasMenu(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("能力判定:非小程序调度端仍只按菜单码判定")
|
||||
void capabilityNonDispatcherUsesMenus() {
|
||||
when(mpUserDataScopeSupport.isMpDispatcher()).thenReturn(false);
|
||||
when(permissionHandler.hasMenu(DRIVER_VIEW_MENU)).thenReturn(true);
|
||||
|
||||
CertAuditCapabilityVO capability = service.capability();
|
||||
|
||||
assertTrue(capability.getCanView());
|
||||
assertFalse(capability.getCanAudit(), "身份分支不放行时,仍要审核菜单码才能审核");
|
||||
}
|
||||
|
||||
// -------------------------------------------- 驳回原因的展示缓存
|
||||
|
||||
@Test
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ import org.springblade.transport.service.IDriverAppService;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.service.ITransportCertRecordService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.support.MpUserDataScopeSupport;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
@@ -99,7 +100,7 @@ class PersonCertRoundTripTest {
|
||||
driverCertService = new DriverCertServiceImpl(driverAppService, driverService,
|
||||
transportVehicleService, certRecordService);
|
||||
certAuditService = new CertAuditServiceImpl(certRecordService, certRecordMapper, transportVehicleService, driverService,
|
||||
mock(IPermissionHandler.class));
|
||||
mock(IPermissionHandler.class), mock(MpUserDataScopeSupport.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+37
-1
@@ -82,11 +82,47 @@ class CertAuditPermissionsTest {
|
||||
assertTrue(CertAuditPermissions.AUDIT_VIEW.contains("cert_certification_audit_vehicle"));
|
||||
assertTrue(CertAuditPermissions.AUDIT_ACTION.contains("cert_certification_audit_driver_audit"));
|
||||
assertTrue(CertAuditPermissions.AUDIT_ACTION.contains("cert_certification_audit_vehicle_audit"));
|
||||
// 不应再出现角色名判断:库里不存在 admin 角色,且会漏掉「运管」岗位
|
||||
// 角色放行只针对小程序调度端管理身份(blade_mp_user.identities 里的 admin,登录后写进 token 的 roleName),
|
||||
// 不能改用 hasAnyRole 把 blade_role 的 administrator 也一并放行
|
||||
assertTrue(CertAuditPermissions.AUDIT_VIEW.contains("hasRole('" + CertAuditPermissions.MP_DISPATCHER_ROLE + "')"));
|
||||
assertTrue(CertAuditPermissions.AUDIT_ACTION.contains("hasRole('" + CertAuditPermissions.MP_DISPATCHER_ROLE + "')"));
|
||||
assertFalse(CertAuditPermissions.AUDIT_VIEW.contains("hasAnyRole"));
|
||||
assertFalse(CertAuditPermissions.AUDIT_ACTION.contains("hasAnyRole"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("小程序调度端管理身份(token roleName=admin)无菜单码也可查看与审核")
|
||||
void mpDispatcherRoleAllowed() {
|
||||
AuthFun mpDispatcher = mock(AuthFun.class);
|
||||
when(mpDispatcher.hasRole("admin")).thenReturn(true);
|
||||
|
||||
assertTrue(evaluate(CertAuditPermissions.AUDIT_VIEW, mpDispatcher),
|
||||
"小程序账号没有 roleId,查不到菜单码");
|
||||
assertTrue(evaluate(CertAuditPermissions.AUDIT_ACTION, mpDispatcher),
|
||||
"小程序调度端管理员需要能执行审核");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("小程序司机身份(token roleName=driver)不得进入审核")
|
||||
void mpDriverRoleDenied() {
|
||||
AuthFun mpDriver = mock(AuthFun.class);
|
||||
when(mpDriver.hasRole("driver")).thenReturn(true);
|
||||
|
||||
assertFalse(evaluate(CertAuditPermissions.AUDIT_VIEW, mpDriver));
|
||||
assertFalse(evaluate(CertAuditPermissions.AUDIT_ACTION, mpDriver));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("超管角色名(administrator)不因角色放行,Web 侧仍须菜单码")
|
||||
void administratorRoleAloneDoesNotPass() {
|
||||
AuthFun administrator = mock(AuthFun.class);
|
||||
when(administrator.hasRole("administrator")).thenReturn(true);
|
||||
|
||||
assertFalse(evaluate(CertAuditPermissions.AUDIT_VIEW, administrator),
|
||||
"表达式只放行 admin 角色名;超管走 hasMenu 由框架内部放行");
|
||||
assertFalse(evaluate(CertAuditPermissions.AUDIT_ACTION, administrator));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用与框架相同的方式求值:AuthFun 作为 root object
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user