2025-11-1 zwy 对机场设备列表、绑定设备列表增加权限配置,新增DataScopeAspectPlus切面文件适配MyBatis-Plus。
1 files added
5 files modified
369 ■■■■■ changed files
ard-admin/src/main/java/com/ard/web/controller/sample/DeviceController.java 31 ●●●●● patch | view | raw | blame | history
ard-common/src/main/java/com/ard/common/annotation/DataScopePlus.java 27 ●●●●● patch | view | raw | blame | history
ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java 189 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/manage/mapper/IDeviceMapper.java 20 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/manage/service/IDeviceService.java 12 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/manage/service/impl/DeviceServiceImpl.java 90 ●●●●● patch | view | raw | blame | history
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();
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 "";
}
ard-framework/src/main/java/com/ard/framework/aspectj/DataScopeAspectPlus.java
New file
@@ -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);
        }
    }
}
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);
}
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);
}
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);
//    }
}