摄像头设置是否开启全天录像接口

This commit is contained in:
2025-12-15 16:43:06 +08:00
parent deef321c1b
commit 654623ad15
11 changed files with 331 additions and 0 deletions

View File

@ -1,9 +1,11 @@
package org.dromara.job.ys7; package org.dromara.job.ys7;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.manager.ys7manager.Ys7Manager; import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo; import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.service.IOthYs7DeviceService; import org.dromara.other.service.IOthYs7DeviceService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled; 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<OthYs7Device> ys7DeviceList = ys7DeviceService.getBaseMapper().selectList(new LambdaQueryWrapper<OthYs7Device>().orderByDesc(OthYs7Device::getCreateTime));
Boolean result = ys7DeviceService.saveOrUpdateEnableByDeviceList(ys7DeviceList);
if (result) {
log.info("定时同步摄像头设备数据成功");
} else {
log.info("没有需要定时同步的设备");
}
}
} }

View File

@ -71,4 +71,13 @@ public interface Ys7Constant {
*/ */
String getDeviceLappVideoUrlByPost = "https://open.ys7.com/api/lapp/v2/live/address/get"; 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";
} }

View File

@ -11,6 +11,7 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.TimestampUtils; import org.dromara.common.core.utils.TimestampUtils;
import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto; 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.dto.DevicePlayBackUrlRequstDto;
import org.dromara.manager.ys7manager.vo.*; 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<String, Object> 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<String, Object> 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");
}
}
} }

View File

@ -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-开启
}

View File

@ -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;
}
}

View File

@ -282,4 +282,20 @@ public class OthYs7DeviceController extends BaseController {
return R.ok(othYs7DeviceService.getDateAndDeviceLocalVideo(req)); return R.ok(othYs7DeviceService.getDateAndDeviceLocalVideo(req));
} }
/**
* 一键开关萤石摄像头全天录像开关
* @return
*/
@SaCheckPermission("other:ys7Device:edit")
@PostMapping("/updateOneKeySwitch")
public R<String> 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?"正在处理中,请稍后刷新页面查看":"操作失败");
}
} }

View File

@ -53,6 +53,11 @@ public class OthYs7Device implements Serializable {
*/ */
private Integer status; private Integer status;
/**
* 状态0-关闭1-开启
*/
private Integer enable;
/** /**
* 布撤防状态 * 布撤防状态
*/ */

View File

@ -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<String> deviceSerial;
/**
* 状态0-关闭1-开启
*/
private Integer enable;
}

View File

@ -69,6 +69,11 @@ public class OthYs7DeviceVo implements Serializable {
@ExcelProperty(value = "设备在线状态(0离线 1在线)") @ExcelProperty(value = "设备在线状态(0离线 1在线)")
private Integer status; private Integer status;
/**
* 状态0-关闭1-开启
*/
private Integer enable;
/** /**
* 固件版本号 * 固件版本号
*/ */

View File

@ -165,4 +165,19 @@ public interface IOthYs7DeviceService extends IService<OthYs7Device> {
* @return * @return
*/ */
DateAndDeviceLocalVideoVo getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req); DateAndDeviceLocalVideoVo getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req);
/**
* 一键开关
* @param req
* @return
*/
int updateOneKeySwitch(OthYs7DeviceOneKeySwitchReq req);
/**
* 保存或更新萤石摄像开关状态对象列表
*
* @param ys7DeviceList 萤石摄像对象列表
* @return 是否有值发生更改
*/
Boolean saveOrUpdateEnableByDeviceList(List<OthYs7Device> ys7DeviceList);
} }

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.Ys7Manager;
import org.dromara.manager.ys7manager.Ys7RequestUtils; import org.dromara.manager.ys7manager.Ys7RequestUtils;
import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto; 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.dto.DevicePlayBackUrlRequstDto;
import org.dromara.manager.ys7manager.enums.DeviceOnOffLineEnum; import org.dromara.manager.ys7manager.enums.DeviceOnOffLineEnum;
import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo; 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.domain.BusProject;
import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -59,6 +63,11 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
@Resource @Resource
private Ys7Manager ys7Manager; private Ys7Manager ys7Manager;
@Resource
private ThreadPoolTaskExecutor deviceSwitchExecutor; // 摄像头全天录像开关操作专用线程池
@Resource
private ThreadPoolTaskExecutor getDeviceSwitchExecutor; // 注入获取摄像头全天录像开关状态线程池
/** /**
* 查询萤石摄像头 * 查询萤石摄像头
* *
@ -487,6 +496,101 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
return vo; return vo;
} }
@Override
public int updateOneKeySwitch(OthYs7DeviceOneKeySwitchReq req) {
String token = ys7Manager.getToken();
if (token == null || token.isEmpty()) {
log.error("updateOneKeySwitch获取token失败无法执行设备开关操作");
return 0;
}
// 3. 遍历设备序列号,提交异步任务
List<String> 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<OthYs7Device>()
.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<OthYs7Device> 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<OthYs7Device>()
.set(OthYs7Device::getEnable, b)
.eq(OthYs7Device::getDeviceSerial, finalDeviceSerial));
log.info("设备{}全天录像开关状态更新成功,开关状态:{},影响行数:{}",
finalDeviceSerial, b, updateRows);
}
} catch (Exception e) {
// 捕获所有异常,确保单个设备失败不影响其他设备
log.error("设备{}全天录像开关状态获取失败", finalDeviceSerial, e);
}
}, getDeviceSwitchExecutor); // 指定自定义线程池
}
return true;
}
/** /**
* 验证萤石摄像对象是否发生更改 * 验证萤石摄像对象是否发生更改
* *