From 7ec5ff190006159cef17ef34cd7e61764e1467b3 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Fri, 31 Oct 2025 16:08:01 +0800
Subject: [PATCH] 增加mybatisplus数据范围过滤AOP和示例

---
 ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java        |   50 +++++++++
 ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java         |    5 +
 ard-framework/src/main/java/com/ard/framework/aspectj/DataScopePlusAspect.java     |  136 +++++++++++++++++++++++++++
 ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java |   33 ++++--
 ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java              |   29 +++++
 5 files changed, 239 insertions(+), 14 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 e11e1e5..0b561b6 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
@@ -1,5 +1,8 @@
 package com.ard.web.controller.sample;
 
+import com.ard.common.core.controller.BaseController;
+import com.ard.common.core.page.TableDataInfo;
+import com.ard.device.camera.domain.ArdCamera;
 import com.ard.sample.common.error.CommonErrorEnum;
 import com.ard.sample.common.model.PaginationData;
 import com.ard.sample.common.model.ResponseResult;
@@ -8,19 +11,27 @@
 import com.ard.sample.component.mqtt.model.CommonTopicResponse;
 import com.ard.sample.manage.model.dto.DeviceDTO;
 import com.ard.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
+import com.ard.sample.manage.model.entity.DeviceEntity;
+import com.ard.sample.manage.model.enums.DeviceFirmwareStatusEnum;
 import com.ard.sample.manage.model.enums.DeviceSetPropertyEnum;
 import com.ard.sample.manage.model.receiver.StatusGatewayReceiver;
 import com.ard.sample.manage.service.IDeviceService;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fasterxml.jackson.databind.JsonNode;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.time.ZoneId;
 import java.util.List;
 import java.util.Optional;
+
+import static com.ard.common.utils.PageUtils.startPage;
 
 /**
  * @author sean.zhou
@@ -31,7 +42,7 @@
 @RestController
 @Slf4j
 @RequestMapping("/dock/device")
-public class DeviceController {
+public class DeviceController extends BaseController {
 
     @Autowired
     private IDeviceService deviceService;
@@ -202,4 +213,41 @@
         deviceService.devicePropertySet(dockSn, propertyEnumOpt.get(), param.get(property));
         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);
+    }
+
+    private DeviceEntity deviceDTO2Entity(DeviceDTO dto) {
+        DeviceEntity.DeviceEntityBuilder builder = DeviceEntity.builder();
+        if (dto == null) {
+            return builder.build();
+        }
+
+        return builder.deviceSn(dto.getDeviceSn())
+                .userId(dto.getUserId())
+                .nickname(dto.getNickname())
+                .boundStatus(dto.getBoundStatus())
+                .loginTime(dto.getLoginTime() != null ?
+                        dto.getLoginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
+                .boundTime(dto.getBoundTime() != null ?
+                        dto.getBoundTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
+                .childSn(dto.getChildDeviceSn())
+                .domain(dto.getDomain())
+                .firmwareVersion(dto.getFirmwareVersion())
+                .workType(dto.getWorkType())
+                .compatibleStatus(dto.getFirmwareStatus() == null ? null :
+                        DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE != DeviceFirmwareStatusEnum.find(dto.getFirmwareStatus()))
+                .build();
+    }
 }
\ No newline at end of file
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
new file mode 100644
index 0000000..1f9e49c
--- /dev/null
+++ b/ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java
@@ -0,0 +1,29 @@
+package com.ard.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限过滤注解
+ * 
+ * @author ard
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataScopePlus
+{
+    /**
+     * 部门表的别名
+     */
+    public String deptAlias() default "";
+
+    /**
+     * 用户表的别名
+     */
+    public String userAlias() default "";
+
+    /**
+     * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
+     */
+    public String permission() default "";
+}
diff --git a/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopePlusAspect.java b/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopePlusAspect.java
new file mode 100644
index 0000000..60ad5bb
--- /dev/null
+++ b/ard-framework/src/main/java/com/ard/framework/aspectj/DataScopePlusAspect.java
@@ -0,0 +1,136 @@
+package com.ard.framework.aspectj;
+
+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.query.QueryWrapper;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import java.util.ArrayList;
+import java.util.List;
+
+@Aspect
+@Component
+public class DataScopePlusAspect {
+    /**
+     * 全部数据权限
+     */
+    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";
+
+
+    @Pointcut("@annotation(com.ard.common.annotation.DataScopePlus)")
+    public void dataScopePointcut() {
+    }
+
+    @Before("@annotation(controllerDataScopePlus)")
+    public void doBefore(JoinPoint joinPoint, DataScopePlus controllerDataScopePlus) {
+        handleDataScope(joinPoint, controllerDataScopePlus);
+    }
+
+    protected void handleDataScope(final JoinPoint joinPoint, DataScopePlus controllerDataScope) {
+        // 获取当前用户
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNotNull(loginUser)) {
+            SysUser currentUser = loginUser.getUser();
+            if (currentUser != null && !currentUser.isAdmin()) {
+                String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(),
+                        PermissionContextHolder.getContext());
+                dataScopeFilter(joinPoint, currentUser, permission);
+            }
+        }
+    }
+
+    private void dataScopeFilter(JoinPoint joinPoint, SysUser user, String permission) {
+        StringBuilder sqlString = new StringBuilder();
+        List<String> conditions = new ArrayList<>();
+        List<String> scopeCustomIds = new ArrayList<>();
+
+        // 获取用户角色并遍历进行权限检查
+        user.getRoles().forEach(role -> {
+            if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(),
+                    UserConstants.ROLE_NORMAL)) {
+                if (StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) {
+                    scopeCustomIds.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)) {
+                sqlString = new StringBuilder();
+                conditions.add(dataScope);
+                break;
+            } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
+                // 自定义数据权限
+                if (scopeCustomIds.size() > 1) {
+                    sqlString.append(StringUtils.format(" OR dept_id IN (SELECT dept_id FROM sys_role_dept WHERE " +
+                            "role_id IN ({}) )", String.join(",", scopeCustomIds)));
+                } else {
+                    sqlString.append(StringUtils.format(" OR dept_id IN (SELECT dept_id FROM sys_role_dept WHERE " +
+                            "role_id = {} )", role.getRoleId()));
+                }
+            } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
+                // 部门数据权限
+                sqlString.append(StringUtils.format(" OR dept_id = {}", user.getDeptId()));
+            } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
+                // 部门及以下数据权限
+                sqlString.append(StringUtils.format(" OR dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = {} " +
+                        "OR find_in_set({}, ancestors))", user.getDeptId(), user.getDeptId()));
+            } else if (DATA_SCOPE_SELF.equals(dataScope)) {
+                // 仅本人数据权限
+                sqlString.append(StringUtils.format(" OR user_id = {}", user.getUserId()));
+            }
+            conditions.add(dataScope);
+        }
+        QueryWrapper<?> queryWrapper = getQueryWrapper(joinPoint);
+        if (StringUtils.isNotBlank(sqlString.toString())) {
+            queryWrapper.apply(sqlString.substring(4));
+        }
+    }
+
+    private QueryWrapper<?> getQueryWrapper(JoinPoint joinPoint) {
+        Object[] args = joinPoint.getArgs();
+        if (args != null && args.length > 0 && args[0] instanceof QueryWrapper) {
+            return (QueryWrapper<?>) args[0];
+        }
+        return null;
+    }
+}
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 dd5f82a..42d8f10 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
@@ -8,10 +8,12 @@
 import com.ard.sample.manage.model.dto.DeviceDTO;
 import com.ard.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
 import com.ard.sample.manage.model.dto.TopologyDeviceDTO;
+import com.ard.sample.manage.model.entity.DeviceEntity;
 import com.ard.sample.manage.model.enums.DeviceSetPropertyEnum;
 import com.ard.sample.manage.model.param.DeviceQueryParam;
 import com.ard.sample.manage.model.receiver.FirmwareVersionReceiver;
 import com.ard.sample.manage.model.receiver.StatusGatewayReceiver;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.fasterxml.jackson.databind.JsonNode;
 import org.springframework.messaging.Message;
 import org.springframework.messaging.MessageHeaders;
@@ -228,4 +230,7 @@
      * @return
      */
     Boolean checkDeviceOnline(String sn);
+
+
+    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 7466075..fc5dd06 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,7 +1,9 @@
 package com.ard.sample.manage.service.impl;
 
 import com.alibaba.fastjson2.JSONObject;
+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.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -264,7 +266,7 @@
     /**
      * 发布状态回复消息
      *
-     * @param sn 序列号
+     * @param sn       序列号
      * @param response 通用主题响应对象
      */
     @Override
@@ -312,7 +314,7 @@
 
     /**
      * 设备列表
-     * */
+     */
     @Override
     public List<DeviceDTO> getDevicesTopoForWeb() {
         List<DeviceDTO> devicesList = this.getDevicesByParams(
@@ -542,7 +544,7 @@
             return Optional.of(deviceEntity);
         }
         // 2025-10-20 zwy 添加时增加授权验证及数量限制,限制为3台机场设备,超出则抛出异常
-        try{
+        try {
             // 查询数据库中机器设备数量
             // 根据参数获取设备列表
             List<DeviceDTO> devicesList = this.getDevicesByParams(
@@ -552,8 +554,7 @@
                             .build());
             // 验证授权数量
             validateAuthorization(devicesList.size(), 1);
-        }
-        catch (Exception e) {
+        } catch (Exception e) {
             return Optional.empty();
         }
         // 调用验证方法
@@ -863,9 +864,9 @@
     /**
      * 根据域名分页获取已绑定的设备信息
      *
-     * @param page      当前页码
-     * @param pageSize  每页显示数量
-     * @param domain    域名
+     * @param page     当前页码
+     * @param pageSize 每页显示数量
+     * @param domain   域名
      * @return PaginationData<DeviceDTO> 包含分页信息的设备DTO列表
      */
     @Override
@@ -1186,7 +1187,7 @@
      * 处理获取机场组织信息的请求
      *
      * @param receiver 接收到的消息
-     * @param headers 消息头
+     * @param headers  消息头
      */
     @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_GET, outputChannel =
             ChannelName.OUTBOUND)
@@ -1195,10 +1196,10 @@
                 OrganizationGetReceiver.class);
         CommonTopicResponse.CommonTopicResponseBuilder<RequestsReply> builder =
                 CommonTopicResponse.<RequestsReply>builder()
-                .tid(receiver.getTid())
-                .bid(receiver.getBid())
-                .method(RequestsMethodEnum.AIRPORT_ORGANIZATION_GET.getMethod())
-                .timestamp(System.currentTimeMillis());
+                        .tid(receiver.getTid())
+                        .bid(receiver.getBid())
+                        .method(RequestsMethodEnum.AIRPORT_ORGANIZATION_GET.getMethod())
+                        .timestamp(System.currentTimeMillis());
 
         String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF;
 
@@ -1219,4 +1220,10 @@
 
         messageSender.publish(topic, builder.build());
     }
+
+    @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