ard-admin/src/main/resources/application.yml
@@ -117,7 +117,7 @@ # redis 配置 redis: # 地址 host: 192.168.1.15 host: 127.0.0.1 # 端口,默认为6379 port: 6379 # 数据库索引 ard-common/src/main/java/com/ard/common/exception/NoPermissionException.java
New file @@ -0,0 +1,7 @@ package com.ard.common.exception; public class NoPermissionException extends RuntimeException { public NoPermissionException(String message) { super(message); } } ard-framework/src/main/java/com/ard/framework/web/exception/GlobalExceptionHandler.java
@@ -1,6 +1,8 @@ package com.ard.framework.web.exception; import javax.servlet.http.HttpServletRequest; import com.ard.common.exception.NoPermissionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -142,4 +144,13 @@ { return AjaxResult.error("演示模式,不允许操作"); } /** * 无人机无控制权异常 */ @ExceptionHandler(NoPermissionException.class) public AjaxResult handleNoPermissionException(NoPermissionException e) { return AjaxResult.error(e.getMessage()); } } ard-work/pom.xml
@@ -62,6 +62,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> </dependencies> </project> ard-work/src/main/java/com/ard/tianyu/aspect/ControlAuthorityAspect.java
New file @@ -0,0 +1,89 @@ package com.ard.tianyu.aspect; import com.ard.common.exception.NoPermissionException; import com.ard.common.utils.SecurityUtils; import com.ard.tianyu.service.impl.UavControlManager; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 控制权限切面 * * @author liusuyi */ @Aspect @Component @Slf4j public class ControlAuthorityAspect { @Pointcut("@annotation(com.ard.tianyu.aspect.annotation.CheckControlAuthority)") public void dsPointCut() { } @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Boolean result = controlScopeFilter(point); if (result) { log.debug("已获取无人机控制权--{}", method.getName()); // 执行目标方法 return point.proceed(); } log.debug("未获取无人机控制权--{}", method.getName()); throw new NoPermissionException("未获取无人机控制权"); } /** * 操控判断逻辑 * * @param joinPoint 切点 */ public Boolean controlScopeFilter(ProceedingJoinPoint joinPoint) { // 获取方法参数 Object[] args = joinPoint.getArgs(); // 使用反射获取参数中的 boxSn for (Object arg : args) { String boxSn = getBoxSn(arg); if (boxSn != null) { //判断无人机控制者是否为当前登录用户 Long currentControllerId = UavControlManager.droneControlMap.get(boxSn); if (currentControllerId != null && currentControllerId.equals(SecurityUtils.getUserId())) { return true; } } } return false; } // 通过反射获取对象中的无人机boxSn字段 public static String getBoxSn(Object dto) { try { // 获取类的所有字段 Field[] fields = dto.getClass().getDeclaredFields(); for (Field field : fields) { // 设置字段可访问 field.setAccessible(true); // 判断是否是 boxSn 字段 if ("boxSn".equals(field.getName())) { // 获取并返回 droneId 的值 return (String) field.get(dto); } } } catch (IllegalAccessException e) { e.printStackTrace(); } return null; // 如果没有找到 droneId 字段,返回 null } } ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckControlAuthority.java
New file @@ -0,0 +1,9 @@ package com.ard.tianyu.aspect.annotation; import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CheckControlAuthority { } ard-work/src/main/java/com/ard/tianyu/controller/UavPermissionsController.java
New file @@ -0,0 +1,57 @@ package com.ard.tianyu.controller; import com.ard.common.core.domain.AjaxResult; import com.ard.common.utils.SecurityUtils; import com.ard.tianyu.service.impl.UavControlManager; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @ClassName: UavPermissionsController * @Description: 无人机权限控制器 * @Author: liusuyi * @Date: 2025年02月20日9:38 **/ @Api(tags = "无人机权限控制接口") @RequestMapping("/uav/permissions") @RestController public class UavPermissionsController { @Resource private UavControlManager uavControlManager; @ApiOperation("申请控制") @ApiOperationSupport(order = 1) @GetMapping("/requestControl") public AjaxResult requestControl(String boxSn) { return AjaxResult.success(uavControlManager.requestControl(boxSn)); } @ApiOperation("处理申请") @ApiOperationSupport(order = 2) @GetMapping("/handleControlResponse") public AjaxResult handleControlResponse(String boxSn, Boolean approve) { return AjaxResult.success(uavControlManager.handleControlResponse(boxSn, approve)); } @ApiOperation("释放控制") @ApiOperationSupport(order = 3) @GetMapping("/releaseControl") public AjaxResult releaseControl(String boxSn) { return AjaxResult.success(uavControlManager.releaseControl(boxSn)); } @ApiOperation("获取无人机控制者") @ApiOperationSupport(order = 4) @GetMapping("/controlRequestMap") public AjaxResult controlRequestMap(String boxSn) { return AjaxResult.success(UavControlManager.droneControlMap.get(boxSn)); } } ard-work/src/main/java/com/ard/tianyu/service/ITyService.java
@@ -72,134 +72,134 @@ * 设置返航高度 * @return */ void updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO); Boolean updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO); /** * 设置视频码流 * @return */ void updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO); Boolean updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO); /** * 设置返航点 * @return */ void updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO); Boolean updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO); /** * 注册 */ void registerMessage(); Boolean registerMessage(); /** * 云台转动(基于角度) */ void angleControl(AngleControlDTO angleControlDto); Boolean angleControl(AngleControlDTO angleControlDto); /** * 云台绝对值控制(基于角度) */ void absAngleControl(AbsAngleControlDTO absAngleControlDto); Boolean absAngleControl(AbsAngleControlDTO absAngleControlDto); /** * 云台转动(基于速度) */ void speedControl(SpeedControlDTO speedControlDto); Boolean speedControl(SpeedControlDTO speedControlDto); /** * 设置相机模式 */ void updateCameraMode(CameraModeDTO cameraModeDTO); Boolean updateCameraMode(CameraModeDTO cameraModeDTO); /** * 拍照 */ void capturePic(CapturePicDTO capturePicDto); Boolean capturePic(CapturePicDTO capturePicDto); /** * 录像 */ void record(RecordDTO recordDto); Boolean record(RecordDTO recordDto); /** * 变倍 */ void zoom(ZoomDTO zoomDTO); Boolean zoom(ZoomDTO zoomDTO); /** * 变倍到指定倍数 */ void zoomToLevel(ZoomToLevelDTO zoomToLevelDTO); Boolean zoomToLevel(ZoomToLevelDTO zoomToLevelDTO); /** * 切换摄像头 */ void switchCamera(SwitchCameraDTO switchCameraDTO); Boolean switchCamera(SwitchCameraDTO switchCameraDTO); /** * 切换视频源 */ void switchSource(SwitchSourceDTO switchSourceDTO); Boolean switchSource(SwitchSourceDTO switchSourceDTO); /** * 打开或关闭照片回传 */ void photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO); Boolean photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO); /** * 切换无人机控制权 */ void switchUavControl(SwitchUavControlDTO switchUavControlDTO); Boolean switchUavControl(SwitchUavControlDTO switchUavControlDTO); /** * 起飞 */ void takeOff(TakeOffDTO takeOffDTO); Boolean takeOff(TakeOffDTO takeOffDTO); /** * 返航 */ void returnHome(ReturnHomeDTO returnHomeDTO); Boolean returnHome(ReturnHomeDTO returnHomeDTO); /** * 取消返航 */ void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO); Boolean returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO); /** * 降落 */ void land(LandDTO landDTO); Boolean land(LandDTO landDTO); /** * 取消降落 */ void landCancel(LandCancelDTO landCancelDTO); Boolean landCancel(LandCancelDTO landCancelDTO); /** * 强制降落 */ void landForced(LandForcedDTO landForcedDTO); Boolean landForced(LandForcedDTO landForcedDTO); /** * 方向控制 */ void flyControl(FlyControlDTO flyControlDTO); Boolean flyControl(FlyControlDTO flyControlDTO); /** * 指点飞行控制 */ void flyPointControl(FlyPointControlDTO flyPointControlDTO); Boolean flyPointControl(FlyPointControlDTO flyPointControlDTO); /** * 停止指点飞行控制 */ void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO); Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO); /** * 紧急制动 */ void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO); Boolean emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO); /** * 获取无人机状态 ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -1,6 +1,7 @@ package com.ard.tianyu.service.impl; import com.ard.common.utils.ByteUtils; import com.ard.tianyu.aspect.annotation.CheckControlAuthority; import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.http.TyClient; import com.ard.tianyu.model.cache.TyTcpServerCache; @@ -155,34 +156,37 @@ * 设置返航高度 */ @Override public void updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO) { public Boolean updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO) { ReturnHeightSettingCmd returnHeightSettingCmd = new ReturnHeightSettingCmd(returnHeightSettingDTO); this.sendMessage(tyConfiguration.getIpPort(), returnHeightSettingCmd); return true; } /** * 设置返航点 */ @Override public void updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO) { public Boolean updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO) { ReturnPointSettingCmd returnPointSettingCmd = new ReturnPointSettingCmd(returnPointSettingDTO); this.sendMessage(tyConfiguration.getIpPort(), returnPointSettingCmd); return true; } /** * 设置视频码流 */ @Override public void updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO) { public Boolean updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO) { VideoStreamSettingCmd videoStreamSettingCmd = new VideoStreamSettingCmd(videoStreamSettingDTO); this.sendMessage(tyConfiguration.getIpPort(), videoStreamSettingCmd); return true; } /** * 注册天宇平台 */ @Override public void registerMessage() { public Boolean registerMessage() { try { AccessTokenReceiver tyToken = tyConfiguration.getAccessToken(); RegisterCmd registerMessage = new RegisterCmd(tyToken.getCompanyId(), tyToken.getAccessToken().getBytes()); @@ -190,59 +194,65 @@ } catch (Exception e) { log.error("注册天宇平台失败:{}", e.getMessage()); } return true; } /** * 云台控制基于角度 */ @Override public void angleControl(AngleControlDTO angleControlDto) { public Boolean angleControl(AngleControlDTO angleControlDto) { AngleControlCmd controlAngleCommand = new AngleControlCmd(angleControlDto); this.sendMessage(tyConfiguration.getIpPort(), controlAngleCommand); return true; } /** * 云台绝对值控制(基于角度) */ @Override public void absAngleControl(AbsAngleControlDTO absAngleControlDto) { public Boolean absAngleControl(AbsAngleControlDTO absAngleControlDto) { AbsAngleControlCmd absAngleControlCommand = new AbsAngleControlCmd(absAngleControlDto); this.sendMessage(tyConfiguration.getIpPort(), absAngleControlCommand); return true; } /** * 云台转动(基于速度) */ @Override public void speedControl(SpeedControlDTO speedControlDto) { public Boolean speedControl(SpeedControlDTO speedControlDto) { SpeedControlCmd speedControlCommand = new SpeedControlCmd(speedControlDto); this.sendMessage(tyConfiguration.getIpPort(), speedControlCommand); return true; } /** * 设置相机模式 */ @Override public void updateCameraMode(CameraModeDTO cameraModeDTO) { public Boolean updateCameraMode(CameraModeDTO cameraModeDTO) { CameraModeCmd cameraModeCommand = new CameraModeCmd(cameraModeDTO); this.sendMessage(tyConfiguration.getIpPort(), cameraModeCommand); return true; } /** * 拍照 */ @Override public void capturePic(CapturePicDTO capturePicDto) { public Boolean capturePic(CapturePicDTO capturePicDto) { updateCameraMode(CameraModeDTO.builder().boxSn(capturePicDto.getBoxSn()).actionParam((byte) 0x01).build()); CapturePicCmd capturePicCommand = new CapturePicCmd(capturePicDto); this.sendMessage(tyConfiguration.getIpPort(), capturePicCommand); return true; } /** * 录像 */ @Override public void record(RecordDTO recordDto) { public Boolean record(RecordDTO recordDto) { BaseCmd baseCmd; if (recordDto.getIsRecord()) { updateCameraMode(CameraModeDTO.builder().boxSn(recordDto.getBoxSn()).actionParam((byte) 0x02).build()); @@ -251,150 +261,168 @@ baseCmd = new RecordStopCmd(recordDto); } this.sendMessage(tyConfiguration.getIpPort(), baseCmd); return true; } /** * 变倍 */ @Override public void zoom(ZoomDTO zoomDTO) { public Boolean zoom(ZoomDTO zoomDTO) { ZoomCmd zoomCommand = new ZoomCmd(zoomDTO); this.sendMessage(tyConfiguration.getIpPort(), zoomCommand); return true; } /** * 变倍到指定倍数 */ @Override public void zoomToLevel(ZoomToLevelDTO zoomToLevelDTO) { public Boolean zoomToLevel(ZoomToLevelDTO zoomToLevelDTO) { ZoomToLevelCmd zoomToLevelCommand = new ZoomToLevelCmd(zoomToLevelDTO); this.sendMessage(tyConfiguration.getIpPort(), zoomToLevelCommand); return true; } /** * 切换摄像头 */ @Override public void switchCamera(SwitchCameraDTO switchCameraDTO) { public Boolean switchCamera(SwitchCameraDTO switchCameraDTO) { SwitchCameraCmd switchCameraCmd = new SwitchCameraCmd(switchCameraDTO); this.sendMessage(tyConfiguration.getIpPort(), switchCameraCmd); return true; } /** * 切换视频源 */ @Override public void switchSource(SwitchSourceDTO switchSourceDTO) { public Boolean switchSource(SwitchSourceDTO switchSourceDTO) { SwitchSourceCmd switchSourceCmd = new SwitchSourceCmd(switchSourceDTO); this.sendMessage(tyConfiguration.getIpPort(), switchSourceCmd); return true; } /** * 打开或关闭照片回传 */ @Override public void photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO) { public Boolean photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO) { PhotoReturnToggleCmd photoReturnToggleCmd = new PhotoReturnToggleCmd(photoReturnToggleDTO); this.sendMessage(tyConfiguration.getIpPort(), photoReturnToggleCmd); return true; } /** * 切换无人机控制权 */ @Override public void switchUavControl(SwitchUavControlDTO switchUavControlDTO) { public Boolean switchUavControl(SwitchUavControlDTO switchUavControlDTO) { SwitchUavControlCmd switchUavControlCmd = new SwitchUavControlCmd(switchUavControlDTO); this.sendMessage(tyConfiguration.getIpPort(), switchUavControlCmd); return true; } /** * 起飞 */ @CheckControlAuthority @Override public void takeOff(TakeOffDTO takeOffDTO) { public Boolean takeOff(TakeOffDTO takeOffDTO) { TakeOffCmd takeOffCmd = new TakeOffCmd(takeOffDTO); this.sendMessage(tyConfiguration.getIpPort(), takeOffCmd); return true; } /** * 返航 */ @Override public void returnHome(ReturnHomeDTO returnHomeDTO) { public Boolean returnHome(ReturnHomeDTO returnHomeDTO) { ReturnHomeCmd returnHomeCmd = new ReturnHomeCmd(returnHomeDTO); this.sendMessage(tyConfiguration.getIpPort(), returnHomeCmd); return true; } /** * 取消返航 */ @Override public void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) { public Boolean returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) { ReturnHomeCancelCmd returnHomeCancelCmd = new ReturnHomeCancelCmd(returnHomeCancelDTO); this.sendMessage(tyConfiguration.getIpPort(), returnHomeCancelCmd); return true; } /** * 降落 */ @Override public void land(LandDTO landDTO) { public Boolean land(LandDTO landDTO) { LandCmd landCmd = new LandCmd(landDTO); this.sendMessage(tyConfiguration.getIpPort(), landCmd); return true; } /** * 取消降落 */ @Override public void landCancel(LandCancelDTO landCancelDTO) { public Boolean landCancel(LandCancelDTO landCancelDTO) { LandCancelCmd landCancelCmd = new LandCancelCmd(landCancelDTO); this.sendMessage(tyConfiguration.getIpPort(), landCancelCmd); return true; } /** * 强制降落 */ @Override public void landForced(LandForcedDTO landForcedDTO) { public Boolean landForced(LandForcedDTO landForcedDTO) { LandForcedCmd landForcedCmd = new LandForcedCmd(landForcedDTO); this.sendMessage(tyConfiguration.getIpPort(), landForcedCmd); return true; } /** * 方向控制 */ @Override public void flyControl(FlyControlDTO flyControlDTO) { public Boolean flyControl(FlyControlDTO flyControlDTO) { FlyControlCmd flyControlCmd = new FlyControlCmd(flyControlDTO); this.sendMessage(tyConfiguration.getIpPort(), flyControlCmd); return true; } /** * 指点飞行控制 */ @Override public void flyPointControl(FlyPointControlDTO flyPointControlDTO) { public Boolean flyPointControl(FlyPointControlDTO flyPointControlDTO) { FlyPointControlCmd flyPointControlCmd = new FlyPointControlCmd(flyPointControlDTO); this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCmd); return true; } /** * 停止指点飞行控制 */ @Override public void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) { public Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) { FlyPointControlCancelCmd flyPointControlCancelCmd = new FlyPointControlCancelCmd(flyPointControlCancelDTO); this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCancelCmd); return true; } /** * 紧急制动 */ @Override public void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) { public Boolean emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) { EmergencyBrakeCmd emergencyBrakeCmd = new EmergencyBrakeCmd(emeragencyBrakeDTO); this.sendMessage(tyConfiguration.getIpPort(), emergencyBrakeCmd); return true; } /** ard-work/src/main/java/com/ard/tianyu/service/impl/UavControlManager.java
New file @@ -0,0 +1,92 @@ package com.ard.tianyu.service.impl; import com.alibaba.fastjson2.JSON; import com.ard.common.exception.NoPermissionException; import com.ard.common.utils.SecurityUtils; import com.ard.tianyu.websocket.domain.ControlRequestMessage; import com.ard.tianyu.websocket.domain.WsMessage; import com.ard.tianyu.websocket.utils.WebSocketUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.concurrent.ConcurrentHashMap; @Service @Slf4j public class UavControlManager { // 存储每个无人机的当前控制者 public static final ConcurrentHashMap<String, Long> droneControlMap = new ConcurrentHashMap<>(); // 存储无人机的控制请求(用于判断哪个控制者正在等待) public static final ConcurrentHashMap<String, Long> controlRequestMap = new ConcurrentHashMap<>(); // 发送通知(通过 WebSocket推送) public void notifyMessage(String boxSn, String action, Long controller, Long requester) { ControlRequestMessage controlRequestMessage = ControlRequestMessage.builder() .boxSn(boxSn) .requester(requester) .action(action) .build(); WsMessage<ControlRequestMessage> wsMessage = new WsMessage<>("controlRequest", controlRequestMessage); WebSocketUtil.sendMessage(controller.toString(), JSON.toJSONString(wsMessage)); } // 申请控制 public boolean requestControl(String boxSn) { Long requester = SecurityUtils.getUserId(); // 如果无人机没有控制者,直接控制 if (droneControlMap.get(boxSn) == null) { droneControlMap.put(boxSn, requester); return true; // 控制成功 } // 如果无人机已经有控制者,则发送申请给当前控制者 Long controller = droneControlMap.get(boxSn); if(!controller.equals(requester)) { // 则将控制请求加入请求队列 controlRequestMap.put(boxSn, requester); // 通知当前控制者,有新的控制请求 notifyMessage(boxSn, "request", controller, requester); } return false; // 等待当前控制者的同意 } // 处理控制请求的同意或拒绝 public boolean handleControlResponse(String boxSn, boolean approve) { Long controller = SecurityUtils.getUserId(); Long requester = controlRequestMap.get(boxSn); // 确保请求者是当前的控制者 if (requester != null && !requester.equals(controller)) { if (approve) { // 同意控制,将控制权赋予请求者 droneControlMap.put(boxSn, requester); notifyMessage(boxSn, "approve", requester, controller); } else { notifyMessage(boxSn, "reject", requester, controller); } // 无论同意还是拒绝,都移除控制请求 controlRequestMap.remove(boxSn); return true; } return false; } // 释放控制 public boolean releaseControl(String boxSn) { Long controller = droneControlMap.get(boxSn); if (controller != null) { if (controller.equals(SecurityUtils.getUserId())) { droneControlMap.remove(boxSn); return true; } else { // 当前控制者不匹配,可以记录日志或者抛出异常 log.warn("无权限释放控制, 实际控制者是 {}", controller); throw new NoPermissionException("无权限释放控制, 实际控制者是 " + controller); } } else { // 说明无人机没有控制者 log.warn("无人机当前 {} 没有控制者,无需释放", boxSn); throw new NoPermissionException("无人机" + boxSn + "当前没有控制者,无需释放"); } } } ard-work/src/main/java/com/ard/tianyu/websocket/config/WebSocketConfig.java
@@ -21,7 +21,7 @@ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //添加myWebSocketHandler消息处理对象以及websocket连接地址 registry.addHandler(tyMessageHandler, "/ws/state/**") registry.addHandler(tyMessageHandler, "/ws/control/**") //设置允许跨域访问 .setAllowedOrigins("*") //添加拦截器可实现用户链接前进行权限校验等操作 ard-work/src/main/java/com/ard/tianyu/websocket/domain/ControlRequestMessage.java
New file @@ -0,0 +1,19 @@ package com.ard.tianyu.websocket.domain; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class ControlRequestMessage { private String boxSn; // 盒子编号 private Long controller; // 当前控制者ID private Long requester; // 请求控制者ID private String action; // 操作类型:request(请求),approve(同意),reject(拒绝) } ard-work/src/main/java/com/ard/tianyu/websocket/domain/WsMessage.java
New file @@ -0,0 +1,13 @@ package com.ard.tianyu.websocket.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class WsMessage<T> { private String type; // 消息类型 private T payload; // 泛型消息内容 } ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyMessageHandler.java
@@ -1,6 +1,9 @@ package com.ard.tianyu.websocket.handler; import com.ard.tianyu.websocket.domain.ControlRequestMessage; import com.ard.tianyu.websocket.domain.WsMessage; import com.ard.tianyu.websocket.utils.WebSocketUtil; import com.ard.tianyu.websocket.utils.WsMessageConverter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyStreamHandler.java
@@ -11,6 +11,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; ard-work/src/main/java/com/ard/tianyu/websocket/utils/WebSocketUtil.java
@@ -51,7 +51,10 @@ } } } public static void sendMessage(String userId, String message) { WebSocketSession session = ONLINE_USER_SESSIONS.get(userId); sendMessage(session, message); } /** * @param session 用户 session * @param message 发送内容 ard-work/src/main/java/com/ard/tianyu/websocket/utils/WsMessageConverter.java
New file @@ -0,0 +1,19 @@ package com.ard.tianyu.websocket.utils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.TypeReference; import com.ard.tianyu.websocket.domain.WsMessage; public class WsMessageConverter { // 将 JSON 字符串转换为 WsMessage 对象 public static <T> WsMessage<T> fromJson(String json, Class<T> payloadType) { // 使用 TypeReference 来处理泛型 return JSON.parseObject(json, new TypeReference<WsMessage<T>>(){}); } // 将 WsMessage 对象转换为 JSON 字符串 public static <T> String toJson(WsMessage<T> wsMessage) { return JSON.toJSONString(wsMessage); } }