zhaowenyu
2025-11-29 f10e12cd88f5f73fc57667ff958abdda50c23ec4
ard-work/src/main/java/com/ard/work/aspect/UavControlAspect.java
@@ -1,24 +1,34 @@
package com.ard.work.aspect;
import com.ard.common.constant.CacheConstants;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.exception.ControlException;
import com.ard.common.utils.SecurityUtils;
import com.ard.work.aspect.annotation.CheckTakeoffAltitude;
import com.ard.common.utils.StringUtils;
import com.ard.common.utils.reflect.ReflectUtils;
import com.ard.data.noflyzone.domain.ArdNoFlyZone;
import com.ard.data.noflyzone.domain.Vertex;
import com.ard.data.noflyzone.service.IArdNoFlyZoneService;
import com.ard.data.noflyzone.utils.NoFlyZoneUtils;
import com.ard.work.aspect.annotation.CheckControlRight;
import com.ard.work.aspect.annotation.CheckDroneStatus;
import com.ard.work.aspect.annotation.CheckNoFlyZone;
import com.ard.work.aspect.annotation.CheckTakeoffAltitude;
import com.ard.work.domain.cache.UavTelemetryDataCache;
import com.ard.work.domain.dto.telemetry.TelemetryData;
import com.ard.work.service.control.UavControlService;
import com.ard.work.domain.message.UavControlType;
import com.ard.work.domain.receiver.BoxInfoReceiver;
import com.ard.work.service.control.UavControlService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
/**
 * AOP 切面:拦截 @RequireUavControl,检查用户是否拥有 UAV 控制权限
 *
 * 无人机控制切面
 * <p>
 * 注意切面顺序:同类方法注解按字母ABCD顺序,不同类注解按order(1)顺序 越小优先级越高
 */
@Aspect
@@ -27,9 +37,13 @@
    @Resource
    private UavControlService uavControlService;
    @Resource
    private IArdNoFlyZoneService ardNoFlyZoneService;
    @Resource
    private RedisCache redisCache;
    /**
     * 前置通知:控制权检测
     * 控制权检查
     */
    @Before("@annotation(checkControlRight)")
    public void checkUavControl(JoinPoint joinPoint, CheckControlRight checkControlRight) {
@@ -42,25 +56,38 @@
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        // 遍历参数,使用反射获取每个参数中的 boxSn
        for (Object arg : args) {
            String boxSn = getBoxSn(arg);  // 调用之前实现的 getBoxSn 方法
            if (boxSn != null) {
                // 检查当前用户是否拥有该设备的控制权限
                Long currentController = uavControlService.getCurrentController(boxSn,
                        controlType.name().toLowerCase());
                if (currentController == null || !currentController.equals(userId)) {
                    throw new ControlException("无权执行该操作,需要 " + controlType + " 控制权限");
                }
            if (arg == null) {
                continue;
            }
            // 1️⃣ 获取 boxSn
            String boxSn = ReflectUtils.getFieldValue(arg, "boxSn");
            if (StringUtils.isBlank(boxSn)) {
                continue;
            }
            // 2️⃣ 检查控制权
            Long currentController = uavControlService.getCurrentController(boxSn,
                    controlType.name().toLowerCase());
            if (currentController == null || !currentController.equals(userId)) {
                throw new ControlException("无权执行该操作,需要 " + controlType + " 控制权限");
            }
            // 3️⃣ 检查在线状态
            BoxInfoReceiver obj = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
            if (obj == null || !obj.getOnLine().equals(1)) {
                throw new ControlException("无人机离线,无法执行操作!boxSn: " + boxSn);
            }
        }
    }
    /**
     * 前置通知:高度检查
     * 高度检查
     */
    @Before("@annotation(checkTakeoffAltitude)")
    public void takeoffAltitudeCheck(JoinPoint joinPoint, CheckTakeoffAltitude checkTakeoffAltitude) {
    public void takeoffAltitudeCheck(JoinPoint joinPoint, CheckTakeoffAltitude checkTakeoffAltitude){
        // 获取最小高度
        double minAltitude = checkTakeoffAltitude.minAltitude();
        // 获取方法参数
@@ -68,9 +95,9 @@
        // 遍历参数,使用反射获取每个参数中的 boxSn
        for (Object arg : args) {
            String boxSn = getBoxSn(arg);  // 调用之前实现的 getBoxSn 方法
            String boxSn = ReflectUtils.getFieldValue(arg, "boxSn");  // 调用之前实现的 getBoxSn 方法
            if (boxSn != null) {
                TelemetryData uavTelemetryData = UavTelemetryDataCache.getUavTelemetryData(boxSn);
                TelemetryData uavTelemetryData = UavTelemetryDataCache.get(boxSn);
                if (uavTelemetryData == null) {
                    throw new ControlException("遥测数据异常,无法执行!");
                }
@@ -83,41 +110,95 @@
    }
    /**
     * 通过反射获取对象中的 boxSn 字段
     *
     * @param dto 要反射的对象
     * @return boxSn 字段的值,如果没有找到则返回 null
     * 状态检查
     */
    public static String getBoxSn(Object dto) {
        if (dto == null) {
            return null; // 如果对象为空,直接返回 null
        }
    @Before("@annotation(checkDroneStatus)")
    public void checkDroneStatus(JoinPoint joinPoint, CheckDroneStatus checkDroneStatus) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg == null) {
                continue;
            }
        try {
            // 获取类的所有字段
            Field[] fields = dto.getClass().getDeclaredFields();
            for (Field field : fields) {
                // 设置字段可访问
                field.setAccessible(true);
                // 判断是否是 boxSn 字段
                if ("boxSn".equals(field.getName())) {
                    // 检查字段类型是否是 String 类型
                    if (field.getType().equals(String.class)) {
                        // 获取并返回 boxSn 字段的值
                        return (String) field.get(dto);
                    } else {
                        // 如果字段类型不是 String,返回 null
                        return null;
                    }
            String boxSn = ReflectUtils.getFieldValue(arg, "boxSn");
            if (boxSn == null) {
                continue;
            }
            // 检查在线状态
            if (checkDroneStatus.checkOnline()) {
                // 3️⃣ 检查在线状态
                BoxInfoReceiver obj = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
                if (obj == null || !obj.getOnLine().equals(1)) {
                    throw new ControlException("无人机离线,无法执行操作!boxSn: " + boxSn);
                }
            }
        } catch (IllegalAccessException e) {
            // 捕获异常并打印日志
            System.err.println("反射访问失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * 禁飞区检查
     */
    @Before("@annotation(checkNoFlyZone)")
    public void checkNoFlyZone(JoinPoint joinPoint, CheckNoFlyZone checkNoFlyZone) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        return null; // 如果没有找到 boxSn 字段,返回 null
        // ① 提取 boxSn(无人机编号)和目标点坐标
        String boxSn = null;
        Double targetLon = null, targetLat = null;
        for (Object arg : args) {
            if (arg == null) continue;
            if (boxSn == null) {
                String sn = ReflectUtils.getFieldValue(arg, "boxSn");
                if (sn != null) boxSn = sn;
            }
            if (targetLon == null || targetLat == null) {
                Double lon = ReflectUtils.getFieldValue(arg, "longitude");
                Double lat = ReflectUtils.getFieldValue(arg, "latitude");
                if (lon != null && lat != null) {
                    targetLon = lon;
                    targetLat = lat;
                }
            }
        }
        if (targetLon == null || targetLat == null) {
            throw new ControlException("方法参数中未找到有效目标经纬度字段,无法判断禁飞区!");
        }
        // ② 获取无人机当前位置(从缓存)
        Vertex current = null;
        if (boxSn != null) {
            TelemetryData telemetry = UavTelemetryDataCache.get(boxSn);
            if (telemetry != null) {
                current = new Vertex(telemetry.getLng(), telemetry.getLat());
                //current = new Vertex(125.03, 46.58);
            }
        }
        Vertex target = new Vertex(targetLon, targetLat);
        // ③ 查询禁飞区
        List<ArdNoFlyZone> zones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
        if (zones.isEmpty()) return;
        for (ArdNoFlyZone zone : zones) {
            List<Vertex> polygon = zone.getVertices();
            if (polygon == null || polygon.size() < 3) continue;
            // 检查目标点是否在禁飞区
            if (NoFlyZoneUtils.isPointInPolygon(target, polygon)) {
                throw new ControlException("目标点在禁飞区【" + zone.getName() + "】内,无法执行操作!");
            }
            // 检查路径是否穿越禁飞区
            if (current != null && NoFlyZoneUtils.isLineIntersectPolygon(current, target, polygon)) {
                throw new ControlException("路径穿越禁飞区【" + zone.getName() + "】,无法执行操作!");
            }
        }
    }
}