[add] 新增萤石摄像头预置位相关逻辑 [update] 修改萤石摄像头相关逻辑

This commit is contained in:
lcj
2025-06-17 18:03:23 +08:00
parent 784c7c0783
commit 9d092facd0
26 changed files with 611 additions and 126 deletions

View File

@ -49,7 +49,7 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://192.168.110.126:3306/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.110.119:3306/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# # 从库数据源
@ -273,5 +273,8 @@ weather:
dxf2GeoJson:
file-name: main.exe
ys7:
app-key: "f01490bd5d5241b7809d8fc5fe84f7f8"
app-secret: "d468f270699de855fd85fe7fd6f9595f"
app-key: 3acf9f1a43dc4209841e0893003db0a2
app-secret: 4bbf3e9394f55d3af6e3af27b2d3db36
#ys7:
# app-key: f01490bd5d5241b7809d8fc5fe84f7f8
# app-secret: d468f270699de855fd85fe7fd6f9595f

View File

@ -275,5 +275,5 @@ weather:
dxf2GeoJson:
file-name: main
ys7:
app-key: "f01490bd5d5241b7809d8fc5fe84f7f8"
app-secret: "d468f270699de855fd85fe7fd6f9595f"
app-key: xx
app-secret: xx

View File

@ -120,6 +120,8 @@ security:
- /*/api-docs
- /*/api-docs/**
- /warm-flow-ui/token-name
- /other/ys7Device/webhook
- /other/ys7Device/test
# 多租户配置
tenant:

View File

@ -0,0 +1,40 @@
package org.dromara.job.cycle;
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.service.IOthYs7DeviceService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author lcj
* @date 2025/6/17 9:33
*/
@Slf4j
@Component
public class IncSyncYs7DeviceData {
@Resource
private Ys7Manager ys7Manager;
@Resource
private IOthYs7DeviceService ys7DeviceService;
// 每 5 分钟执行一次
@Scheduled(cron = "0 */5 * * * ?")
public void run() {
log.info("定时同步摄像头设备数据");
List<Ys7QueryDeviceResponseVo> ys7QueryDeviceList = ys7Manager.queryAllDeviceList();
Boolean result = ys7DeviceService.saveOrUpdateByDeviceList(ys7QueryDeviceList);
if (result) {
log.info("定时同步摄像头设备数据成功");
} else {
log.info("没有需要定时同步的设备");
}
}
}

View File

@ -15,7 +15,7 @@ import java.util.List;
* @date 2025/6/13 11:08
*/
@Slf4j
//@Component
@Component
public class FullSyncYs7DeviceData implements CommandLineRunner {
@Resource

View File

@ -16,6 +16,11 @@ public interface Ys7Constant {
*/
Integer MAX_PAGE_SIZE = 50;
/**
* 设备在线状态类型
*/
String ON_OFF_LINE_TOPIC_TYPE = "ys.onoffline";
/**
* 获取 token 请求地址 Post
*/
@ -30,4 +35,19 @@ public interface Ys7Constant {
* 修改设备名称请求地址 Post
*/
String updateDeviceNameUrlByPost = "https://open.ys7.com/api/lapp/device/name/update";
/**
* 添加设备预置点请求地址 Post
*/
String addDevicePresetUrlByPost = "https://open.ys7.com/api/lapp/device/preset/add";
/**
* 调用设备预置点请求地址 Post
*/
String moveDevicePresetUrlByPost = "https://open.ys7.com/api/lapp/device/preset/move";
/**
* 清除设备预置点请求地址 Post
*/
String deleteDevicePresetUrlByPost = "https://open.ys7.com/api/lapp/device/preset/clear";
}

View File

@ -65,4 +65,50 @@ public class Ys7Manager {
stringRedisTemplate.opsForValue().set(tokenRedisKey, newToken, 6, TimeUnit.DAYS);
return newToken;
}
/**
* 更新设备名称
*
* @param deviceSerial 设备序列号
* @param deviceName 设备名称
* @return 是否成功
*/
public Boolean updateDeviceName(String deviceSerial, String deviceName) {
return Ys7RequestUtils.updateDeviceName(getToken(), deviceSerial, deviceName);
}
/**
* 添加设备预置点
*
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @return 预置点编号
*/
public Integer addDevicePreset(String deviceSerial, int channelNo) {
return Ys7RequestUtils.addDevicePreset(getToken(), deviceSerial, channelNo);
}
/**
* 移动设备预置点
*
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @param index 预置点编号
* @return 是否成功
*/
public Boolean moveDevicePreset(String deviceSerial, int channelNo, int index) {
return Ys7RequestUtils.moveDevicePreset(getToken(), deviceSerial, channelNo, index);
}
/**
* 删除设备预置点
*
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @param index 预置点编号
* @return 是否成功
*/
public Boolean deleteDevicePreset(String deviceSerial, int channelNo, int index) {
return Ys7RequestUtils.deleteDevicePreset(getToken(), deviceSerial, channelNo, index);
}
}

View File

@ -1,6 +1,5 @@
package org.dromara.manager.ys7manager;
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
@ -75,7 +74,7 @@ public class Ys7RequestUtils {
paramMap.put("accessToken", accessToken);
paramMap.put("pageStart", pageStart);
paramMap.put("pageSize", pageSize);
String errorMsg = String.format("Ys7 分页查询设备列表 第%s页大小%s 请求失败", (pageStart + 1), pageSize);
String errorMsg = String.format("Ys7 分页查询设备列表 第%s页大小%s 请求失败", pageStart, pageSize);
try (HttpResponse response = HttpRequest.post(Ys7Constant.queryDevicePageUrlByPost)
.form(paramMap)
.execute()) {
@ -89,7 +88,8 @@ public class Ys7RequestUtils {
log.error("{}{}", errorMsg, responseVo.getMsg());
throw new ServiceException(errorMsg + responseVo.getMsg());
}
log.info("Ys7 分页查询设备列表 第{}页大小{} 请求成功{}", (pageStart + 1), pageSize, body);
log.info("Ys7 分页查询设备列表 第{}页大小{} 响应数据{}", pageStart, pageSize, responseVo.getData());
log.info("Ys7 分页查询设备列表 第{}页大小{} 请求成功:{}", pageStart, pageSize, responseVo.getPage());
return JSONUtil.toList(responseVo.getData(), Ys7QueryDeviceResponseVo.class);
}
}
@ -110,9 +110,6 @@ public class Ys7RequestUtils {
if (deviceName.length() > 50) {
throw new ServiceException("设备名称长度不能超过50个字符", HttpStatus.BAD_REQUEST);
}
if (!Validator.isGeneralWithChinese(deviceName)) {
throw new ServiceException("设备名称不能包含特殊字符", HttpStatus.BAD_REQUEST);
}
// 组合请求体
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("accessToken", accessToken);
@ -138,4 +135,118 @@ public class Ys7RequestUtils {
}
}
/**
* 添加设备预置位
*
* @param accessToken accessToken
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @return 预置点序号
*/
public static int addDevicePreset(String accessToken, String deviceSerial, int channelNo) {
if (StringUtils.isAnyBlank(accessToken, deviceSerial)) {
throw new ServiceException("添加设备预置位参数为空", HttpStatus.BAD_REQUEST);
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("accessToken", accessToken);
paramMap.put("deviceSerial", deviceSerial);
paramMap.put("channelNo", channelNo);
String errorMsg = "添加设备预置位请求失败";
try (HttpResponse response = HttpRequest.post(Ys7Constant.addDevicePresetUrlByPost)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
Ys7ResponseVo responseVo = JSONUtil.toBean(body, Ys7ResponseVo.class);
if (!responseVo.getCode().equals("200")) {
log.error("{}{}", errorMsg, responseVo.getMsg());
throw new ServiceException(errorMsg + responseVo.getMsg());
}
// 获取 data 中的 index
String data = responseVo.getData();
// 解析为 JSONObject
JSONObject jsonObject = JSONUtil.parseObj(data);
Integer index = jsonObject.getInt("index");
log.info("添加设备预置位请求成功,设备 {} 添加预置点成功,通道:{},序号 {}", deviceSerial, channelNo, index);
return index;
}
}
/**
* 调用设备预置点
*
* @param accessToken accessToken
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @param index 预置点序号
* @return 是否调用成功
*/
public static Boolean moveDevicePreset(String accessToken, String deviceSerial, int channelNo, int index) {
if (StringUtils.isAnyBlank(accessToken, deviceSerial)) {
throw new ServiceException("调用设备预置点参数为空", HttpStatus.BAD_REQUEST);
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("accessToken", accessToken);
paramMap.put("deviceSerial", deviceSerial);
paramMap.put("channelNo", channelNo);
paramMap.put("index", index);
String errorMsg = "调用设备预置点请求失败";
try (HttpResponse response = HttpRequest.post(Ys7Constant.moveDevicePresetUrlByPost)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
Ys7NoDataResponseVo responseVo = JSONUtil.toBean(body, Ys7NoDataResponseVo.class);
if (!responseVo.getCode().equals("200")) {
log.error("{}{}", errorMsg, responseVo.getMsg());
throw new ServiceException(errorMsg + responseVo.getMsg());
}
log.info("调用设备预置点请求成功,设备 {} 调用预置点成功,通道:{},序号 {}", deviceSerial, channelNo, index);
return true;
}
}
/**
* 删除设备预置点
*
* @param accessToken accessToken
* @param deviceSerial 设备序列号
* @param channelNo 通道号
* @param index 预置点序号
* @return 是否删除成功
*/
public static Boolean deleteDevicePreset(String accessToken, String deviceSerial, int channelNo, int index) {
if (StringUtils.isAnyBlank(accessToken, deviceSerial)) {
throw new ServiceException("删除设备预置点参数为空", HttpStatus.BAD_REQUEST);
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("accessToken", accessToken);
paramMap.put("deviceSerial", deviceSerial);
paramMap.put("channelNo", channelNo);
paramMap.put("index", index);
String errorMsg = "删除设备预置点请求失败";
try (HttpResponse response = HttpRequest.post(Ys7Constant.deleteDevicePresetUrlByPost)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
Ys7NoDataResponseVo responseVo = JSONUtil.toBean(body, Ys7NoDataResponseVo.class);
if (!responseVo.getCode().equals("200")) {
log.error("{}{}", errorMsg, responseVo.getMsg());
throw new ServiceException(errorMsg + responseVo.getMsg());
}
log.info("删除设备预置点请求成功,设备 {} 删除预置点成功,通道:{},序号 {}", deviceSerial, channelNo, index);
return true;
}
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.manager.ys7manager.enums;
import lombok.Getter;
/**
* @author lcj
* @date 2025/6/17 15:36
*/
@Getter
public enum DeviceOnOffLineEnum {
ONLINE("设备上线消息", "ONLINE"),
OFFLINE("设备离线消息", "OFFLINE");
private final String text;
private final String value;
DeviceOnOffLineEnum(String text, String value) {
this.text = text;
this.value = value;
}
}

View File

@ -3,7 +3,6 @@ package org.dromara.other.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
@ -93,16 +92,29 @@ public class OthDevicePresetController extends BaseController {
return toAjax(othDevicePresetService.updateByBo(req));
}
/**
* 调用摄像头预置位
*
* @param id 主键
*/
@SaCheckPermission("other:devicePreset:edit")
@Log(title = "摄像头预置位", businessType = BusinessType.UPDATE)
@PutMapping("/move/{id}")
public R<Void> move(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return toAjax(othDevicePresetService.moveById(id));
}
/**
* 删除摄像头预置位
*
* @param ids 主键串
* @param id 主键串
*/
@SaCheckPermission("other:devicePreset:remove")
@Log(title = "摄像头预置位", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(othDevicePresetService.deleteWithValidByIds(List.of(ids), true));
@DeleteMapping("/{id}")
public R<Void> remove(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return toAjax(othDevicePresetService.deleteById(id));
}
}

View File

@ -1,10 +1,12 @@
package org.dromara.other.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
@ -15,16 +17,17 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceQueryReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateEncryptedReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceWithProjectReq;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import org.dromara.other.service.IOthYs7DeviceService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 萤石摄像头
@ -32,6 +35,7 @@ import java.util.List;
* @author lcj
* @date 2025-06-13
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/other/ys7Device")
@ -127,6 +131,29 @@ public class OthYs7DeviceController extends BaseController {
@SaCheckPermission("other:ys7Device:query")
@GetMapping("/get/token")
public R<String> getToken() {
return R.ok(ys7Manager.getToken());
return R.ok("操作成功", ys7Manager.getToken());
}
@RequestMapping(value = "/webhook")
public ResponseEntity<String> webhook(@RequestHeader HttpHeaders header, @RequestBody String body) {
WebhookMessage receiveMessage;
log.info("消息获取时间:{}, 请求头:{},请求体:{}", System.currentTimeMillis(), JSONUtil.toJsonStr(header), body);
System.out.println("收到的消息:" + body);
receiveMessage = JSONUtil.toBean(body, WebhookMessage.class);
// todo:对收到的消息进行处理,最好发送到其他中间件,或者写到数据库中,不要影响回调地址的处理
othYs7DeviceService.webhook(receiveMessage);
// 必须进行返回
Map<String, String> result = new HashMap<>(1);
assert receiveMessage != null;
String messageId = receiveMessage.getHeader().getMessageId();
result.put("messageId", messageId);
final ResponseEntity<String> resp = ResponseEntity.ok(JSONUtil.toJsonStr(result));
log.info("返回的信息:{}", JSONUtil.toJsonStr(result));
return resp;
}
@RequestMapping(value = "/test")
public R<String> test() {
return R.ok("操作成功", "测试成功");
}
}

View File

@ -35,17 +35,17 @@ public class OthDevicePreset implements Serializable {
/**
* 通道号
*/
private Long channelNo;
private Integer channelNo;
/**
* 预置点序号
*/
private Long index;
private Integer presetIndex;
/**
* 预置点
*/
private String name;
private String presetName;
/**
* 创建时间

View File

@ -1,7 +1,6 @@
package org.dromara.other.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@ -103,15 +102,4 @@ public class OthYs7Device implements Serializable {
*/
private Date updateTime;
/**
* 删除时间
*/
private Date deletedAt;
/**
* 是否删除0正常 1删除
*/
@TableLogic
private Integer isDelete;
}

View File

@ -25,16 +25,11 @@ public class OthDevicePresetCreateReq implements Serializable {
/**
* 通道号
*/
private Long channelNo;
/**
* 预置点序号
*/
private Long index;
private Integer channelNo;
/**
* 预置点
*/
private String name;
private String presetName;
}

View File

@ -25,16 +25,16 @@ public class OthDevicePresetQueryReq implements Serializable {
/**
* 通道号
*/
private Long channelNo;
private Integer channelNo;
/**
* 预置点序号
*/
private Long index;
private Integer presetIndex;
/**
* 预置点
*/
private String name;
private String presetName;
}

View File

@ -22,19 +22,10 @@ public class OthDevicePresetUpdateReq implements Serializable {
@NotNull(message = "主键id不能为空")
private Long id;
/**
* 通道号
*/
private Long channelNo;
/**
* 预置点序号
*/
private Long index;
/**
* 预置点
*/
private String name;
@NotNull(message = "预置点名称不能为空")
private String presetName;
}

View File

@ -20,11 +20,6 @@ public class OthYs7DeviceUpdateReq implements Serializable {
*/
private Long id;
/**
* 项目id
*/
private Long projectId;
/**
* 设备名称
*/

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lcj
@ -20,7 +21,7 @@ public class OthYs7DeviceWithProjectReq implements Serializable {
* 主键
*/
@NotNull(message = "主键不能为空")
private Long id;
private List<Long> id;
/**
* 项目id

View File

@ -0,0 +1,63 @@
package org.dromara.other.domain.dto.ys7device;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lcj
* @date 2025/6/17 11:30
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebhookMessage implements Serializable {
@Serial
private static final long serialVersionUID = -3462174422247941389L;
/**
* 消息头
*/
private WebhookMessageHeader header;
/**
* 消息体
*/
private Object body;
@Data
@NoArgsConstructor
public static class WebhookMessageHeader {
/**
* 消息id
*/
private String messageId;
/**
* 设备序列号
*/
private String deviceId;
/**
* 消息类型,需向消息管道服务申请
*/
private String type;
/**
* 通道号
*/
private Integer channelNo;
/**
* 消息推送时间
*/
private Long messageTime;
}
}

View File

@ -0,0 +1,34 @@
package org.dromara.other.domain.enums;
import lombok.Getter;
/**
* @author lcj
* @date 2025/6/17 10:54
*/
@Getter
public enum OthDeviceStatusEnum {
ONLINE("在线", 1),
OFFLINE("离线", 0);
private final String text;
private final int value;
OthDeviceStatusEnum(String text, int value) {
this.text = text;
this.value = value;
}
// 根据 value 获取对应的 text
public static String getTextByValue(int value) {
for (OthDeviceStatusEnum type : values()) {
if (type.getValue() == value) {
return type.getText();
}
}
return null;
}
}

View File

@ -40,18 +40,18 @@ public class OthDevicePresetVo implements Serializable {
* 通道号
*/
@ExcelProperty(value = "通道号")
private Long channelNo;
private Integer channelNo;
/**
* 预置点序号
*/
@ExcelProperty(value = "预置点序号")
private Long index;
private Integer presetIndex;
/**
* 预置点
*/
@ExcelProperty(value = "预置点")
private String name;
private String presetName;
}

View File

@ -11,7 +11,6 @@ import org.dromara.other.domain.dto.devicepreset.OthDevicePresetQueryReq;
import org.dromara.other.domain.dto.devicepreset.OthDevicePresetUpdateReq;
import org.dromara.other.domain.vo.devicepreset.OthDevicePresetVo;
import java.util.Collection;
import java.util.List;
/**
@ -63,14 +62,21 @@ public interface IOthDevicePresetService extends IService<OthDevicePreset> {
*/
Boolean updateByBo(OthDevicePresetUpdateReq req);
/**
* 调用摄像头预置位
*
* @param id 摄像头预置位id
* @return 是否调用成功
*/
Boolean moveById(Long id);
/**
* 校验并批量删除摄像头预置位信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @param id 待删除的主键
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
Boolean deleteById(Long id);
/**
* 获取摄像头预置位视图

View File

@ -7,10 +7,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceQueryReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateEncryptedReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceWithProjectReq;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import java.util.Collection;
@ -130,4 +127,10 @@ public interface IOthYs7DeviceService extends IService<OthYs7Device> {
*/
Boolean saveOrUpdateByDeviceList(List<Ys7QueryDeviceResponseVo> deviceResponseVoList);
/**
* 萤石摄像头webhook
*
* @param receiveMessage 接收消息
*/
void webhook(WebhookMessage receiveMessage);
}

View File

@ -11,19 +11,22 @@ import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.other.domain.OthDevicePreset;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.devicepreset.OthDevicePresetCreateReq;
import org.dromara.other.domain.dto.devicepreset.OthDevicePresetQueryReq;
import org.dromara.other.domain.dto.devicepreset.OthDevicePresetUpdateReq;
import org.dromara.other.domain.enums.OthDeviceStatusEnum;
import org.dromara.other.domain.vo.devicepreset.OthDevicePresetVo;
import org.dromara.other.mapper.OthDevicePresetMapper;
import org.dromara.other.service.IOthDevicePresetService;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.project.service.IBusProjectService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
@ -39,6 +42,12 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
@Resource
private IOthYs7DeviceService othYs7DeviceService;
@Resource
private IBusProjectService projectService;
@Resource
private Ys7Manager ys7Manager;
/**
* 查询摄像头预置位
*
@ -90,6 +99,17 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
OthDevicePreset devicePreset = new OthDevicePreset();
BeanUtils.copyProperties(req, devicePreset);
validEntityBeforeSave(devicePreset, true);
OthYs7Device ys7Device = othYs7DeviceService.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, devicePreset.getDeviceSerial())
.one();
if (ys7Device == null) {
throw new ServiceException("摄像头信息不存在", HttpStatus.NOT_FOUND);
}
if (!ys7Device.getStatus().equals(OthDeviceStatusEnum.ONLINE.getValue())) {
throw new ServiceException("摄像头不在线", HttpStatus.ERROR);
}
Integer index = ys7Manager.addDevicePreset(req.getDeviceSerial(), Math.toIntExact(req.getChannelNo()));
devicePreset.setPresetIndex(index);
boolean result = this.save(devicePreset);
if (!result) {
throw new ServiceException("摄像头预置位信息新增失败", HttpStatus.ERROR);
@ -105,6 +125,10 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
*/
@Override
public Boolean updateByBo(OthDevicePresetUpdateReq req) {
String presetName = req.getPresetName();
if (StringUtils.isBlank(presetName)) {
throw new ServiceException("预置点名称不能为空", HttpStatus.ERROR);
}
OthDevicePreset devicePreset = new OthDevicePreset();
BeanUtils.copyProperties(req, devicePreset);
validEntityBeforeSave(devicePreset, false);
@ -119,6 +143,34 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
return true;
}
/**
* 调用摄像头预置位
*
* @param id 摄像头预置位id
* @return 是否调用成功
*/
@Override
public Boolean moveById(Long id) {
OthDevicePreset devicePreset = this.getById(id);
if (devicePreset == null) {
throw new ServiceException("摄像头预置位信息不存在", HttpStatus.NOT_FOUND);
}
OthYs7Device ys7Device = othYs7DeviceService.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, devicePreset.getDeviceSerial())
.one();
if (ys7Device == null) {
throw new ServiceException("摄像头信息不存在", HttpStatus.NOT_FOUND);
}
boolean result = ys7Manager.moveDevicePreset(
devicePreset.getDeviceSerial(),
devicePreset.getChannelNo(),
devicePreset.getPresetIndex());
if (!result) {
throw new ServiceException("调用摄像头预置位失败", HttpStatus.ERROR);
}
return true;
}
/**
* 保存前的数据校验
*/
@ -143,16 +195,32 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
/**
* 校验并批量删除摄像头预置位信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @param id 待删除的主键
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
public Boolean deleteById(Long id) {
Long userId = LoginHelper.getUserId();
OthDevicePreset devicePreset = this.getById(id);
String deviceSerial = devicePreset.getDeviceSerial();
OthYs7Device device = othYs7DeviceService.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, deviceSerial)
.one();
if (device == null) {
throw new ServiceException("设备不存在", HttpStatus.BAD_REQUEST);
}
return baseMapper.deleteByIds(ids) > 0;
Long projectId = device.getProjectId();
if (projectId != 0) {
projectService.validAuth(projectId, userId);
}
Boolean result = ys7Manager.deleteDevicePreset(
deviceSerial,
devicePreset.getChannelNo(),
devicePreset.getPresetIndex());
if (!result) {
throw new ServiceException("删除摄像头预置位信息失败", HttpStatus.ERROR);
}
return this.removeById(id);
}
/**
@ -181,13 +249,13 @@ public class OthDevicePresetServiceImpl extends ServiceImpl<OthDevicePresetMappe
public LambdaQueryWrapper<OthDevicePreset> buildQueryWrapper(OthDevicePresetQueryReq req) {
LambdaQueryWrapper<OthDevicePreset> lqw = new LambdaQueryWrapper<>();
String deviceSerial = req.getDeviceSerial();
Long channelNo = req.getChannelNo();
Long index = req.getIndex();
String name = req.getName();
Integer channelNo = req.getChannelNo();
Integer index = req.getPresetIndex();
String name = req.getPresetName();
lqw.eq(StringUtils.isNotBlank(deviceSerial), OthDevicePreset::getDeviceSerial, deviceSerial);
lqw.eq(ObjectUtils.isNotEmpty(channelNo), OthDevicePreset::getChannelNo, channelNo);
lqw.eq(ObjectUtils.isNotEmpty(index), OthDevicePreset::getIndex, index);
lqw.like(StringUtils.isNotBlank(name), OthDevicePreset::getName, name);
lqw.eq(ObjectUtils.isNotEmpty(index), OthDevicePreset::getPresetIndex, index);
lqw.like(StringUtils.isNotBlank(name), OthDevicePreset::getPresetName, name);
return lqw;
}

View File

@ -1,6 +1,8 @@
package org.dromara.other.service.impl;
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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -13,14 +15,13 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.manager.ys7manager.Ys7Constant;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.Ys7RequestUtils;
import org.dromara.manager.ys7manager.enums.DeviceOnOffLineEnum;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceQueryReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateEncryptedReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceUpdateReq;
import org.dromara.other.domain.dto.ys7device.OthYs7DeviceWithProjectReq;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.enums.OthDeviceStatusEnum;
import org.dromara.other.domain.enums.OthVideoEncryptedEnum;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import org.dromara.other.mapper.OthYs7DeviceMapper;
@ -109,7 +110,6 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
if (oldYs7Device == null) {
throw new ServiceException("萤石摄像头信息不存在", HttpStatus.NOT_FOUND);
}
validEntityBeforeSave(ys7Device);
// 判断是否更新名称
String deviceName = req.getDeviceName();
if (deviceName != null && !deviceName.equals(oldYs7Device.getDeviceName())) {
@ -119,8 +119,7 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
if (count > 0) {
throw new ServiceException("已存在同名萤石摄像头", HttpStatus.CONFLICT);
}
String token = ys7Manager.getToken();
Boolean result = Ys7RequestUtils.updateDeviceName(token, oldYs7Device.getDeviceSerial(), deviceName);
Boolean result = ys7Manager.updateDeviceName(oldYs7Device.getDeviceSerial(), deviceName);
if (!result) {
throw new ServiceException("更新云端萤石摄像头名称异常", HttpStatus.ERROR);
}
@ -140,14 +139,14 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
*/
@Override
public Boolean updateWithProject(OthYs7DeviceWithProjectReq req) {
Long id = req.getId();
List<Long> ids = req.getId();
Long projectId = req.getProjectId();
// 参数校验
if (id == null || projectId == null) {
if (CollUtil.isEmpty(ids) || projectId == null) {
throw new ServiceException("参数为空", HttpStatus.BAD_REQUEST);
}
OthYs7Device ys7Device = this.getById(id);
if (ys7Device == null) {
List<OthYs7Device> ys7DeviceList = this.listByIds(ids);
if (CollUtil.isEmpty(ys7DeviceList)) {
throw new ServiceException("萤石摄像头信息不存在", HttpStatus.NOT_FOUND);
}
BusProject project = projectService.getById(projectId);
@ -159,7 +158,7 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
projectService.validAuth(projectId, userId);
// 更新
boolean update = this.lambdaUpdate()
.eq(OthYs7Device::getId, id)
.in(OthYs7Device::getId, ids)
.set(OthYs7Device::getProjectId, projectId)
.update();
if (!update) {
@ -203,19 +202,6 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
return true;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(OthYs7Device entity) {
//TODO 做一些数据校验,如唯一约束
Long projectId = entity.getProjectId();
if (projectId != null && projectId != 0) {
if (projectService.getById(projectId) == null) {
throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
}
}
}
/**
* 校验并批量删除萤石摄像头信息
*
@ -224,11 +210,22 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
Long userId = LoginHelper.getUserId();
List<OthYs7Device> deviceList = this.listByIds(ids);
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
// TODO 做一些业务上的校验,判断是否需要校验
List<Long> projectIds = deviceList.stream()
.map(OthYs7Device::getProjectId)
.filter(id -> id != 0)
.distinct()
.toList();
if (CollUtil.isNotEmpty(projectIds)) {
projectService.validAuth(projectIds, userId);
}
}
return baseMapper.deleteByIds(ids) > 0;
return this.removeBatchByIds(ids);
}
/**
@ -268,15 +265,17 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
String deviceType = req.getDeviceType();
String deviceVersion = req.getDeviceVersion();
Long status = req.getStatus();
lqw.eq(ObjectUtils.isNotEmpty(projectId), OthYs7Device::getProjectId, projectId)
lqw.and(ObjectUtils.isNotEmpty(projectId), w -> w
.eq(OthYs7Device::getProjectId, projectId)
.or()
.eq(OthYs7Device::getProjectId, 0L);
.eq(OthYs7Device::getProjectId, 0L));
lqw.eq(ObjectUtils.isNotEmpty(status), OthYs7Device::getStatus, status);
lqw.like(StringUtils.isNotBlank(deviceName), OthYs7Device::getDeviceName, deviceName);
lqw.like(StringUtils.isNotBlank(deviceType), OthYs7Device::getDeviceType, deviceType);
lqw.like(StringUtils.isNotBlank(deviceSerial), OthYs7Device::getDeviceSerial, deviceSerial);
lqw.like(StringUtils.isNotBlank(deviceVersion), OthYs7Device::getDeviceVersion, deviceVersion);
lqw.orderByDesc(OthYs7Device::getProjectId);
lqw.orderByDesc(OthYs7Device::getStatus);
return lqw;
}
@ -336,7 +335,6 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
ys7Device.setStatus(deviceResponseVo.getStatus());
ys7Device.setDefence(Long.valueOf(deviceResponseVo.getDefence()));
ys7Device.setDeviceVersion(deviceResponseVo.getDeviceVersion());
ys7Device.setPosition(deviceResponseVo.getNetAddress());
ys7Device.setDeviceCreateTime(new Date(deviceResponseVo.getAddTime()));
ys7Device.setDeviceUpdateTime(new Date(deviceResponseVo.getUpdateTime()));
ys7Device.setRiskLevel(deviceResponseVo.getRiskLevel());
@ -370,6 +368,9 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
return false;
}
List<OthYs7Device> deviceList = this.getEntityList(deviceResponseVoList);
if (CollUtil.isEmpty(deviceList)) {
return false;
}
// 查询数据库中的数据并构建 Map<deviceSerial, OthYs7Device>
Map<String, OthYs7Device> entityMap = this.list().stream()
.collect(Collectors.toMap(
@ -381,6 +382,11 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
StringBuffer saveLog = new StringBuffer();
// 遍历设备列表,设置已存在的 id
List<OthYs7Device> resultList = deviceList.stream()
.filter(device -> {
OthYs7Device existing = entityMap.get(device.getDeviceSerial());
// 若已有记录且未发生变化,则过滤掉
return existing == null || !validDeviceUpdate(existing, device);
})
.peek(device -> {
OthYs7Device existing = entityMap.get(device.getDeviceSerial());
if (existing != null) {
@ -399,9 +405,59 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
}
})
.toList();
log.info("需新增信息设备:{}", saveLog);
log.info("修改信息设备:{}", updateLog);
// 更新或保存设备列表
return saveOrUpdateBatch(resultList);
if (CollUtil.isNotEmpty(resultList)) {
log.info("新增信息设备:{}", saveLog);
log.info("需修改信息设备:{}", updateLog);
// 更新或保存设备列表
return saveOrUpdateBatch(resultList);
}
return false;
}
/**
* 萤石摄像头webhook
*
* @param receiveMessage 接收消息
*/
@Override
public void webhook(WebhookMessage receiveMessage) {
String type = receiveMessage.getHeader().getType();
// 设备上下线消息
if (Ys7Constant.ON_OFF_LINE_TOPIC_TYPE.equals(type)) {
JSONObject jsonObject = JSONUtil.parseObj(receiveMessage.getBody());
String deviceSerial = jsonObject.get("subSerial", String.class);
String msgType = jsonObject.get("msgType", String.class);
OthYs7Device device = this.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, deviceSerial)
.one();
if (device == null) {
log.info("更新设备上下线状态失败,设备不存在:{}", deviceSerial);
return;
}
OthYs7Device updateDevice = new OthYs7Device();
updateDevice.setId(device.getId());
if (DeviceOnOffLineEnum.ONLINE.getValue().equals(msgType)) {
updateDevice.setStatus(OthDeviceStatusEnum.ONLINE.getValue());
} else if (DeviceOnOffLineEnum.OFFLINE.getValue().equals(msgType)) {
updateDevice.setStatus(OthDeviceStatusEnum.OFFLINE.getValue());
}
this.updateById(updateDevice);
}
}
/**
* 验证萤石摄像对象是否发生更改
*
* @param cloudDevice 云端萤石摄像对象
* @param device 萤石摄像对象
* @return true云端设备与数据库中设备一致false云端设备与数据库中设备存在差异即至少有一个字段发生了变化。
*/
private Boolean validDeviceUpdate(OthYs7Device cloudDevice, OthYs7Device device) {
return Objects.equals(cloudDevice.getDeviceName(), device.getDeviceName())
&& Objects.equals(cloudDevice.getDeviceType(), device.getDeviceType())
&& Objects.equals(cloudDevice.getStatus(), device.getStatus())
&& Objects.equals(cloudDevice.getDefence(), device.getDefence())
&& Objects.equals(cloudDevice.getDeviceVersion(), device.getDeviceVersion())
&& Objects.equals(cloudDevice.getRiskLevel(), device.getRiskLevel());
}
}

View File

@ -1097,9 +1097,9 @@ CREATE TABLE `oth_device_preset`
(
`id` bigint not null auto_increment comment '主键id',
`device_serial` varchar(255) null comment '设备序列号',
`channel_no` bigint null comment '通道号',
`index` int null comment '预置点序号',
`name` varchar(255) null comment '预置点',
`channel_no` int null comment '通道号',
`preset_index` int null comment '预置点序号',
`preset_name` varchar(255) null comment '预置点',
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
primary key (`id`) using btree