diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/job/ys7/IncSyncYs7DeviceData.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/job/ys7/IncSyncYs7DeviceData.java index 5da170f6..baaf60d1 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/job/ys7/IncSyncYs7DeviceData.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/job/ys7/IncSyncYs7DeviceData.java @@ -1,9 +1,11 @@ package org.dromara.job.ys7; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.dromara.manager.ys7manager.Ys7Manager; import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo; +import org.dromara.other.domain.OthYs7Device; import org.dromara.other.service.IOthYs7DeviceService; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; @@ -41,4 +43,19 @@ public class IncSyncYs7DeviceData { } } + //同步摄像头全天录像开关状态 + // 每 1 分钟执行一次 + @Scheduled(cron = "1 0 0 * * ?") +// @Scheduled(cron = "0 */10 * * * ?") + public void getEnable() { + log.info("定时同步摄像头设备数据"); + List ys7DeviceList = ys7DeviceService.getBaseMapper().selectList(new LambdaQueryWrapper().orderByDesc(OthYs7Device::getCreateTime)); + Boolean result = ys7DeviceService.saveOrUpdateEnableByDeviceList(ys7DeviceList); + if (result) { + log.info("定时同步摄像头设备数据成功"); + } else { + log.info("没有需要定时同步的设备"); + } + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7Constant.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7Constant.java index b15f11f8..42b15191 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7Constant.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7Constant.java @@ -71,4 +71,13 @@ public interface Ys7Constant { */ String getDeviceLappVideoUrlByPost = "https://open.ys7.com/api/lapp/v2/live/address/get"; + /** + * 设置全天录像开关 POST + */ + String UPDATEONEKEYSWITCH = "https://open.ys7.com/api/lapp/device/fullday/record/switch/set"; + /** + * 获取全天录像开关状态 POST + */ + String GETONEKEYSWITCHSTATUS = "https://open.ys7.com/api/lapp/device/fullday/record/switch/status"; + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7RequestUtils.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7RequestUtils.java index 636ba096..4cb6e078 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7RequestUtils.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/Ys7RequestUtils.java @@ -11,6 +11,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.TimestampUtils; import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto; +import org.dromara.manager.ys7manager.dto.DeviceOneKeySwitchRequstDto; import org.dromara.manager.ys7manager.dto.DevicePlayBackUrlRequstDto; import org.dromara.manager.ys7manager.vo.*; @@ -448,4 +449,75 @@ public class Ys7RequestUtils { } } + /** + * 设置全天录像开关 + * @param dto + * @return + */ + public static Boolean putDeviceOneKeySwitch(DeviceOneKeySwitchRequstDto dto) { + if (StringUtils.isAnyBlank(dto.getAccessToken(), dto.getDeviceSerial())) { + throw new ServiceException("设置全天录像开关参数为空", HttpStatus.BAD_REQUEST); + } + if (dto.getEnable() == null){ + throw new ServiceException("请选择开关状态", HttpStatus.BAD_REQUEST); + } + HashMap paramMap = new HashMap<>(); + paramMap.put("accessToken", dto.getAccessToken()); + paramMap.put("deviceSerial", dto.getDeviceSerial()); + paramMap.put("enable", dto.getEnable()); + if (dto.getChannelNo() != null) { + paramMap.put("channelNo", dto.getChannelNo()); + } + + String errorMsg = "设置全天录像开关请求失败"; + try (HttpResponse response = HttpRequest.post(Ys7Constant.UPDATEONEKEYSWITCH) + .form(paramMap) + .execute()) { + if (!response.isOk()) { + log.error("{}:{}", errorMsg, response.getStatus()); + throw new ServiceException(errorMsg + response.getStatus()); + } + String body = response.body(); + log.info("设置全天录像开关body{}",body); + Ys7ResponseVo responseVo = JSONUtil.toBean(body, Ys7ResponseVo.class); + if (!responseVo.getCode().equals("200")) { + log.error("{},状态码:{},{}", errorMsg, responseVo.getCode(), responseVo.getMsg()); + throw new ServiceException("序列号:"+dto.getDeviceSerial()+errorMsg + responseVo.getMsg()); + } + return true; + } + } + /** + * 获取全天录像开关状态 + * @param dto + * @return + */ + public static Integer getDeviceSwitchStatus(DeviceOneKeySwitchRequstDto dto) { + if (StringUtils.isAnyBlank(dto.getAccessToken(), dto.getDeviceSerial())) { + throw new ServiceException("获取全天录像开关状态参数为空", HttpStatus.BAD_REQUEST); + } + HashMap paramMap = new HashMap<>(); + paramMap.put("accessToken", dto.getAccessToken()); + paramMap.put("deviceSerial", dto.getDeviceSerial()); + + String errorMsg = "获取全天录像开关状态请求失败"; + try (HttpResponse response = HttpRequest.post(Ys7Constant.GETONEKEYSWITCHSTATUS) + .form(paramMap) + .execute()) { + if (!response.isOk()) { + log.error("{}:{}", errorMsg, response.getStatus()); + throw new ServiceException(errorMsg + response.getStatus()); + } + String body = response.body(); + log.info("获取全天录像开关状态body{}",body); + Ys7ResponseVo responseVo = JSONUtil.toBean(body, Ys7ResponseVo.class); + if (!responseVo.getCode().equals("200")) { + log.error("{},状态码:{},{}", errorMsg, responseVo.getCode(), responseVo.getMsg()); + throw new ServiceException("序列号:"+dto.getDeviceSerial()+errorMsg + responseVo.getMsg()); + } + String data = responseVo.getData(); + return JSONUtil.parseObj(data).getInt("enable"); + } + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/dto/DeviceOneKeySwitchRequstDto.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/dto/DeviceOneKeySwitchRequstDto.java new file mode 100644 index 00000000..e40e2730 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/manager/ys7manager/dto/DeviceOneKeySwitchRequstDto.java @@ -0,0 +1,13 @@ +package org.dromara.manager.ys7manager.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DeviceOneKeySwitchRequstDto implements Serializable { + private String accessToken; //toke + private String deviceSerial; //设备序列号 + private Integer channelNo; // 通道号,默认1 + private Integer enable; // 状态:0-关闭,1-开启 +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/config/ThreadPoolConfig.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/config/ThreadPoolConfig.java new file mode 100644 index 00000000..2250a35c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/config/ThreadPoolConfig.java @@ -0,0 +1,51 @@ +package org.dromara.other.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ThreadPoolExecutor; + + +@Configuration +public class ThreadPoolConfig { + + /** + * 摄像头全天录像开关操作专用线程池 + */ + @Bean("deviceSwitchExecutor") + public ThreadPoolTaskExecutor deviceSwitchExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); // 核心线程数 + executor.setMaxPoolSize(10); // 最大线程数 + executor.setKeepAliveSeconds(60); // 空闲线程存活时间(秒) + executor.setQueueCapacity(100); // 任务队列容量 + executor.setThreadNamePrefix("device-switch-task-"); // 线程名前缀(替代自定义ThreadFactory) + // 拒绝策略:队列满时由调用线程执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化线程池 + executor.initialize(); + // 应用关闭时优雅关闭 + Runtime.getRuntime().addShutdownHook(new Thread(executor::shutdown)); + return executor; + } + + /** + * 获取摄像头全天录像开关状态操作专用线程池 + */ + @Bean("getDeviceSwitchExecutor") + public ThreadPoolTaskExecutor getDeviceSwitchExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); // 核心线程数 + executor.setMaxPoolSize(10); // 最大线程数 + executor.setKeepAliveSeconds(60); // 空闲线程存活时间(秒) + executor.setQueueCapacity(100); // 任务队列容量 + executor.setThreadNamePrefix("get-device-switch-task-"); // 线程名前缀(替代自定义ThreadFactory) + // 拒绝策略:队列满时由调用线程执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化线程池 + executor.initialize(); + // 应用关闭时优雅关闭 + Runtime.getRuntime().addShutdownHook(new Thread(executor::shutdown)); + return executor; + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/controller/OthYs7DeviceController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/controller/OthYs7DeviceController.java index e3c1e25c..9b47e08f 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/controller/OthYs7DeviceController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/controller/OthYs7DeviceController.java @@ -282,4 +282,20 @@ public class OthYs7DeviceController extends BaseController { return R.ok(othYs7DeviceService.getDateAndDeviceLocalVideo(req)); } + /** + * 一键开关萤石摄像头全天录像开关 + * @return + */ + @SaCheckPermission("other:ys7Device:edit") + @PostMapping("/updateOneKeySwitch") + public R updateOneKeySwitch(@RequestBody OthYs7DeviceOneKeySwitchReq req){ + if (req.getDeviceSerial() == null){ + throw new ServiceException("设备序列号不能为空!!!"); + } + if (req.getEnable() == null){ + throw new ServiceException("状态不能为空!!!"); + } + int i = othYs7DeviceService.updateOneKeySwitch(req); + return R.ok(i>0?"正在处理中,请稍后刷新页面查看":"操作失败"); + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/OthYs7Device.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/OthYs7Device.java index 897045cc..22a3b643 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/OthYs7Device.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/OthYs7Device.java @@ -53,6 +53,11 @@ public class OthYs7Device implements Serializable { */ private Integer status; + /** + * 状态:0-关闭,1-开启 + */ + private Integer enable; + /** * 布撤防状态 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/dto/ys7device/OthYs7DeviceOneKeySwitchReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/dto/ys7device/OthYs7DeviceOneKeySwitchReq.java new file mode 100644 index 00000000..6574c0b1 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/dto/ys7device/OthYs7DeviceOneKeySwitchReq.java @@ -0,0 +1,24 @@ +package org.dromara.other.domain.dto.ys7device; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author lilemy + * @date 2025/6/13 10:19 + */ +@Data +public class OthYs7DeviceOneKeySwitchReq implements Serializable { + + /** + * 设备序列号 + */ + private List deviceSerial; + + /** + * 状态:0-关闭,1-开启 + */ + private Integer enable; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/vo/ys7device/OthYs7DeviceVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/vo/ys7device/OthYs7DeviceVo.java index ef621e51..84a57db3 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/vo/ys7device/OthYs7DeviceVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/domain/vo/ys7device/OthYs7DeviceVo.java @@ -69,6 +69,11 @@ public class OthYs7DeviceVo implements Serializable { @ExcelProperty(value = "设备在线状态(0离线 1在线)") private Integer status; + /** + * 状态:0-关闭,1-开启 + */ + private Integer enable; + /** * 固件版本号 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/IOthYs7DeviceService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/IOthYs7DeviceService.java index fbeb8637..7ff1618b 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/IOthYs7DeviceService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/IOthYs7DeviceService.java @@ -165,4 +165,19 @@ public interface IOthYs7DeviceService extends IService { * @return */ DateAndDeviceLocalVideoVo getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req); + + /** + * 一键开关 + * @param req + * @return + */ + int updateOneKeySwitch(OthYs7DeviceOneKeySwitchReq req); + + /** + * 保存或更新萤石摄像开关状态对象列表 + * + * @param ys7DeviceList 萤石摄像对象列表 + * @return 是否有值发生更改 + */ + Boolean saveOrUpdateEnableByDeviceList(List ys7DeviceList); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/impl/OthYs7DeviceServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/impl/OthYs7DeviceServiceImpl.java index ff26b69b..5e16777e 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/impl/OthYs7DeviceServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/other/service/impl/OthYs7DeviceServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -19,6 +20,7 @@ import org.dromara.manager.ys7manager.Ys7Constant; import org.dromara.manager.ys7manager.Ys7Manager; import org.dromara.manager.ys7manager.Ys7RequestUtils; import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto; +import org.dromara.manager.ys7manager.dto.DeviceOneKeySwitchRequstDto; import org.dromara.manager.ys7manager.dto.DevicePlayBackUrlRequstDto; import org.dromara.manager.ys7manager.enums.DeviceOnOffLineEnum; import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo; @@ -35,10 +37,12 @@ import org.dromara.other.utils.DateRangeUtils; import org.dromara.project.domain.BusProject; import org.dromara.project.service.IBusProjectService; import org.springframework.beans.BeanUtils; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; @@ -59,6 +63,11 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl deviceSerials = req.getDeviceSerial(); + Integer enable = req.getEnable(); + for (String deviceSerial : deviceSerials) { + // lambda内变量需为final/有效final,重新赋值 + String finalDeviceSerial = deviceSerial; + Integer finalEnable = enable; + String finalToken = token; + + // 4. 异步执行单个设备的开关操作 + CompletableFuture.runAsync(() -> { + try { + // 构建请求DTO + DeviceOneKeySwitchRequstDto dto = new DeviceOneKeySwitchRequstDto(); + dto.setAccessToken(finalToken); + dto.setDeviceSerial(finalDeviceSerial); + dto.setEnable(finalEnable); + + // 调用第三方接口(可能抛出异常,已被try-catch捕获) + Boolean b = Ys7RequestUtils.putDeviceOneKeySwitch(dto); + if (b != null && b) { + // 接口调用成功,更新数据库 + int updateRows = baseMapper.update(new LambdaUpdateWrapper() + .set(OthYs7Device::getEnable, finalEnable) + .eq(OthYs7Device::getDeviceSerial, finalDeviceSerial)); + log.info("设备{}一键开关更新成功,开关状态:{},影响行数:{}", + finalDeviceSerial, finalEnable, updateRows); + } else { + log.warn("设备{}一键开关接口返回false,未更新数据库,开关状态:{}", + finalDeviceSerial, finalEnable); + } + } catch (Exception e) { + // 捕获所有异常,确保单个设备失败不影响其他设备 + log.error("设备{}一键开关处理失败,开关状态:{}", finalDeviceSerial, finalEnable, e); + } + }, deviceSwitchExecutor); // 指定自定义线程池 + } + + // 5. 主线程快速返回(异步任务后台执行) + log.info("已提交{}个设备的一键开关异步任务,开关状态:{}", deviceSerials.size(), enable); + return 1; + } + + @Override + public Boolean saveOrUpdateEnableByDeviceList(List ys7DeviceList) { + if (CollectionUtils.isEmpty(ys7DeviceList)) { + return false; + } + String token = ys7Manager.getToken(); + + if (token == null || token.isEmpty()) { + log.error("updateOneKeySwitch获取token失败,无法执行设备开关操作"); + return false; + } + for (OthYs7Device ys7Device : ys7DeviceList) { + // lambda内变量需为final/有效final,重新赋值 + String finalDeviceSerial = ys7Device.getDeviceSerial(); + String finalToken = token; + + // 4. 异步执行单个设备的开关操作 + CompletableFuture.runAsync(() -> { + try { + // 构建请求DTO + DeviceOneKeySwitchRequstDto dto = new DeviceOneKeySwitchRequstDto(); + dto.setAccessToken(finalToken); + dto.setDeviceSerial(finalDeviceSerial); + + // 调用第三方接口(可能抛出异常,已被try-catch捕获) + Integer b = Ys7RequestUtils.getDeviceSwitchStatus(dto); + if (b != null) { + // 接口调用成功,更新数据库 + int updateRows = baseMapper.update(new LambdaUpdateWrapper() + .set(OthYs7Device::getEnable, b) + .eq(OthYs7Device::getDeviceSerial, finalDeviceSerial)); + log.info("设备{}全天录像开关状态更新成功,开关状态:{},影响行数:{}", + finalDeviceSerial, b, updateRows); + } + } catch (Exception e) { + // 捕获所有异常,确保单个设备失败不影响其他设备 + log.error("设备{}全天录像开关状态获取失败", finalDeviceSerial, e); + } + }, getDeviceSwitchExecutor); // 指定自定义线程池 + } + return true; + } + /** * 验证萤石摄像对象是否发生更改 *