From d32978347568fe2a2822e2c988864695f0b47a92 Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Sat, 01 Nov 2025 12:30:42 +0800
Subject: [PATCH] 2025-11-1 zwy 对机场设备列表、绑定设备列表增加权限配置,新增DataScopeAspectPlus切面文件适配MyBatis-Plus。
---
ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java | 31 ++--
ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java | 12 +
ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java | 20 ++
ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java | 189 +++++++++++++++++++++++++++
ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java | 90 +++++++++++-
ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java | 27 ++-
6 files changed, 333 insertions(+), 36 deletions(-)
diff --git a/ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java b/ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java
index 0b561b6..16b7c1f 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java
@@ -95,9 +95,11 @@
* @return
*/
@ApiOperation("设备列表")
+ @PreAuthorize("@ss.hasPermi('device:dock:list')")
@GetMapping("/list")
public ResponseResult<List<DeviceDTO>> getDevices() {
- List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb();
+// List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb();
+ List<DeviceDTO> devicesList = deviceService.getDockList();
return ResponseResult.success(devicesList);
}
@@ -140,6 +142,7 @@
*/
@ApiOperation("绑定设备列表")
@GetMapping("/list/bound")
+ @PreAuthorize("@ss.hasPermi('device:dock:list')")
public ResponseResult<PaginationData<DeviceDTO>> getBoundDevicesWithDomain(Integer domain,
@RequestParam(defaultValue = "1") Long page,
@RequestParam(value = "page_size",
@@ -214,19 +217,19 @@
return ResponseResult.success();
}
- /**
- * 查询机场设备列表
- */
- @PreAuthorize("@ss.hasPermi('device:camera:list')")
- @GetMapping("/test/list")
- @ApiOperation("查询机场设备列表")
- public TableDataInfo list(DeviceDTO device) {
- startPage();
- DeviceEntity deviceEntity = deviceDTO2Entity(device);
- QueryWrapper<DeviceEntity> queryWrapper=new QueryWrapper<>(deviceEntity);
- List<DeviceEntity> list = deviceService.selectList(queryWrapper);
- return getDataTable(list);
- }
+// /**
+// * 查询机场设备列表
+// */
+// @PreAuthorize("@ss.hasPermi('device:camera:list')")
+// @GetMapping("/test/list")
+// @ApiOperation("查询机场设备列表")
+// public TableDataInfo list(DeviceDTO device) {
+// startPage();
+// DeviceEntity deviceEntity = deviceDTO2Entity(device);
+// QueryWrapper<DeviceEntity> queryWrapper=new QueryWrapper<>(deviceEntity);
+// List<DeviceEntity> list = deviceService.selectList(queryWrapper);
+// return getDataTable(list);
+// }
private DeviceEntity deviceDTO2Entity(DeviceDTO dto) {
DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder();
diff --git a/ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java b/ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java
index 1f9e49c..44548e0 100644
--- a/ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java
+++ b/ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java
@@ -1,29 +1,34 @@
package com.ard.common.annotation;
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * 数据权限过滤注解
- *
+ * MyBatis-Plus 专用数据权限过滤注解
+ * 用于标注需要进行数据权限过滤的方法,配合 QueryWrapper 使用
+ *
* @author ard
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
-public @interface DataScopePlus
-{
+public @interface DataScopePlus {
/**
- * 部门表的别名
+ * 部门表的别名(需与 Wrapper 中设置的表别名一致)
*/
- public String deptAlias() default "";
+ String deptAlias() default "";
/**
- * 用户表的别名
+ * 用户表的别名(需与 Wrapper 中设置的表别名一致)
*/
- public String userAlias() default "";
+ String userAlias() default "";
/**
- * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
+ * 权限字符(用于匹配角色对应的权限)
+ * 多个权限用逗号分隔,默认从 PermissionContextHolder 获取
*/
- public String permission() default "";
+ String permission() default "";
}
diff --git a/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java b/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java
new file mode 100644
index 0000000..ad12e1f
--- /dev/null
+++ b/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java
@@ -0,0 +1,189 @@
+package com.ard.framework.aspectj;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.stereotype.Component;
+import com.ard.common.annotation.DataScopePlus;
+import com.ard.common.constant.UserConstants;
+import com.ard.common.core.domain.entity.SysRole;
+import com.ard.common.core.domain.entity.SysUser;
+import com.ard.common.core.domain.model.LoginUser;
+import com.ard.common.core.text.Convert;
+import com.ard.common.utils.SecurityUtils;
+import com.ard.common.utils.StringUtils;
+import com.ard.framework.security.context.PermissionContextHolder;
+import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
+
+/**
+ * MyBatis-Plus 专用数据权限切面
+ * 仅处理带有 @MyBatisPlusDataScope 注解的方法,且参数包含 QueryWrapper/LambdaQueryWrapper
+ */
+@Aspect
+@Component
+public class DataScopeAspectPlus {
+ /**
+ * 全部数据权限
+ */
+ public static final String DATA_SCOPE_ALL = "1";
+
+ /**
+ * 自定数据权限(部门维度)
+ */
+ public static final String DATA_SCOPE_CUSTOM = "2";
+
+ /**
+ * 部门数据权限(仅本部门)
+ */
+ public static final String DATA_SCOPE_DEPT = "3";
+
+ /**
+ * 部门及以下数据权限
+ */
+ public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+ /**
+ * 仅本人数据权限
+ */
+ public static final String DATA_SCOPE_SELF = "5";
+
+ /**
+ * 拦截带有 @MyBatisPlusDataScope 注解的方法,处理部门权限
+ */
+ @Before("@annotation(dataScope)")
+ public void doBefore(JoinPoint point, DataScopePlus dataScope) throws Throwable {
+ // 遍历方法参数,找到 Wrapper 类型的参数
+ AbstractWrapper<?, ?, ?> wrapper = findWrapperFromArgs(point.getArgs());
+ if (wrapper != null) {
+ handleDataScope(wrapper, dataScope); // 找到后执行权限逻辑
+ }
+ }
+
+ /**
+ * 从方法参数中找到 AbstractWrapper 类型的参数(兼容任意位置)
+ */
+ private AbstractWrapper<?, ?, ?> findWrapperFromArgs(Object[] args) {
+ if (args == null || args.length == 0) {
+ return null;
+ }
+ for (Object arg : args) {
+ // 判断是否为 MyBatis-Plus 的 Wrapper 类型
+ if (arg instanceof AbstractWrapper) {
+ return (AbstractWrapper<?, ?, ?>) arg;
+ }
+ }
+ return null; // 没找到 Wrapper,不执行权限过滤
+ }
+
+ /**
+ * 处理部门数据权限
+ */
+ protected void handleDataScope(AbstractWrapper<?, ?, ?> wrapper, DataScopePlus dataScope) {
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ if (StringUtils.isNotNull(loginUser)) {
+ SysUser currentUser = loginUser.getUser();
+ // 超级管理员跳过权限过滤
+ if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
+ String permission = StringUtils.defaultIfEmpty(
+ dataScope.permission(),
+ PermissionContextHolder.getContext()
+ );
+ // 生成部门权限条件并注入 Wrapper
+ generateDeptScopeSql(wrapper, currentUser, dataScope.deptAlias(), dataScope.userAlias(), permission);
+ }
+ }
+ }
+
+ /**
+ * 仅生成部门维度的权限 SQL(移除用户相关逻辑)
+ */
+ private void generateDeptScopeSql(AbstractWrapper<?, ?, ?> wrapper, SysUser user,
+ String deptAlias, String userAlias, String permission) {
+ StringBuilder sqlBuilder = new StringBuilder();
+ List<String> conditions = new ArrayList<>();
+ List<String> customRoleIds = new ArrayList<>();
+
+ // 收集拥有自定义部门权限的角色ID
+ user.getRoles().forEach(role -> {
+ if (DATA_SCOPE_CUSTOM.equals(role.getDataScope())
+ && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL)
+ && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) {
+ customRoleIds.add(Convert.toStr(role.getRoleId()));
+ }
+ });
+
+ // 遍历角色生,生成对应的数据权限条件
+ for (SysRole role : user.getRoles()) {
+ String dataScope = role.getDataScope();
+ // 跳过已处理的权限类型或禁用角色
+ if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) {
+ continue;
+ }
+ // 跳过不包含目标权限的角色
+ if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) {
+ continue;
+ }
+
+ // 仅保留部门相关的权限逻辑(移除用户权限)
+ if (DATA_SCOPE_ALL.equals(dataScope)) {
+ // 全部权限:清空条件
+ sqlBuilder.setLength(0);
+ conditions.add(dataScope);
+ break;
+ } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
+ // 自定义部门权限:关联角色-部门表
+ if (customRoleIds.size() > 1) {
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id IN ({}) )",
+ deptAlias, String.join(",", customRoleIds)
+ ));
+ } else {
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id = {} )",
+ deptAlias, role.getRoleId()
+ ));
+ }
+ } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
+ // 本部门权限:仅当前用户所属部门
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.dept_id = {}", deptAlias, user.getDeptId()
+ ));
+ } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
+ // 本部门及下属部门权限:通过部门表 ancestors 字段递归查询
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = {} OR find_in_set({}, ancestors) )",
+ deptAlias, user.getDeptId(), user.getDeptId()
+ ));
+ } else if (DATA_SCOPE_SELF.equals(dataScope)) {
+ // 仅本人权限:关联用户表
+ if (StringUtils.isNotBlank(userAlias)) {
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.user_id = {}", userAlias, user.getUserId()
+ ));
+ } else {
+ // 无用户别名时强制过滤(不查询任何数据)
+ sqlBuilder.append(StringUtils.format(
+ " OR {}.dept_id = 0", deptAlias
+ ));
+ }
+ }
+ conditions.add(dataScope);
+ }
+
+ // 无匹配角色时,强制过滤(不查询任何数据)
+ if (conditions.isEmpty()) {
+ sqlBuilder.append(StringUtils.format(" OR {}.dept_id = 0", deptAlias));
+ }
+
+ // 注入部门权限条件
+ if (sqlBuilder.length() > 0) {
+ String dataScopeSql = sqlBuilder.substring(4); // 去除开头的 "OR "
+ wrapper.apply(dataScopeSql);
+ }
+ }
+}
diff --git a/ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java b/ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java
index cffbb16..2fd4946 100644
--- a/ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java
+++ b/ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java
@@ -1,7 +1,14 @@
package com.ard.sample.manage.mapper;
+import com.ard.common.annotation.DataScopePlus;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ard.sample.manage.model.entity.DeviceEntity;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
/**
*
@@ -11,4 +18,17 @@
*/
public interface IDeviceMapper extends BaseMapper<DeviceEntity> {
+ @Select("SELECT m.* " +
+ "FROM manage_device m " +
+ "left join sys_dept d on d.dept_id = m.dept_id " +
+ "${ew.customSqlSegment}")
+ @DataScopePlus(deptAlias = "d", permission = "device:dock:list")
+ List<DeviceEntity> selectListPer(@Param("ew") Wrapper<DeviceEntity> queryWrapper);
+
+ @Select("SELECT m.* " +
+ "FROM manage_device m " +
+ "left join sys_dept d on d.dept_id = m.dept_id " +
+ "${ew.customSqlSegment}")
+ @DataScopePlus(deptAlias = "d", permission = "device:dock:list")
+ <P extends IPage<DeviceEntity>> P selectPage(P page, @Param("ew") Wrapper<DeviceEntity> queryWrapper);
}
\ No newline at end of file
diff --git a/ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java b/ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java
index 42d8f10..f8123a1 100644
--- a/ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java
+++ b/ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java
@@ -104,6 +104,16 @@
void pushDeviceOnlineTopo(Collection<ConcurrentWebSocketSession> sessions, String sn, String gatewaySn);
/**
+ * 根据部门权限获取机场列表
+ * */
+ List<DeviceDTO> getDockList();
+
+ void spliceDeviceTopoPer(DeviceDTO gateway);
+
+// @DataScope(deptAlias = "d")
+ List<DeviceDTO> getDockListByParams(DeviceQueryParam param);
+
+ /**
* Query the information of the device according to the sn of the device.
* @param sn device sn
* @return
@@ -232,5 +242,5 @@
Boolean checkDeviceOnline(String sn);
- public List<DeviceEntity> selectList(Wrapper<DeviceEntity> queryWrapper);
+// public List<DeviceEntity> selectList(Wrapper<DeviceEntity> queryWrapper);
}
\ No newline at end of file
diff --git a/ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java b/ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java
index fc5dd06..816c596 100644
--- a/ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java
+++ b/ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java
@@ -1,10 +1,12 @@
package com.ard.sample.manage.service.impl;
import com.alibaba.fastjson2.JSONObject;
+import com.ard.common.annotation.DataScope;
import com.ard.common.annotation.DataScopePlus;
import com.ard.system.authorize.service.AuthorizeService;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ard.common.core.domain.entity.SysDept;
@@ -347,6 +349,68 @@
// payloads
// subDevice.setPayloadsList(payloadService.getDevicePayloadEntitiesByDeviceSn(gateway.getChildDeviceSn()));
+ }
+
+ @Override
+ public List<DeviceDTO> getDockList() {
+ List<DeviceDTO> devicesList = this.getDockListByParams(
+ DeviceQueryParam.builder()
+ .domains(List.of(DeviceDomainEnum.GATEWAY.getVal(), DeviceDomainEnum.DOCK.getVal()))
+ .build());
+
+ devicesList.stream()
+ .filter(gateway -> DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() ||
+ RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn()))
+ .forEach(this::spliceDeviceTopoPer);
+
+ return devicesList;
+ }
+
+ @Override
+ public void spliceDeviceTopoPer(DeviceDTO gateway) {
+
+ gateway.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn()));
+
+ // sub device
+ if (!StringUtils.hasText(gateway.getChildDeviceSn())) {
+ return;
+ }
+
+ DeviceDTO subDevice =
+ getDevicesByParams(DeviceQueryParam.builder().deviceSn(gateway.getChildDeviceSn()).build()).get(0);
+ subDevice.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + subDevice.getDeviceSn()));
+ gateway.setChildren(subDevice);
+
+ // payloads
+ // subDevice.setPayloadsList(payloadService.getDevicePayloadEntitiesByDeviceSn(gateway.getChildDeviceSn()));
+ }
+
+ //@DataScope(deptAlias = "d")
+ @Override
+ public List<DeviceDTO> getDockListByParams(DeviceQueryParam param){
+
+ return mapper.selectListPer(
+ new LambdaQueryWrapper<DeviceEntity>()
+ .eq(StringUtils.hasText(param.getDeviceSn()),
+ DeviceEntity::getDeviceSn, param.getDeviceSn())
+ .eq(param.getDeviceType() != null,
+ DeviceEntity::getDeviceType, param.getDeviceType())
+ .eq(param.getSubType() != null,
+ DeviceEntity::getSubType, param.getSubType())
+ .eq(StringUtils.hasText(param.getChildSn()),
+ DeviceEntity::getChildSn, param.getChildSn())
+ .and(!CollectionUtils.isEmpty(param.getDomains()), wrapper -> {
+ for (Integer domain : param.getDomains()) {
+ wrapper.eq(DeviceEntity::getDomain, domain).or();
+ }
+ })
+ .eq(param.getBoundStatus() != null, DeviceEntity::getBoundStatus,
+ param.getBoundStatus())
+ .orderBy(param.isOrderBy(),
+ param.isAsc(), DeviceEntity::getId))
+ .stream()
+ .map(this::deviceEntityConvertToDTO)
+ .collect(Collectors.toList());
}
@Override
@@ -874,11 +938,17 @@
Long pageSize, Integer domain, Long deptId) {
Page<DeviceEntity> pagination = mapper.selectPage(new Page<>(page, pageSize),
- new LambdaQueryWrapper<DeviceEntity>()
- .eq(DeviceEntity::getDomain, domain)
- .eq(deptId != null, DeviceEntity::getDeptId, deptId)
- .isNotNull(DeviceEntity::getDeptId));
-// .eq(DeviceEntity::getBoundStatus, true));
+ new QueryWrapper<DeviceEntity>()
+ .eq("m.domain", domain)
+ .eq(deptId != null, "m.dept_id", deptId)
+ .isNotNull("m.dept_id"));
+
+ //以前的写法
+// new LambdaQueryWrapper<DeviceEntity>()
+// .eq(DeviceEntity::getDomain, domain)
+// .eq(deptId != null, DeviceEntity::getDeptId, deptId)
+// .isNotNull(DeviceEntity::getDeptId);
+
List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO)
.peek(device -> {
device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()));
@@ -1221,9 +1291,9 @@
messageSender.publish(topic, builder.build());
}
- @DataScopePlus(deptAlias="d")
- @Override
- public List<DeviceEntity> selectList(Wrapper<DeviceEntity> queryWrapper) {
- return mapper.selectList(queryWrapper);
- }
+// @DataScopePlus(deptAlias="d")
+// @Override
+// public List<DeviceEntity> selectList(Wrapper<DeviceEntity> queryWrapper) {
+// return mapper.selectList(queryWrapper);
+// }
}
\ No newline at end of file
--
Gitblit v1.9.3