Merge branch 'master' of http://47.109.37.87:3000/by2025/SmartParks
This commit is contained in:
@@ -21,4 +21,6 @@ public class ZLMediaKitConfig {
|
||||
|
||||
private String vhost;
|
||||
|
||||
private String pushStreamUrl;
|
||||
|
||||
}
|
||||
|
@@ -33,31 +33,35 @@ public class CleanLiftAuthRocketConsumer implements RocketMQListener<MessageExt>
|
||||
public void onMessage(MessageExt ext) {
|
||||
log.info("【消费者】接收消息:消息体 => {}", new String(ext.getBody()));
|
||||
|
||||
Long deviceId = Long.parseLong(ext.getUserProperty("deviceId"));
|
||||
Long groupId = Long.parseLong(ext.getUserProperty("groupId"));
|
||||
Long deviceFloorId = Long.parseLong(ext.getUserProperty("deviceFloorId"));
|
||||
|
||||
// 获取当前电梯信息
|
||||
SisElevatorInfoVo ele = sisElevatorInfoService.queryById(deviceId);
|
||||
|
||||
// 获取权限组下电梯⇄楼层关联信息
|
||||
List<SisElevatorFloorRefVo> groupRef = sisElevatorFloorRefService.queryByAuthGroupId(groupId);
|
||||
|
||||
// 取出当前电梯的楼层授权信息
|
||||
List<SisElevatorFloorRefVo> eleRef = groupRef.stream().filter(o -> Objects.equals(o.getElevatorId(), deviceId)).toList();
|
||||
|
||||
for (SisElevatorFloorRefVo ref : eleRef){
|
||||
if (ref.getUpChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getUpChannel().intValue(), 3);
|
||||
}
|
||||
|
||||
if (ref.getDownChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getDownChannel().intValue(), 3);
|
||||
}
|
||||
|
||||
if (ref.getInChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getInChannel().intValue(), 3);
|
||||
}
|
||||
// Long deviceId = Long.parseLong(ext.getUserProperty("deviceId"));
|
||||
// Long groupId = Long.parseLong(ext.getUserProperty("groupId"));
|
||||
// Long deviceFloorId = Long.parseLong(ext.getUserProperty("deviceFloorId"));
|
||||
//
|
||||
// // 获取当前电梯信息
|
||||
// SisElevatorInfoVo ele = sisElevatorInfoService.queryById(deviceId);
|
||||
//
|
||||
// // 获取权限组下电梯⇄楼层关联信息
|
||||
// List<SisElevatorFloorRefVo> groupRef = sisElevatorFloorRefService.queryByAuthGroupId(groupId);
|
||||
//
|
||||
// // 取出当前电梯的楼层授权信息
|
||||
// List<SisElevatorFloorRefVo> eleRef = groupRef.stream().filter(o -> Objects.equals(o.getElevatorId(), deviceId)).toList();
|
||||
//
|
||||
// for (SisElevatorFloorRefVo ref : eleRef){
|
||||
// if (ref.getUpChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
// HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getUpChannel().intValue(), 3);
|
||||
// }
|
||||
//
|
||||
// if (ref.getDownChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
// HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getDownChannel().intValue(), 3);
|
||||
// }
|
||||
//
|
||||
// if (ref.getInChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)){
|
||||
// HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getInChannel().intValue(), 3);
|
||||
// }
|
||||
// }
|
||||
// todo 暂时应急处理,后续调整业务逻辑
|
||||
for (int i = 1; i < 5; i++) {
|
||||
HikApiService.getInstance().controlGateway("192.168.24.150", i, 3);
|
||||
}
|
||||
log.info("梯控清除权限完成");
|
||||
}
|
||||
|
@@ -108,6 +108,7 @@ public class SisDeviceManageController extends BaseController {
|
||||
return toAjax(sisDeviceManageService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/tree")
|
||||
public R<List<TreeNode<Long>>> tree() {
|
||||
return R.ok(sisDeviceManageService.tree());
|
||||
|
@@ -32,7 +32,7 @@ public class VideoAlarmController {
|
||||
}
|
||||
|
||||
@PostMapping("/huawei/callback")
|
||||
public void huaweiAlarm(@RequestBody String data) {
|
||||
public void huaweiAlarm(@RequestBody Object data) {
|
||||
log.info("华为上报消息,msg={}", data);
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,11 @@
|
||||
package org.dromara.sis.controller.zkmedia;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.sis.api.enums.FactoryNoEnum;
|
||||
import org.dromara.sis.sdk.zkmedia.MediaServerUtils;
|
||||
import org.dromara.sis.sdk.zkmedia.ZLMediaKitService;
|
||||
import org.dromara.sis.sdk.zkmedia.model.AddStreamProxy;
|
||||
import org.dromara.sis.sdk.zkmedia.model.AddStreamProxyResp;
|
||||
import org.dromara.sis.sdk.zkmedia.model.StartStreamProxy;
|
||||
import org.dromara.sis.sdk.zkmedia.model.StreamPlay;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -31,35 +28,31 @@ public class ZKLmediaController {
|
||||
@Resource
|
||||
private ZLMediaKitService zlMediaKitService;
|
||||
|
||||
|
||||
private static final String HIK_REALTIME_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/Streaming/Channels/%s";
|
||||
private static final String DAHUA_REALTIME_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/cam/realmonitor?channel=%s&subtype=0";
|
||||
|
||||
private static final String HIK_HISTORY_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/Streaming/tracks/%s?starttime=%s&endtime=%s";
|
||||
private static final String DAHUA_HISTORY_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/cam/playback?channel=%s&subtype=0&starttime=%s&endtime=%s";
|
||||
|
||||
/**
|
||||
* 创建拉流任务,返回null代表创建拉流任务失败
|
||||
*
|
||||
* @param data 创建拉流设备信息(如果外网不建议使用这种方式)
|
||||
* @return 返回拉流任务信息
|
||||
* @return 返回播放地址
|
||||
*/
|
||||
@PostMapping("/realtime/add")
|
||||
public R<AddStreamProxyResp> alarm(@RequestBody @Validated AddStreamProxy data) {
|
||||
StartStreamProxy proxy = new StartStreamProxy();
|
||||
proxy.setApp("realtime");
|
||||
// 实时流不用每次都去拉流,流不存在的情况下在拉取
|
||||
String streanStr = data.getVideoIp() + "_" + data.getChannelId();
|
||||
// proxy.setStream(SecureUtil.md5(streanStr));
|
||||
proxy.setStream(IdUtil.fastSimpleUUID());
|
||||
if (FactoryNoEnum.HIK.getCode().equals(data.getFactoryNo())) {
|
||||
proxy.setUrl(String.format(HIK_REALTIME_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId()));
|
||||
} else if (FactoryNoEnum.DAHUA.getCode().equals(data.getFactoryNo())) {
|
||||
proxy.setUrl(String.format(DAHUA_REALTIME_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId()));
|
||||
} else {
|
||||
throw new RuntimeException("未知的设备类型!");
|
||||
public R<AddStreamProxyResp> addStreamProxy(@RequestBody @Validated AddStreamProxy data) {
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addStreamProxy(data);
|
||||
if (addStreamProxyResp != null) {
|
||||
return R.ok(addStreamProxyResp);
|
||||
}
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addStreamProxy(proxy);
|
||||
return R.fail();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过是设备ip和通道新增拉流代理
|
||||
*
|
||||
* @param streamPlay 拉流参数
|
||||
* @return 返回播放地址
|
||||
*/
|
||||
@PostMapping("/proxy")
|
||||
public R<AddStreamProxyResp> addStreamProxy(@RequestBody @Validated StreamPlay streamPlay) {
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addStreamProxy(streamPlay);
|
||||
if (addStreamProxyResp != null) {
|
||||
return R.ok(addStreamProxyResp);
|
||||
}
|
||||
@@ -67,23 +60,14 @@ public class ZKLmediaController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建拉流任务,返回null代表创建拉流任务失败
|
||||
* 新增ffmpeg拉流代理
|
||||
*
|
||||
* @param data 创建拉流设备信息(如果外网不建议使用这种方式)
|
||||
* @return 返回拉流任务信息
|
||||
* @return 返回播放地址
|
||||
*/
|
||||
@PostMapping("/realtime/addFfmpeg")
|
||||
public R<AddStreamProxyResp> addFfmpegTask(@RequestBody @Validated AddStreamProxy data) {
|
||||
String sourceUrl = "";
|
||||
if (FactoryNoEnum.HIK.getCode().equals(data.getFactoryNo())) {
|
||||
sourceUrl = String.format(HIK_REALTIME_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId());
|
||||
} else if (FactoryNoEnum.DAHUA.getCode().equals(data.getFactoryNo())) {
|
||||
sourceUrl = String.format(DAHUA_REALTIME_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId());
|
||||
} else {
|
||||
throw new RuntimeException("未知的设备类型!");
|
||||
}
|
||||
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addFFmpegSource(sourceUrl);
|
||||
@PostMapping("/FFmpeg/proxy")
|
||||
public R<AddStreamProxyResp> addFfmpegStreamProxy(@RequestBody @Validated AddStreamProxy data) {
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addFfmpegStreamProxy(data);
|
||||
if (addStreamProxyResp != null) {
|
||||
return R.ok(addStreamProxyResp);
|
||||
}
|
||||
@@ -92,62 +76,17 @@ public class ZKLmediaController {
|
||||
|
||||
|
||||
/**
|
||||
* 创建历史回放拉流任务,返回null代表创建拉流任务失败
|
||||
* 通过是设备ip和通道新增ffmpeg拉流代理
|
||||
*
|
||||
* @param data 创建拉流设备信息(如果外网不建议使用这种方式)
|
||||
* @return 返回拉流任务信息
|
||||
* @param data 拉流参数
|
||||
* @return 返回播放地址
|
||||
*/
|
||||
@PostMapping("/history/add")
|
||||
public R<AddStreamProxyResp> history(@RequestBody @Validated AddStreamProxy data) throws InterruptedException {
|
||||
StartStreamProxy proxy = new StartStreamProxy();
|
||||
proxy.setApp("history");
|
||||
String s = IdUtil.fastSimpleUUID();
|
||||
proxy.setStream(s);
|
||||
if ("DS1010".equals(data.getFactoryNo())) {
|
||||
String pattern = "yyyyMMdd'T'HHmmss'Z'";
|
||||
String startTime = MediaServerUtils.formatTimestamp(data.getStartTime(), "yyyyMMdd'T'HHmmss'Z'");
|
||||
String endTime = MediaServerUtils.formatTimestamp(data.getEndTime(), "yyyyMMdd'T'HHmmss'Z'");
|
||||
proxy.setUrl(String.format(HIK_HISTORY_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId(), startTime, endTime));
|
||||
} else if ("DS1014".equals(data.getFactoryNo())) {
|
||||
String startTime = MediaServerUtils.formatTimestamp(data.getStartTime(), "yyyy_MM_dd_HH_mm_ss");
|
||||
String endTime = MediaServerUtils.formatTimestamp(data.getEndTime(), "yyyy_MM_dd_HH_mm_ss");
|
||||
proxy.setUrl(String.format(DAHUA_HISTORY_RTSP_TEMPLATE, data.getAccount(), data.getPwd(), data.getVideoIp(), data.getVideoPort(), data.getChannelId(), startTime, endTime));
|
||||
} else {
|
||||
throw new RuntimeException("未知的设备类型!");
|
||||
}
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addStreamProxy(proxy);
|
||||
@PostMapping("/ffmpeg/proxy")
|
||||
public R<AddStreamProxyResp> addFfmpegProxy(@RequestBody @Validated StreamPlay data) {
|
||||
AddStreamProxyResp addStreamProxyResp = zlMediaKitService.addFfmpegStreamProxy(data);
|
||||
if (addStreamProxyResp != null) {
|
||||
return R.ok(addStreamProxyResp);
|
||||
}
|
||||
return R.fail();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备的信息
|
||||
*
|
||||
* @param data 设备ip
|
||||
* @return
|
||||
*/
|
||||
/*@PostMapping("/queryMediaInfo")
|
||||
public R<TpEqpAcquisitionDTO> queryMediaInfo(@RequestBody VideoConfigTreeDTO data) {
|
||||
if (StringUtils.isEmpty(data.getVideoIp())) {
|
||||
throw new BizException(ErrorType.VIDEOIP_FAIL, "视频IP不能为空");
|
||||
}
|
||||
logger.info("查询视频信息,参数:{}", JSONObject.toJSON(data.getVideoIp()));
|
||||
TpEqpAcquisitionDTO result = tpEqpAcquisitionService.queryConfigByEqpNo(data.getVideoIp());
|
||||
logger.info("查询视频信息,返回信息:{}", JSONObject.toJSON(result));
|
||||
return BizResultVO.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/history/delete/{stream}")
|
||||
public BizResultVO<String> delete(@PathVariable("stream") String stream) throws InterruptedException {
|
||||
StartStreamProxy proxy = new StartStreamProxy();
|
||||
proxy.setApp("history");
|
||||
proxy.setStream(stream);
|
||||
String ss = zlMediaKitService.delStreamProxy(proxy);
|
||||
if (ss != null) {
|
||||
return BizResultVO.success(ss);
|
||||
}
|
||||
return BizResultVO.fail(ErrorType.ADD_STREAMPROXY_FAIL, null);
|
||||
}*/
|
||||
}
|
||||
|
@@ -85,4 +85,19 @@ public class SisDeviceManage extends BaseEntity {
|
||||
*/
|
||||
private Long floorId;
|
||||
|
||||
/**
|
||||
* 是否支持人脸比对
|
||||
*/
|
||||
private Boolean isComparison;
|
||||
|
||||
/**
|
||||
* 设备经度
|
||||
*/
|
||||
private Double lon;
|
||||
|
||||
/**
|
||||
* 设备维度
|
||||
*/
|
||||
private Double lat;
|
||||
|
||||
}
|
||||
|
@@ -71,7 +71,7 @@ public class SisPersonLibImg extends TenantEntity {
|
||||
/**
|
||||
* 远程库图像ID
|
||||
*/
|
||||
private Long remoteImgId;
|
||||
private Long remoteHWId;
|
||||
|
||||
/**
|
||||
* 入驻员工id
|
||||
@@ -83,4 +83,9 @@ public class SisPersonLibImg extends TenantEntity {
|
||||
*/
|
||||
private String imgMd5Value;
|
||||
|
||||
/**
|
||||
* 人员标签
|
||||
*/
|
||||
private Integer rosterType;
|
||||
|
||||
}
|
||||
|
@@ -83,6 +83,21 @@ public class SisDeviceManageBo extends BaseEntity {
|
||||
*/
|
||||
private Long groupId;
|
||||
|
||||
/**
|
||||
* 是否支持人脸比对
|
||||
*/
|
||||
private Boolean isComparison;
|
||||
|
||||
/**
|
||||
* 设备经度
|
||||
*/
|
||||
private Double lon;
|
||||
|
||||
/**
|
||||
* 设备维度
|
||||
*/
|
||||
private Double lat;
|
||||
|
||||
private String tenantId;
|
||||
|
||||
}
|
||||
|
@@ -79,7 +79,7 @@ public class SisPersonLibImgBo extends BaseEntity {
|
||||
/**
|
||||
* 远程库图像ID
|
||||
*/
|
||||
private Long remoteImgId;
|
||||
private Long remoteHWId;
|
||||
|
||||
/**
|
||||
* 入驻员工id
|
||||
@@ -91,4 +91,9 @@ public class SisPersonLibImgBo extends BaseEntity {
|
||||
*/
|
||||
private String imgMd5Value;
|
||||
|
||||
/**
|
||||
* 人员标签
|
||||
*/
|
||||
private Integer rosterType;
|
||||
|
||||
}
|
||||
|
@@ -9,13 +9,13 @@ public enum ControlTypeEnum {
|
||||
*/
|
||||
ACCESS_CONTROL(1),
|
||||
/**
|
||||
* 远程呼梯
|
||||
* 电梯外面面板权限
|
||||
*/
|
||||
REMOTE_CALL_ELEVATOR(2),
|
||||
ELEVATOR_OUT_CONTROL(2),
|
||||
/**
|
||||
* 梯控
|
||||
* 电梯里面的面板
|
||||
*/
|
||||
ELEVATOR_CONTROL(3);
|
||||
ELEVATOR_IN_CONTROL(3);
|
||||
|
||||
private final Integer code;
|
||||
|
||||
|
@@ -0,0 +1,32 @@
|
||||
package org.dromara.sis.domain.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author lsm
|
||||
* @apiNote RosterTypeEnum
|
||||
* @since 2025/8/7
|
||||
*/
|
||||
@Getter
|
||||
public enum RosterTypeEnum {
|
||||
/**
|
||||
* 红名单
|
||||
*/
|
||||
RED_LIST(1),
|
||||
|
||||
/**
|
||||
* 白名单
|
||||
*/
|
||||
WHITE_LIST(2),
|
||||
|
||||
/**
|
||||
* 黑名单名单
|
||||
*/
|
||||
BLACK_LIST(3);
|
||||
|
||||
private final Integer code;
|
||||
|
||||
RosterTypeEnum(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
@@ -62,5 +62,9 @@ public class SisAuthRecordVo implements Serializable {
|
||||
@ExcelProperty(value = "结束时间")
|
||||
private Date endDate;
|
||||
|
||||
/**
|
||||
* 人员标签
|
||||
*/
|
||||
private Integer rosterType;
|
||||
|
||||
}
|
||||
|
@@ -95,4 +95,20 @@ public class SisDeviceManageVo implements Serializable {
|
||||
private Long groupId;
|
||||
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 是否支持人脸比对
|
||||
*/
|
||||
private Boolean isComparison;
|
||||
|
||||
/**
|
||||
* 设备经度
|
||||
*/
|
||||
private Double lon;
|
||||
|
||||
/**
|
||||
* 设备维度
|
||||
*/
|
||||
private Double lat;
|
||||
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ public class SisPersonLibImgVo implements Serializable {
|
||||
* 远程库图像ID
|
||||
*/
|
||||
@ExcelProperty(value = "远程库图像ID")
|
||||
private Long remoteImgId;
|
||||
private Long remoteHWId;
|
||||
|
||||
/**
|
||||
* 人像库id
|
||||
@@ -102,4 +102,10 @@ public class SisPersonLibImgVo implements Serializable {
|
||||
*/
|
||||
@ExcelProperty(value = "图片MD5")
|
||||
private String imgMd5Value;
|
||||
|
||||
/**
|
||||
* 人员标签
|
||||
*/
|
||||
@ExcelProperty(value = "人员标签")
|
||||
private Integer rosterType;
|
||||
}
|
||||
|
@@ -85,15 +85,15 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过MD5,查询图片id
|
||||
* 通过MD5,查询华为盒子id
|
||||
*
|
||||
* @param imgMd5 图片MD5
|
||||
* @return Long
|
||||
*/
|
||||
@Override
|
||||
public Long queryImgIdByImgMd5(String imgMd5) {
|
||||
public Long queryHuaweiBoxIdByImgMd5(String imgMd5) {
|
||||
SisPersonLibImgVo vo = sisPersonLibImgService.queryByImgMd5(imgMd5);
|
||||
return vo != null ? vo.getId() : null;
|
||||
return vo != null ? vo.getRemoteHWId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,9 +107,17 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
public Long syncHuaweiBox(RemotePersonAuth person, byte[] imgByte) {
|
||||
Long pId;
|
||||
try {
|
||||
SisPersonLibImgVo vo = sisPersonLibImgService.queryByPersonId(person.getId());
|
||||
if (vo == null) {
|
||||
log.info("本地人像信息不存在::{}----{}", person.getName(), person.getId());
|
||||
return null;
|
||||
}
|
||||
|
||||
AddHWPersonReq req = new AddHWPersonReq();
|
||||
req.setIndex(CodePrefixConstants.PERSON_LIB_IMAGE_CODE_PREFIX + IdUtil.getSnowflakeNextIdStr());
|
||||
req.setName(person.getName());
|
||||
req.setCredentialType("5");
|
||||
req.setCredentialNumber(vo.getId().toString());
|
||||
req.setGender(person.getSex() == 1 ? "0" : person.getSex() == 2 ? "1" : "-1");
|
||||
|
||||
ArrayList<String> pictures = new ArrayList<>();
|
||||
@@ -118,6 +126,7 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
|
||||
pId = huaWeiBoxApi.addPerson(List.of(req));
|
||||
} catch (Exception e) {
|
||||
log.info("同步华为盒子失败:{}----{}", person.getName(), person.getId());
|
||||
return null;
|
||||
}
|
||||
return pId;
|
||||
@@ -152,7 +161,7 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
try {
|
||||
log.info("e8平台上传照片");
|
||||
String e8ImgUrl = e8PlatformApi.uploadFace(imgByte);
|
||||
Assert.notNull(e8ImgUrl, "图片上传E8平台失败");
|
||||
Assert.notNull(e8ImgUrl, "图片上传E8平台失败:" + person.getName() + "----" + person.getId());
|
||||
log.info("e8平台上传照片完成");
|
||||
count++; // 图片上传完成步进器+1
|
||||
|
||||
@@ -161,7 +170,7 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
req.setName(person.getName());
|
||||
req.setGender(person.getSex() != 1 ? 0 : 1);
|
||||
e8Id = e8PlatformApi.addCustomer(req).getId();
|
||||
Assert.notNull(e8Id, "e8同步新建人员失败");
|
||||
Assert.notNull(e8Id, "e8同步新建人员失败:" + person.getName() + "----" + person.getId());
|
||||
log.info("e8同步新建人员完成");
|
||||
count++; // 新增人员完成步进器+1
|
||||
|
||||
@@ -172,7 +181,7 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
voucherReq.setTxtData(e8ImgUrl);
|
||||
voucherReq.setCardType(34);
|
||||
Long voucherId = e8PlatformApi.issueVoucher(voucherReq);
|
||||
Assert.notNull(voucherId, "e8平台发行凭证失败");
|
||||
Assert.notNull(voucherId, "e8平台发行凭证失败:" + person.getName() + "----" + person.getId());
|
||||
log.info("e8平台发行凭证成功");
|
||||
count++; // 发行凭证完成步进器+1
|
||||
|
||||
@@ -199,11 +208,12 @@ public class RemoteSisAuthServiceImpl implements RemoteSisAuthService {
|
||||
|
||||
log.info("e8平台开始授权");
|
||||
Boolean flag = e8PlatformApi.addCustomerAuth(authReq);
|
||||
Assert.isTrue(flag, "E8平台授权失败!");
|
||||
Assert.isTrue(flag, "E8平台授权失败:" + person.getName() + "----" + person.getId());
|
||||
log.info("E8平台授权完成!");
|
||||
count++; // 授权完成步进器+1
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,53 @@
|
||||
package org.dromara.sis.runner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.sdk.smartDevices.utils.ElevatorControlTcpUtil;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lsm
|
||||
* @apiNote ElevatorTcpRunner
|
||||
* @since 2025/8/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ElevatorTcpRunner implements ApplicationRunner {
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
// 获取单例实例
|
||||
ElevatorControlTcpUtil elevatorHelper = ElevatorControlTcpUtil.getInstance();
|
||||
|
||||
// 设置连接监听器
|
||||
elevatorHelper.setConnectionListener(new ElevatorControlTcpUtil.ConnectionListener() {
|
||||
@Override
|
||||
public void onConnected() {
|
||||
log.info("梯控设备已连接");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
log.info("梯控设备已断开");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// 启动服务
|
||||
elevatorHelper.start(23);
|
||||
log.info("电梯控制TCP服务启动成功");
|
||||
|
||||
// 添加关闭钩子
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
log.info("应用关闭中,停止电梯控制TCP服务...");
|
||||
elevatorHelper.stop();
|
||||
}));
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("无法启动电梯控制TCP服务: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +1,12 @@
|
||||
package org.dromara.sis.sdk.hik.calback;
|
||||
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
import com.sun.jna.Pointer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.domain.SisDeviceManage;
|
||||
import org.dromara.sis.sdk.hik.HCNetSDK;
|
||||
import org.dromara.sis.service.ISisDeviceManageService;
|
||||
import org.dromara.sis.service.IZeroSensationPassageService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -11,6 +14,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -21,6 +25,7 @@ import static org.dromara.sis.sdk.hik.HCNetSDK.*;
|
||||
@RequiredArgsConstructor
|
||||
public class HikAlarmCallBack implements HCNetSDK.FMSGCallBack_V31 {
|
||||
|
||||
private final ISisDeviceManageService sisDeviceManageService;
|
||||
private final IZeroSensationPassageService zeroSensationPassageService;
|
||||
|
||||
@Override
|
||||
@@ -238,17 +243,15 @@ public class HikAlarmCallBack implements HCNetSDK.FMSGCallBack_V31 {
|
||||
}
|
||||
// 人脸比对结果上报
|
||||
case COMM_SNAP_MATCH_ALARM: {
|
||||
TimeInterval interval = new TimeInterval();
|
||||
HCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM strFaceSnapMatch = new HCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM();
|
||||
strFaceSnapMatch.write();
|
||||
Pointer pFaceSnapMatch = strFaceSnapMatch.getPointer();
|
||||
pFaceSnapMatch.write(0, pAlarmInfo.getByteArray(0, strFaceSnapMatch.size()), 0, strFaceSnapMatch.size());
|
||||
strFaceSnapMatch.read();
|
||||
//比对结果,0-保留,1-比对成功,2-比对失败
|
||||
String sFaceSnapMatchInfo = "比对结果:" + strFaceSnapMatch.byContrastStatus + ",相似度:" + strFaceSnapMatch.fSimilarity;
|
||||
|
||||
|
||||
handleFaceComparison(strFaceSnapMatch, pAlarmer, interval);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
|
||||
}
|
||||
@@ -262,9 +265,8 @@ public class HikAlarmCallBack implements HCNetSDK.FMSGCallBack_V31 {
|
||||
*
|
||||
* @param result 人脸数据
|
||||
* @param pAlarmer 设备数据
|
||||
* @return 返回是否处理成功
|
||||
*/
|
||||
private boolean handleFaceSnap(HCNetSDK.NET_VCA_FACESNAP_RESULT result, HCNetSDK.NET_DVR_ALARMER pAlarmer) {
|
||||
private void handleFaceSnap(HCNetSDK.NET_VCA_FACESNAP_RESULT result, HCNetSDK.NET_DVR_ALARMER pAlarmer) {
|
||||
// 读取人脸小图
|
||||
ByteBuffer buffers = result.pBuffer1.getByteBuffer(0, result.dwFacePicLen);
|
||||
byte[] smallImg = new byte[result.dwFacePicLen];
|
||||
@@ -279,8 +281,47 @@ public class HikAlarmCallBack implements HCNetSDK.FMSGCallBack_V31 {
|
||||
//设备ip
|
||||
String sAlarmInfo = new String(pAlarmer.sDeviceIP).trim();
|
||||
log.info("海康设备告警信息上传,设备={}, 人脸图片大小={}, 背景图片大小={}", sAlarmInfo, smallImg.length, bigImg.length);
|
||||
zeroSensationPassageService.pass(sAlarmInfo, smallImg, bigImg);
|
||||
return false;
|
||||
|
||||
SisDeviceManage device = sisDeviceManageService.queryByDeviceIp(sAlarmInfo);
|
||||
if (!device.getIsComparison()) {
|
||||
zeroSensationPassageService.pass(sAlarmInfo, smallImg, bigImg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理人脸比对上报
|
||||
*/
|
||||
private void handleFaceComparison(HCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM result, HCNetSDK.NET_DVR_ALARMER pAlarmer, TimeInterval interval) {
|
||||
// 读取比对结果比对结果,0-保留,1-比对成功,2-比对失败
|
||||
int compareResults = result.byContrastStatus;
|
||||
// 比对不成功,不处理
|
||||
if (compareResults != 1) {
|
||||
log.info("前置人脸比对失败,设备:{}", new String(pAlarmer.sDeviceIP).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取相似度
|
||||
float similarity = result.fSimilarity;
|
||||
// 相似度小于70,不处理
|
||||
if (similarity < 0.7) {
|
||||
log.info("前置人脸比对相似度小于70,设备:{}", new String(pAlarmer.sDeviceIP).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取名字
|
||||
String name = new String(result.struBlockListInfo.struBlockListInfo.struAttribute.byName, Charset.forName("GBK")).trim();
|
||||
// 人员id为空,不处理
|
||||
if (result.struBlockListInfo.pPID == null) return;
|
||||
// 读取人员id
|
||||
ByteBuffer buffers = result.struBlockListInfo.pPID.getByteBuffer(0, result.struBlockListInfo.dwPIDLen);
|
||||
byte[] pidByte = new byte[result.struBlockListInfo.dwPIDLen];
|
||||
buffers.rewind();
|
||||
buffers.get(pidByte);
|
||||
int pid = Integer.parseInt(new String(pidByte));
|
||||
|
||||
log.info("比对结果:{},相似度:{},人员:{},id:{}", compareResults, similarity, name, pid);
|
||||
log.info("处理前置人脸比对结果,耗时:{}", interval.interval());
|
||||
zeroSensationPassageService.handleEleOut(new String(pAlarmer.sDeviceIP).trim(), interval);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,341 @@
|
||||
package org.dromara.sis.sdk.smartDevices.utils;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @author lsm
|
||||
* @apiNote ElevatorControlTcpUtil
|
||||
* @since 2025/8/8
|
||||
*/
|
||||
@Slf4j
|
||||
public class ElevatorControlTcpUtil {
|
||||
private static final int FRAME_LENGTH = 10;
|
||||
private static final byte FIXED_ADDRESS = 0x00;
|
||||
private static final byte MANUAL_COMMAND = (byte) 0xD1;
|
||||
private static final byte AUTO_COMMAND = (byte) 0xD2;
|
||||
private static final byte MANUAL_RESPONSE = (byte) 0xC1;
|
||||
private static final byte AUTO_RESPONSE = (byte) 0xC2;
|
||||
|
||||
private static ElevatorControlTcpUtil instance;
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
private Socket clientSocket;
|
||||
private InputStream inputStream;
|
||||
private OutputStream outputStream;
|
||||
private final Object lock = new Object();
|
||||
private volatile boolean isRunning = false;
|
||||
|
||||
/**
|
||||
* -- SETTER --
|
||||
* 设置连接状态监听器
|
||||
*/
|
||||
// 连接状态监听器
|
||||
@Setter
|
||||
private ConnectionListener connectionListener;
|
||||
|
||||
// 线程池处理连接
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
// 私有构造函数
|
||||
private ElevatorControlTcpUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static synchronized ElevatorControlTcpUtil getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ElevatorControlTcpUtil();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动TCP服务
|
||||
*
|
||||
* @param port 监听端口
|
||||
*/
|
||||
public void start(int port) throws IOException {
|
||||
if (isRunning) {
|
||||
throw new IllegalStateException("服务已在运行中");
|
||||
}
|
||||
|
||||
serverSocket = new ServerSocket(port);
|
||||
isRunning = true;
|
||||
|
||||
// 启动连接监听线程
|
||||
executor.execute(this::acceptConnections);
|
||||
log.info("电梯控制TCP服务已启动,监听端口: {}", port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止服务
|
||||
*/
|
||||
public void stop() {
|
||||
isRunning = false;
|
||||
|
||||
// 关闭客户端连接
|
||||
closeClientResources();
|
||||
|
||||
// 关闭服务器
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
try {
|
||||
serverSocket.close();
|
||||
log.info("电梯控制TCP服务已停止");
|
||||
} catch (IOException e) {
|
||||
log.error("停止电梯控制TCP服务时出错: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭线程池
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受客户端连接
|
||||
*/
|
||||
private void acceptConnections() {
|
||||
while (isRunning && !serverSocket.isClosed()) {
|
||||
try {
|
||||
log.info("等待梯控设备连接...");
|
||||
Socket newClient = serverSocket.accept();
|
||||
|
||||
synchronized (lock) {
|
||||
// 关闭现有连接(如果有)
|
||||
closeClientResources();
|
||||
|
||||
// 接受新连接
|
||||
clientSocket = newClient;
|
||||
inputStream = clientSocket.getInputStream();
|
||||
outputStream = clientSocket.getOutputStream();
|
||||
|
||||
log.info("梯控设备已连接: {}", clientSocket.getRemoteSocketAddress());
|
||||
|
||||
// 通知连接状态变化
|
||||
if (connectionListener != null) {
|
||||
connectionListener.onConnected();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
if (isRunning) {
|
||||
log.error("接受连接时出错: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查客户端是否连接
|
||||
*/
|
||||
public boolean isClientConnected() {
|
||||
synchronized (lock) {
|
||||
return clientSocket != null &&
|
||||
clientSocket.isConnected() &&
|
||||
!clientSocket.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送手动选层命令
|
||||
*
|
||||
* @param floors 要开放的楼层列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean sendManualCommand(List<Integer> floors) {
|
||||
return sendCommand(MANUAL_COMMAND, MANUAL_RESPONSE, generateFloorData(floors));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送自动选层命令
|
||||
*
|
||||
* @param floors 要开放的楼层列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean sendAutoCommand(List<Integer> floors) {
|
||||
return sendCommand(AUTO_COMMAND, AUTO_RESPONSE, generateFloorData(floors));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送管理员权限命令(开放所有楼层)
|
||||
*/
|
||||
public boolean sendAdminCommand() {
|
||||
byte[] allFloors = new byte[]{
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
|
||||
};
|
||||
return sendCommand(MANUAL_COMMAND, MANUAL_RESPONSE, allFloors);
|
||||
}
|
||||
|
||||
private boolean sendCommand(byte command, byte expectedResponse, byte[] floorData) {
|
||||
if (!isClientConnected()) {
|
||||
log.info("发送命令失败:未连接梯控设备");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (floorData == null || floorData.length != 7) {
|
||||
log.info("发送命令失败:无效的楼层数据");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
try {
|
||||
// 检查连接是否仍然有效
|
||||
if (!isClientConnected()) {
|
||||
log.info("连接在发送前已断开");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] frame = buildFrame(command, floorData);
|
||||
outputStream.write(frame);
|
||||
outputStream.flush();
|
||||
|
||||
// 设置100ms超时
|
||||
clientSocket.setSoTimeout(100);
|
||||
|
||||
byte[] response = new byte[FRAME_LENGTH];
|
||||
int bytesRead = 0;
|
||||
while (bytesRead < FRAME_LENGTH) {
|
||||
int read = inputStream.read(response, bytesRead, FRAME_LENGTH - bytesRead);
|
||||
if (read == -1) {
|
||||
// 连接关闭
|
||||
handleConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
bytesRead += read;
|
||||
}
|
||||
|
||||
if (validateResponse(response, expectedResponse)) {
|
||||
return true;
|
||||
} else {
|
||||
log.info("响应验证失败,尝试重试");
|
||||
}
|
||||
} catch (java.net.SocketTimeoutException e) {
|
||||
log.info("命令响应超时,尝试重试");
|
||||
} catch (IOException e) {
|
||||
log.error("发送命令时出错: {}", e.getMessage());
|
||||
handleConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理连接关闭
|
||||
*/
|
||||
private void handleConnectionClosed() {
|
||||
log.info("检测到连接已关闭");
|
||||
closeClientResources();
|
||||
if (connectionListener != null) {
|
||||
connectionListener.onDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成楼层数据
|
||||
*/
|
||||
public byte[] generateFloorData(List<Integer> floors) {
|
||||
if (floors == null || floors.isEmpty()) {
|
||||
return new byte[]{0, 0, 0, 0, 0, 0, 0};
|
||||
}
|
||||
|
||||
byte[] data = new byte[7];
|
||||
for (int floor : floors) {
|
||||
if (floor < 1 || floor > 56) continue;
|
||||
|
||||
int index = 56 - floor;
|
||||
int bytePos = index / 8;
|
||||
int bitPos = 7 - (index % 8);
|
||||
|
||||
data[bytePos] |= (byte) (1 << bitPos);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private byte[] buildFrame(byte command, byte[] floorData) {
|
||||
byte[] frame = new byte[FRAME_LENGTH];
|
||||
frame[0] = command;
|
||||
frame[1] = FIXED_ADDRESS;
|
||||
System.arraycopy(floorData, 0, frame, 2, 7);
|
||||
|
||||
byte checksum = 0;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
checksum = (byte) (checksum + frame[i]);
|
||||
}
|
||||
frame[9] = checksum;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
private boolean validateResponse(byte[] response, byte expectedHeader) {
|
||||
if (response[0] != expectedHeader) {
|
||||
log.info("响应帧头错误,期望: {},实际:{}", expectedHeader, response[0]);
|
||||
return false;
|
||||
}
|
||||
if (response[1] != FIXED_ADDRESS) {
|
||||
log.info("响应地址错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte checksum = 0;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
checksum = (byte) (checksum + response[i]);
|
||||
}
|
||||
|
||||
if (response[9] != checksum) {
|
||||
log.info("校验和错误,期望: {},实际: {}", checksum, response[9]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭客户端资源
|
||||
*/
|
||||
private void closeClientResources() {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
if (inputStream != null) inputStream.close();
|
||||
if (outputStream != null) outputStream.close();
|
||||
if (clientSocket != null && !clientSocket.isClosed()) {
|
||||
clientSocket.close();
|
||||
}
|
||||
log.info("客户端连接已关闭");
|
||||
} catch (IOException e) {
|
||||
log.error("关闭连接时出错: {}", e.getMessage());
|
||||
} finally {
|
||||
clientSocket = null;
|
||||
inputStream = null;
|
||||
outputStream = null;
|
||||
|
||||
// 通知连接断开
|
||||
if (connectionListener != null) {
|
||||
connectionListener.onDisconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接状态监听接口
|
||||
*/
|
||||
public interface ConnectionListener {
|
||||
void onConnected();
|
||||
|
||||
void onDisconnected();
|
||||
}
|
||||
}
|
@@ -1,64 +1,46 @@
|
||||
package org.dromara.sis.sdk.zkmedia;
|
||||
|
||||
|
||||
import org.dromara.sis.sdk.zkmedia.model.AddStreamProxy;
|
||||
import org.dromara.sis.sdk.zkmedia.model.AddStreamProxyResp;
|
||||
import org.dromara.sis.sdk.zkmedia.model.R;
|
||||
import org.dromara.sis.sdk.zkmedia.model.StartStreamProxy;
|
||||
import org.dromara.sis.sdk.zkmedia.model.ThreadsLoadDelay;
|
||||
|
||||
import java.util.List;
|
||||
import org.dromara.sis.sdk.zkmedia.model.StreamPlay;
|
||||
|
||||
/**
|
||||
* 拉流服务
|
||||
* @author lxj
|
||||
*/
|
||||
public interface ZLMediaKitService {
|
||||
|
||||
/**
|
||||
* 获取各后台 epoll(或 select)线程负载以及延时
|
||||
* 创建视频流拉流代理
|
||||
*
|
||||
* @param addStreamProxy 拉流参数
|
||||
* @return 视频播放地址
|
||||
*/
|
||||
R<List<ThreadsLoadDelay>> getWorkThreadsLoad();
|
||||
AddStreamProxyResp addStreamProxy(AddStreamProxy addStreamProxy);
|
||||
|
||||
/**
|
||||
* 获取ZLMediaKit服务器配置信息
|
||||
* 通过 fork FFmpeg 进程的方式拉流代理,支持任意协议
|
||||
*/
|
||||
Object getServerConfig();
|
||||
AddStreamProxyResp addFfmpegStreamProxy(AddStreamProxy proxy);
|
||||
|
||||
|
||||
/**
|
||||
* 设置服务器配置
|
||||
* 创建视频流拉流代理
|
||||
*
|
||||
* @param streamPlay 拉流参数
|
||||
* @return 视频播放地址
|
||||
*/
|
||||
Object setServerConfig();
|
||||
AddStreamProxyResp addStreamProxy(StreamPlay streamPlay);
|
||||
|
||||
/**
|
||||
* 重启服务器,只有 Daemon 方式才能重启,否则是直接关闭!
|
||||
* 增加FFmpeg 拉流代理
|
||||
*
|
||||
* @param streamPlay 拉流参数
|
||||
* @return 视频流播放地址
|
||||
*/
|
||||
Object restartServer();
|
||||
|
||||
/**
|
||||
* 获取流列表,可选筛选参数
|
||||
*/
|
||||
Object getMediaList();
|
||||
|
||||
/**
|
||||
* 关闭流(目前所有类型的流都支持关闭)
|
||||
*/
|
||||
Object closeStreams();
|
||||
|
||||
/**
|
||||
* 获取所有 TcpSession 列表(获取所有 tcp 客户端相关信息)
|
||||
*/
|
||||
Object getAllSession();
|
||||
|
||||
/**
|
||||
* 断开 tcp 连接,比如说可以断开 rtsp、rtmp 播放器等
|
||||
*/
|
||||
Object kickSession();
|
||||
|
||||
/**
|
||||
* 断开 tcp 连接,比如说可以断开 rtsp、rtmp 播放器等
|
||||
*/
|
||||
Object kickSessions();
|
||||
|
||||
/**
|
||||
* 动态添加 rtsp/rtmp/hls/http-ts/http-flv 拉流代理(只支持 H264/H265/aac/G711/opus 负载)
|
||||
*/
|
||||
AddStreamProxyResp addStreamProxy(StartStreamProxy startStreamProxy);
|
||||
AddStreamProxyResp addFfmpegStreamProxy(StreamPlay streamPlay);
|
||||
|
||||
/**
|
||||
* (流注册成功后,也可以使用close_streams接口替代)
|
||||
@@ -66,94 +48,14 @@ public interface ZLMediaKitService {
|
||||
*/
|
||||
String delStreamProxy(StartStreamProxy startStreamProxy);
|
||||
|
||||
/**
|
||||
* 通过 fork FFmpeg 进程的方式拉流代理,支持任意协议
|
||||
*/
|
||||
AddStreamProxyResp addFFmpegSource(String src_url);
|
||||
|
||||
/**
|
||||
* 流注册成功后,也可以使用close_streams接口替代
|
||||
* 删除ffmpeg 拉流任务
|
||||
*
|
||||
* @param key 流id
|
||||
* @return
|
||||
*/
|
||||
Boolean delFFmpegSource(String key);
|
||||
Boolean delFfmpegSource(String key);
|
||||
|
||||
/**
|
||||
* 获取 rtp 代理时的某路 ssrc rtp 信息
|
||||
*/
|
||||
Object getRtpInfo();
|
||||
|
||||
/**
|
||||
* 搜索文件系统,获取流对应的录像文件列表或日期文件夹列表
|
||||
*/
|
||||
Object getMp4RecordFile();
|
||||
|
||||
/**
|
||||
* 开始录制 hls 或 MP4
|
||||
*/
|
||||
Object startRecord();
|
||||
|
||||
/**
|
||||
* 停止录制流
|
||||
*/
|
||||
Object stopRecord();
|
||||
|
||||
/**
|
||||
* 获取流录制状态
|
||||
*/
|
||||
Object isRecording();
|
||||
|
||||
/**
|
||||
* 获取截图或生成实时截图并返回
|
||||
*/
|
||||
Object getSnap();
|
||||
|
||||
/**
|
||||
* 创建 GB28181 RTP 接收端口,如果该端口接收数据超时,则会自动被回收(不用调用 closeRtpServer 接口)
|
||||
*/
|
||||
Object openRtpServer();
|
||||
|
||||
/**
|
||||
* 关闭 GB28181 RTP 接收端口
|
||||
*/
|
||||
Object closeRtpServer();
|
||||
|
||||
/**
|
||||
* 获取 openRtpServer 接口创建的所有 RTP 服务器
|
||||
*/
|
||||
Object listRtpServer();
|
||||
|
||||
/**
|
||||
* 作为 GB28181 客户端,启动 ps-rtp 推流,支持 rtp/udp 方式;该接口支持 rtsp/rtmp 等协议转 ps-rtp 推流。第一次推流失败会直接返回错误,成功一次后,后续失败也将无限重试。
|
||||
*/
|
||||
Object startSendRtp();
|
||||
|
||||
/**
|
||||
* 停止 GB28181 ps-rtp 推流
|
||||
*/
|
||||
Object stopSendRtp();
|
||||
|
||||
/**
|
||||
* 获取主要对象个数统计,主要用于分析内存性能
|
||||
*/
|
||||
Object getStatistic();
|
||||
|
||||
/**
|
||||
* 添加 rtsp/rtmp 主动推流(把本服务器的直播流推送到其他服务器去)
|
||||
*/
|
||||
Object addStreamPusherProxy();
|
||||
|
||||
/**
|
||||
* 关闭推流,可以使用close_streams接口关闭源直播流也可以停止推流)
|
||||
*/
|
||||
Object delStreamPusherProxy();
|
||||
|
||||
/**
|
||||
* 获取版本信息,如分支,commit id, 编译时间
|
||||
*/
|
||||
Object getVersion();
|
||||
|
||||
/**
|
||||
* 获取某个流观看者列表
|
||||
*/
|
||||
Object getMediaPlayerList();
|
||||
|
||||
}
|
||||
|
@@ -1,18 +1,18 @@
|
||||
package org.dromara.sis.sdk.zkmedia;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sis.api.enums.FactoryNoEnum;
|
||||
import org.dromara.sis.config.ZLMediaKitConfig;
|
||||
import org.dromara.sis.sdk.zkmedia.model.AddStreamProxyResp;
|
||||
import org.dromara.sis.sdk.zkmedia.model.R;
|
||||
import org.dromara.sis.sdk.zkmedia.model.StartStreamProxy;
|
||||
import org.dromara.sis.sdk.zkmedia.model.ThreadsLoadDelay;
|
||||
import org.dromara.sis.domain.SisDeviceChannel;
|
||||
import org.dromara.sis.sdk.zkmedia.model.*;
|
||||
import org.dromara.sis.service.ISisDeviceChannelService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -26,13 +26,41 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService {
|
||||
|
||||
@Resource
|
||||
private ZLMediaKitConfig zlmConfig;
|
||||
@Resource
|
||||
private ISisDeviceChannelService deviceChannelService;
|
||||
|
||||
// 海康实时流取流模板
|
||||
private static final String HIK_REALTIME_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/Streaming/Channels/%s";
|
||||
// 大华实时流取流模板
|
||||
private static final String DAHUA_REALTIME_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/cam/realmonitor?channel=%s&subtype=0";
|
||||
// 海康历史流取流模板
|
||||
private static final String HIK_HISTORY_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/Streaming/tracks/%s?starttime=%s&endtime=%s";
|
||||
// 大华历史流取流模板
|
||||
private static final String DAHUA_HISTORY_RTSP_TEMPLATE = "rtsp://%s:%s@%s:%s/cam/playback?channel=%s&subtype=0&starttime=%s&endtime=%s";
|
||||
//流媒体请求模板
|
||||
private static final String STREAM_REQUEST_TEMLATE = "http://%s:%d/index/api/";
|
||||
// RTMP 视频流播放模板
|
||||
private static final String RTMP_PLAY_URL = "rtmp://%s:%d/%s/%s";
|
||||
// RTSP 视频流播放模板
|
||||
private static final String RTSP_PLAY_URL = "rtsp://%s:%d/%s/%s";
|
||||
// HTTP-FLV 视频流播放模板
|
||||
private static final String HTTP_FLV_PLAY_URL = "http://%s:%d/%s/%s.live.flv";
|
||||
// WS-FLV 视频流播放模板
|
||||
private static final String WS_FLV_PLAY_URL = "ws://%s:%d/%s/%s.live.flv";
|
||||
// HLS 视频流播放模板
|
||||
private static final String HLS_FLV_PLAY_URL = "http://%s:%d/%s/%s/hls.m3u8";
|
||||
// MP4 视频流播放模板
|
||||
private static final String MP4_FLV_PLAY_URL = "http://%s:%d/%s/%s.live.mp4";
|
||||
// 推流地址
|
||||
|
||||
|
||||
private static volatile String ZLM_REQUEST_PREFIX = null;
|
||||
|
||||
public String getRequestUrl(String uri) {
|
||||
if (ZLM_REQUEST_PREFIX == null) {
|
||||
synchronized (ZLMediaKitServiceImpl.class) {
|
||||
if (ZLM_REQUEST_PREFIX == null) {
|
||||
ZLM_REQUEST_PREFIX = String.format("http://%s:%d/index/api/", zlmConfig.getIp(), zlmConfig.getHttpPort());
|
||||
ZLM_REQUEST_PREFIX = String.format(STREAM_REQUEST_TEMLATE, zlmConfig.getIp(), zlmConfig.getHttpPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,84 +73,96 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<List<ThreadsLoadDelay>> getWorkThreadsLoad() {
|
||||
String url = getRequestUrl("getThreadsLoad");
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
return HttpClientUtil.get(url, commonParams, new TypeReference<R<List<ThreadsLoadDelay>>>() {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getServerConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setServerConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object restartServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMediaList() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object closeStreams() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAllSession() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object kickSession() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object kickSessions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拉流地址
|
||||
* 设置返回参数的视频播放地址
|
||||
*/
|
||||
private AddStreamProxyResp setPlayerUrl(String app, String streamId, AddStreamProxyResp resp) {
|
||||
if (resp == null) {
|
||||
resp = new AddStreamProxyResp();
|
||||
}
|
||||
// RTMP 播放地址
|
||||
resp.setRtmp(String.format("rtmp://%s:%d/%s/%s", zlmConfig.getIp(), zlmConfig.getRtmpPort(), app, streamId));
|
||||
resp.setRtmp(String.format(RTMP_PLAY_URL, zlmConfig.getIp(), zlmConfig.getRtmpPort(), app, streamId));
|
||||
// RTSP 播放地址
|
||||
resp.setRtsp(String.format("rtsp://%s:%d/%s/%s", zlmConfig.getIp(), zlmConfig.getRtspPort(), app, streamId));
|
||||
resp.setRtsp(String.format(RTSP_PLAY_URL, zlmConfig.getIp(), zlmConfig.getRtspPort(), app, streamId));
|
||||
// HTTP-FLV 播放地址
|
||||
resp.setFlv(String.format("http://%s:%d/%s/%s.live.flv", zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
resp.setWsFlv(String.format("ws://%s:%d/%s/%s.live.flv", zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
resp.setFlv(String.format(HTTP_FLV_PLAY_URL, zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
resp.setWsFlv(String.format(WS_FLV_PLAY_URL, zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
// HLS 播放地址
|
||||
resp.setHls(String.format("http://%s:%d/%s/%s/hls.m3u8", zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
resp.setHls(String.format(HLS_FLV_PLAY_URL, zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
// MP4 播放地址
|
||||
resp.setMp4(String.format("http://%s:%d/%s/%s.live.mp4", zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
resp.setMp4(String.format(MP4_FLV_PLAY_URL, zlmConfig.getIp(), zlmConfig.getHttpPort(), app, streamId));
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成视频流地址
|
||||
*
|
||||
* @param factoryNo 厂商
|
||||
* @param account 账号
|
||||
* @param pwd 密码
|
||||
* @param ip ip
|
||||
* @param port 端口
|
||||
* @param channel 通道
|
||||
* @return 返回视频流播放地址
|
||||
*/
|
||||
private String getRealTimeStreamUrl(String factoryNo, String account, String pwd, String ip, Integer port, String channel) {
|
||||
if (FactoryNoEnum.HIK.getCode().equals(factoryNo)) {
|
||||
return String.format(HIK_REALTIME_RTSP_TEMPLATE, account, pwd, ip, port, channel);
|
||||
} else if (FactoryNoEnum.DAHUA.getCode().equals(factoryNo)) {
|
||||
return String.format(DAHUA_REALTIME_RTSP_TEMPLATE, account, pwd, ip, port, channel);
|
||||
} else {
|
||||
throw new RuntimeException("未知的设备类型!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addStreamProxy(StartStreamProxy startStreamProxy) {
|
||||
private String getPlayBackStreamUrl(String factoryNo, String account, String pwd, String ip, Integer port, String channel, String startTime, String endTime) {
|
||||
if (FactoryNoEnum.HIK.getCode().equals(factoryNo)) {
|
||||
String pattern = "yyyyMMdd'T'HHmmss'Z'";
|
||||
String st = MediaServerUtils.formatTimestamp(startTime, pattern);
|
||||
String et = MediaServerUtils.formatTimestamp(endTime, pattern);
|
||||
return String.format(HIK_HISTORY_RTSP_TEMPLATE, account, pwd, ip, port, channel, st, et);
|
||||
} else if (FactoryNoEnum.DAHUA.getCode().equals(factoryNo)) {
|
||||
String pattern = "yyyy_MM_dd_HH_mm_ss";
|
||||
String st = MediaServerUtils.formatTimestamp(startTime, pattern);
|
||||
String et = MediaServerUtils.formatTimestamp(endTime, pattern);
|
||||
return String.format(DAHUA_HISTORY_RTSP_TEMPLATE, account, pwd, ip, port, channel, st, et);
|
||||
} else {
|
||||
throw new RuntimeException("未知的设备类型!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拉流地址
|
||||
* streamType=1 是实时流, streamType=2 是历史流
|
||||
* factoryNo = 1 是海康视频流, factoryNo= 2 是大华视频流
|
||||
*
|
||||
* @param factoryNo 厂商编码
|
||||
* @param streamType 流类型
|
||||
* @param account 账号
|
||||
* @param pwd 密码
|
||||
* @param ip ip
|
||||
* @param port 端口
|
||||
* @param channel 通道
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return
|
||||
*/
|
||||
private String getPullStreamUrl(String factoryNo, Integer streamType, String account, String pwd, String ip, Integer port, String channel, String startTime, String endTime) {
|
||||
if (streamType == 1) {
|
||||
return getRealTimeStreamUrl(factoryNo, account, pwd, ip, port, channel);
|
||||
} else {
|
||||
return getPlayBackStreamUrl(factoryNo, account, pwd, ip, port, channel, startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private AddStreamProxyResp addStreamProxy(String app, String stream, String url) {
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
commonParams.put("vhost", zlmConfig.getVhost());
|
||||
commonParams.put("app", startStreamProxy.getApp());
|
||||
commonParams.put("stream", startStreamProxy.getStream());
|
||||
commonParams.put("url", startStreamProxy.getUrl());
|
||||
commonParams.put("rtp_type", startStreamProxy.getRtpType());
|
||||
commonParams.put("app", app);
|
||||
commonParams.put("stream", stream);
|
||||
commonParams.put("url", url);
|
||||
commonParams.put("rtp_type", 1);
|
||||
R<AddStreamProxyResp> result = HttpClientUtil.get(getRequestUrl("addStreamProxy"), commonParams, AddStreamProxyResp.class);
|
||||
if (result != null) {
|
||||
if (result.getCode() == 0) {
|
||||
@@ -132,11 +172,132 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService {
|
||||
if (result.getCode() == -1) {
|
||||
log.info("拉流任务已存在,返回播放地址。");
|
||||
}
|
||||
return setPlayerUrl(startStreamProxy.getApp(), startStreamProxy.getStream(), result.getData());
|
||||
return setPlayerUrl(app, stream, result.getData());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备通道信息
|
||||
*
|
||||
* @param deviceIp 设备ip
|
||||
* @param channelId 设备通道id
|
||||
* @return 返回通道信息
|
||||
*/
|
||||
private SisDeviceChannel getDeviceChannel(String deviceIp, String channelId) {
|
||||
SisDeviceChannel channel = deviceChannelService.queryChannels(deviceIp, channelId);
|
||||
if (channel == null) {
|
||||
throw new RuntimeException("设备通道不存在!");
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addStreamProxy(AddStreamProxy proxy) {
|
||||
// 实时流
|
||||
String app = proxy.getStreamType() == 1 ? "realtime" : "history";
|
||||
String url = getPullStreamUrl(proxy.getFactoryNo(), proxy.getStreamType(), proxy.getAccount(), proxy.getPwd(),
|
||||
proxy.getVideoIp(), proxy.getVideoPort(), proxy.getChannelId(), proxy.getStartTime(), proxy.getEndTime());
|
||||
String stream = IdUtil.fastSimpleUUID();
|
||||
return addStreamProxy(app, stream, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addStreamProxy(StreamPlay streamPlay) {
|
||||
// 查询设备通道信息
|
||||
SisDeviceChannel channel = getDeviceChannel(streamPlay.getDeviceIp(), streamPlay.getChannelNo());
|
||||
// 构建拉流地址
|
||||
String app = null;
|
||||
String url = null;
|
||||
// 判断是走录像机拉流还是设备直接拉流
|
||||
if (streamPlay.getStreamType() == 1) {
|
||||
app = "realtime";
|
||||
// 当前如果配置了录像机会默认走录像机拉流
|
||||
if (StrUtil.isNotEmpty(channel.getNvrIp())) {
|
||||
url = getRealTimeStreamUrl(channel.getNvrFactoryNo(), channel.getNvrAccount(), channel.getNvrPwd(), channel.getNvrIp(), channel.getNvrPort(), channel.getNvrChannelNo());
|
||||
} else {
|
||||
url = getRealTimeStreamUrl(channel.getFactoryNo(), channel.getDeviceAccount(), channel.getDevicePwd(), channel.getDeviceIp(), channel.getDevicePort(), channel.getChannelNo());
|
||||
}
|
||||
} else {
|
||||
app = "history";
|
||||
// 校验通道是否配置了nvr和cvr
|
||||
if (StrUtil.isNotEmpty(channel.getNvrIp())) {
|
||||
url = getPlayBackStreamUrl(channel.getNvrFactoryNo(), channel.getNvrAccount(), channel.getNvrPwd(), channel.getNvrIp(), channel.getNvrPort(), channel.getNvrChannelNo()
|
||||
, streamPlay.getStartTime(), streamPlay.getEndTime());
|
||||
} else {
|
||||
throw new RuntimeException("设备机未配置存储设备,无法拉取视频流。");
|
||||
}
|
||||
}
|
||||
String stream = IdUtil.fastSimpleUUID();
|
||||
return addStreamProxy(app, stream, url);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AddStreamProxyResp getAddStreamProxyResp(String url) {
|
||||
// 生成拉流任务key
|
||||
String taskKey = IdUtil.fastSimpleUUID();
|
||||
// String targetUrl = "rtmp://127.0.0.1/live/" + taskKey;
|
||||
// ffmpeg 推流地址
|
||||
String targetUrl = zlmConfig.getPushStreamUrl() + taskKey;
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
commonParams.put("src_url", url);
|
||||
commonParams.put("dst_url", targetUrl);
|
||||
commonParams.put("timeout_ms", 10000);
|
||||
commonParams.put("enable_hls", false);
|
||||
commonParams.put("enable_mp4", false);
|
||||
R<AddStreamProxyResp> result = HttpClientUtil.get(getRequestUrl("addFFmpegSource"), commonParams, AddStreamProxyResp.class);
|
||||
if (result != null) {
|
||||
if (result.getCode() == 0) {
|
||||
log.info("创建FFMPEG拉流任务成功.");
|
||||
}
|
||||
// 此处代表拉流任务已存在
|
||||
if (result.getCode() == -1) {
|
||||
log.info("FFMPEG拉流任务已存在,返回播放地址。");
|
||||
}
|
||||
if (result.getData() != null) {
|
||||
|
||||
return setPlayerUrl("live", taskKey, result.getData());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addFfmpegStreamProxy(AddStreamProxy proxy) {
|
||||
String url = "";
|
||||
if (proxy.getStreamType() == 1) {
|
||||
url = getRealTimeStreamUrl(proxy.getFactoryNo(), proxy.getAccount(), proxy.getPwd(), proxy.getVideoIp(), proxy.getVideoPort(), proxy.getChannelId());
|
||||
} else {
|
||||
url = getPlayBackStreamUrl(proxy.getFactoryNo(), proxy.getAccount(), proxy.getPwd(), proxy.getVideoIp(), proxy.getVideoPort(), proxy.getChannelId(), proxy.getStartTime(), proxy.getEndTime());
|
||||
}
|
||||
return getAddStreamProxyResp(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addFfmpegStreamProxy(StreamPlay streamPlay) {
|
||||
// 查询设备通道信息
|
||||
SisDeviceChannel channel = getDeviceChannel(streamPlay.getDeviceIp(), streamPlay.getChannelNo());
|
||||
String url = null;
|
||||
if (streamPlay.getStreamType() == 1) {
|
||||
// 当前如果配置了录像机会默认走录像机拉流
|
||||
if (StrUtil.isNotEmpty(channel.getNvrIp())) {
|
||||
url = getRealTimeStreamUrl(channel.getNvrFactoryNo(), channel.getNvrAccount(), channel.getNvrPwd(), channel.getNvrIp(), channel.getNvrPort(), channel.getNvrChannelNo());
|
||||
} else {
|
||||
url = getRealTimeStreamUrl(channel.getFactoryNo(), channel.getDeviceAccount(), channel.getDevicePwd(), channel.getDeviceIp(), channel.getDevicePort(), channel.getChannelNo());
|
||||
}
|
||||
} else {
|
||||
// 校验通道是否配置了nvr和cvr
|
||||
if (StrUtil.isNotEmpty(channel.getNvrIp())) {
|
||||
url = getPlayBackStreamUrl(channel.getNvrFactoryNo(), channel.getNvrAccount(), channel.getNvrPwd(), channel.getNvrIp(), channel.getNvrPort(), channel.getNvrChannelNo()
|
||||
, streamPlay.getStartTime(), streamPlay.getEndTime());
|
||||
} else {
|
||||
throw new RuntimeException("设备机未配置存储设备,无法拉取视频流。");
|
||||
}
|
||||
}
|
||||
return getAddStreamProxyResp(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String delStreamProxy(StartStreamProxy startStreamProxy) {
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
@@ -161,132 +322,12 @@ public class ZLMediaKitServiceImpl implements ZLMediaKitService {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddStreamProxyResp addFFmpegSource(String src_url) {
|
||||
|
||||
// 生成拉流任务key
|
||||
String taskKey = IdUtil.fastSimpleUUID();
|
||||
String targetUrl = "rtmp://127.0.0.1/live/" + taskKey;
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
commonParams.put("src_url", src_url);
|
||||
commonParams.put("dst_url", targetUrl);
|
||||
commonParams.put("timeout_ms", 10000);
|
||||
commonParams.put("enable_hls", false);
|
||||
commonParams.put("enable_mp4", false);
|
||||
R<AddStreamProxyResp> result = HttpClientUtil.get(getRequestUrl("addFFmpegSource"), commonParams, AddStreamProxyResp.class);
|
||||
if (result != null) {
|
||||
if (result.getCode() == 0) {
|
||||
log.info("创建FFMPEG拉流任务成功.");
|
||||
}
|
||||
// 此处代表拉流任务已存在
|
||||
if (result.getCode() == -1) {
|
||||
log.info("FFMPEG拉流任务已存在,返回播放地址。");
|
||||
}
|
||||
|
||||
// RTMP 播放地址
|
||||
result.getData().setRtmp(String.format("rtmp://%s:%d/live/%s", zlmConfig.getIp(), zlmConfig.getRtmpPort(), taskKey));
|
||||
// RTSP 播放地址
|
||||
result.getData().setRtsp(String.format("rtsp://%s:%d/live/%s", zlmConfig.getIp(), zlmConfig.getRtspPort(), taskKey));
|
||||
// HTTP-FLV 播放地址
|
||||
result.getData().setFlv(String.format("http://%s:%d/live/%s.live.flv", zlmConfig.getIp(), zlmConfig.getHttpPort(), taskKey));
|
||||
result.getData().setWsFlv(String.format("ws://%s:%d/live/%s.live.flv", zlmConfig.getIp(), zlmConfig.getHttpPort(), taskKey));
|
||||
// HLS 播放地址
|
||||
result.getData().setHls(String.format("http://%s:%d/live/%s/hls.m3u8", zlmConfig.getIp(), zlmConfig.getHttpPort(), taskKey));
|
||||
// MP4 播放地址
|
||||
result.getData().setMp4(String.format("http://%s:%d/live/%s.live.mp4", zlmConfig.getIp(), zlmConfig.getHttpPort(), taskKey));
|
||||
return result.getData();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean delFFmpegSource(String key) {
|
||||
public Boolean delFfmpegSource(String key) {
|
||||
Map<String, Object> commonParams = getCommonParams();
|
||||
R<String> result = HttpClientUtil.get(getRequestUrl("addFFmpegSource?key=" + key), commonParams, String.class);
|
||||
if (result != null && result.getCode() == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRtpInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMp4RecordFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object startRecord() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object stopRecord() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object isRecording() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSnap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object openRtpServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object closeRtpServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object listRtpServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object startSendRtp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object stopSendRtp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getStatistic() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object addStreamPusherProxy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object delStreamPusherProxy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMediaPlayerList() {
|
||||
return null;
|
||||
return result != null && result.getCode() == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,31 +5,68 @@ import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 增加拉流代理
|
||||
*
|
||||
* @author lxj
|
||||
*/
|
||||
@Data
|
||||
public class AddStreamProxy {
|
||||
|
||||
/**
|
||||
* 设备ip
|
||||
*/
|
||||
@NotBlank
|
||||
private String videoIp;
|
||||
|
||||
/**
|
||||
* 设备端口
|
||||
*/
|
||||
private Integer videoPort;
|
||||
|
||||
/**
|
||||
* 厂商
|
||||
*/
|
||||
@NotBlank
|
||||
private String factoryNo;
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
@NotBlank
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 设备密码
|
||||
*/
|
||||
@NotBlank
|
||||
private String pwd;
|
||||
|
||||
/**
|
||||
* 通道id
|
||||
*/
|
||||
@NotNull
|
||||
private String channelId;
|
||||
|
||||
|
||||
private String startTime;
|
||||
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 流应用名称
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 流类型1:实时流,2:历史流
|
||||
*/
|
||||
private Integer streamType = 1;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package org.dromara.sis.sdk.zkmedia.model;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 实时视频流播放请求参数
|
||||
*
|
||||
* @author lxj
|
||||
*/
|
||||
@Data
|
||||
public class StreamPlay {
|
||||
|
||||
/**
|
||||
* 设备编号
|
||||
*/
|
||||
@NotEmpty
|
||||
private String deviceIp;
|
||||
|
||||
/**
|
||||
* 设备通道号
|
||||
*/
|
||||
@NotEmpty
|
||||
private String channelNo;
|
||||
|
||||
/**
|
||||
* 流类型1:实时流,2:历史流
|
||||
*/
|
||||
private Integer streamType = 1;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
package org.dromara.sis.service;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import org.dromara.common.core.domain.TreeNode;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
@@ -129,5 +130,12 @@ public interface ISisDeviceChannelService {
|
||||
*/
|
||||
Boolean updateDeviceChannelState(String deviceIp, Integer onLineState);
|
||||
|
||||
|
||||
/**
|
||||
* 通过设备ip和通道编码查询设备通道信息
|
||||
*
|
||||
* @param deviceIp 设备ip
|
||||
* @param channelNo 设备通道号
|
||||
* @return 返回通道信息
|
||||
*/
|
||||
SisDeviceChannel queryChannels(@NotEmpty String deviceIp, @NotEmpty String channelNo);
|
||||
}
|
||||
|
@@ -70,21 +70,18 @@ public interface ISisDeviceManageService {
|
||||
|
||||
/**
|
||||
* 通过设备ip查询设备信息
|
||||
*
|
||||
* @param deviceIp 设备编码
|
||||
* @param deviceIp 设备ip
|
||||
* @return 设备信息
|
||||
*/
|
||||
SisDeviceManageVo queryVoByDeviceIp(String deviceIp);
|
||||
SisDeviceManage queryByDeviceIp(String deviceIp);
|
||||
|
||||
SisDeviceManage queryByDeviceIp(String deviceId);
|
||||
/**
|
||||
* 查询设备数
|
||||
*
|
||||
* @return
|
||||
* @return tree
|
||||
*/
|
||||
List<TreeNode<Long>> tree();
|
||||
|
||||
|
||||
/**
|
||||
* 通过设备id列表查询设备信息
|
||||
* @param deviceIds 设备id列表
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package org.dromara.sis.service;
|
||||
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
|
||||
public interface IZeroSensationPassageService {
|
||||
|
||||
/**
|
||||
@@ -11,4 +13,11 @@ public interface IZeroSensationPassageService {
|
||||
*/
|
||||
void pass(String deviceIp, byte[] smallImp, byte[] bigImg);
|
||||
|
||||
/**
|
||||
* 电梯外部按键触发
|
||||
*
|
||||
* @param deviceIp 设备ip
|
||||
*/
|
||||
void handleEleOut(String deviceIp, TimeInterval interval);
|
||||
|
||||
}
|
||||
|
@@ -237,6 +237,7 @@ public class SisAccessControlServiceImpl implements ISisAccessControlService {
|
||||
node.setLevel(6);
|
||||
node.setCode(item.getId());
|
||||
node.setParentCode(item.getAcId());
|
||||
node.setData(item);
|
||||
node.setLabel(item.getDeviceName());
|
||||
return node;
|
||||
}).toList();
|
||||
|
@@ -18,6 +18,7 @@ import org.dromara.sis.api.domain.RemotePersonAuth;
|
||||
import org.dromara.sis.domain.bo.SisAccessControlBo;
|
||||
import org.dromara.sis.domain.bo.SisElevatorInfoBo;
|
||||
import org.dromara.sis.domain.bo.SisPersonLibImgBo;
|
||||
import org.dromara.sis.domain.enums.RosterTypeEnum;
|
||||
import org.dromara.sis.domain.vo.SisAccessControlVo;
|
||||
import org.dromara.sis.domain.vo.SisElevatorInfoVo;
|
||||
import org.dromara.sis.domain.vo.SisPersonLibImgVo;
|
||||
@@ -115,30 +116,34 @@ public class SisAuthRecordServiceImpl implements ISisAuthRecordService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByPerson(RemotePersonAuth bo) {
|
||||
SisAuthRecord add = new SisAuthRecord();
|
||||
add.setTargetType(1L);
|
||||
add.setTargetId(bo.getId());
|
||||
add.setGroupId(bo.getAuthGroupId());
|
||||
add.setBegDate(bo.getAuthBegDate());
|
||||
add.setEndDate(bo.getAuthEndDate());
|
||||
add.setTenantId("000000");
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
Assert.isTrue(flag, "新增授权记录失败");
|
||||
// 黑名单不加授权记录
|
||||
if (!Objects.equals(bo.getRosterType(), RosterTypeEnum.BLACK_LIST.getCode())) {
|
||||
SisAuthRecord add = new SisAuthRecord();
|
||||
add.setTargetType(1L);
|
||||
add.setTargetId(bo.getId());
|
||||
add.setGroupId(bo.getAuthGroupId());
|
||||
add.setBegDate(bo.getAuthBegDate());
|
||||
add.setEndDate(bo.getAuthEndDate());
|
||||
add.setTenantId("000000");
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
Assert.isTrue(flag, "新增授权记录失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 记录图片md5值
|
||||
byte[] imgByte = remoteFileService.downloadToByteArray(Long.parseLong(bo.getOssId()));
|
||||
String md5 = calculateMD5(imgByte);
|
||||
// 写入安防人像信息
|
||||
this.syncPersonImg(bo, md5);
|
||||
Boolean sync = this.syncPersonImg(bo, md5);
|
||||
Assert.isTrue(sync, "写入安防人像信息失败");
|
||||
} catch (Exception e) {
|
||||
log.info("下载图片失败");
|
||||
log.info(e.getMessage());
|
||||
}
|
||||
|
||||
return flag;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void syncPersonImg(RemotePersonAuth bo, String md5) {
|
||||
private Boolean syncPersonImg(RemotePersonAuth bo, String md5) {
|
||||
log.info("开始写入安防人像信息");
|
||||
SisPersonLibImgBo personLibImg = new SisPersonLibImgBo();
|
||||
personLibImg.setImgOssId(Long.parseLong(bo.getOssId()));
|
||||
@@ -149,8 +154,10 @@ public class SisAuthRecordServiceImpl implements ISisAuthRecordService {
|
||||
personLibImg.setCertificateNo(bo.getIdCardNumber());
|
||||
personLibImg.setResidentPersonId(bo.getId());
|
||||
personLibImg.setImgMd5Value(md5);
|
||||
sisPersonLibImgService.insertByBo(personLibImg);
|
||||
log.info("写入安防人像信息完成");
|
||||
personLibImg.setRosterType(bo.getRosterType());
|
||||
Boolean flag = sisPersonLibImgService.insertByBo(personLibImg);
|
||||
if (flag) log.info("写入安防人像信息完成");
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,28 +359,26 @@ public class SisAuthRecordServiceImpl implements ISisAuthRecordService {
|
||||
public Boolean deleteByPersonIds(Collection<Long> ids, Collection<Long> e8Ids) {
|
||||
LambdaQueryWrapper<SisAuthRecord> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.in(SisAuthRecord::getTargetId, ids);
|
||||
boolean flag = baseMapper.delete(lqw) > 0;
|
||||
Assert.isTrue(flag, "删除授权记录失败");
|
||||
baseMapper.delete(lqw);
|
||||
|
||||
if (flag) {
|
||||
List<SisPersonLibImgVo> list = new ArrayList<>();
|
||||
ids.forEach(id -> {
|
||||
SisPersonLibImgVo imgVo = sisPersonLibImgService.queryByPersonId(id);
|
||||
if (imgVo != null) {
|
||||
list.add(imgVo);
|
||||
}
|
||||
});
|
||||
Collection<Long> imgIds = list.stream().map(SisPersonLibImgVo::getId).toList();
|
||||
if (CollUtil.isNotEmpty(imgIds)){
|
||||
flag = sisPersonLibImgService.deleteWithValidByIds(imgIds, false);
|
||||
Assert.isTrue(flag, "删除人像库图片失败");
|
||||
}
|
||||
|
||||
if (CollUtil.isNotEmpty(e8Ids)){
|
||||
e8Ids.forEach(e8PlatformApi::deleteCustomer);
|
||||
List<SisPersonLibImgVo> list = new ArrayList<>();
|
||||
ids.forEach(id -> {
|
||||
SisPersonLibImgVo imgVo = sisPersonLibImgService.queryByPersonId(id);
|
||||
if (imgVo != null) {
|
||||
list.add(imgVo);
|
||||
}
|
||||
});
|
||||
Collection<Long> imgIds = list.stream().map(SisPersonLibImgVo::getId).toList();
|
||||
if (CollUtil.isNotEmpty(imgIds)) {
|
||||
Boolean flag = sisPersonLibImgService.deleteWithValidByIds(imgIds, false);
|
||||
Assert.isTrue(flag, "删除人像库图片失败");
|
||||
}
|
||||
return flag;
|
||||
|
||||
if (CollUtil.isNotEmpty(e8Ids)) {
|
||||
e8Ids.forEach(e8PlatformApi::deleteCustomer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,7 +387,7 @@ public class SisAuthRecordServiceImpl implements ISisAuthRecordService {
|
||||
* @param groupIds 权限组IDs
|
||||
*/
|
||||
@Override
|
||||
public Boolean queryByGroupIds(Collection<Long> groupIds){
|
||||
public Boolean queryByGroupIds(Collection<Long> groupIds) {
|
||||
LambdaQueryWrapper<SisAuthRecord> lqw = Wrappers.lambdaQuery();
|
||||
lqw.in(SisAuthRecord::getGroupId, groupIds);
|
||||
List<SisAuthRecordVo> authVoList = baseMapper.selectVoList(lqw);
|
||||
|
@@ -328,4 +328,14 @@ public class SisDeviceChannelServiceImpl implements ISisDeviceChannelService {
|
||||
lqw.eq(SisDeviceChannel::getDeviceIp, deviceIp);
|
||||
return baseMapper.update(lqw) > 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SisDeviceChannel queryChannels(String deviceIp, String channelNo) {
|
||||
LambdaQueryWrapper<SisDeviceChannel> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(SisDeviceChannel::getDeviceIp, deviceIp);
|
||||
lqw.eq(SisDeviceChannel::getChannelNo, channelNo)
|
||||
.or().eq(SisDeviceChannel::getNvrChannelNo, channelNo);
|
||||
return baseMapper.selectOne(lqw);
|
||||
}
|
||||
}
|
||||
|
@@ -177,18 +177,15 @@ public class SisDeviceManageServiceImpl implements ISisDeviceManageService {
|
||||
log.info("删除设备通道完成,num={}", num);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过设备ip查询设备信息
|
||||
* @param deviceIp 设备ip
|
||||
* @return 设备信息
|
||||
*/
|
||||
@Override
|
||||
public SisDeviceManageVo queryVoByDeviceIp(String deviceIp) {
|
||||
public SisDeviceManage queryByDeviceIp(String deviceIp) {
|
||||
LambdaQueryWrapper<SisDeviceManage> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(SisDeviceManage::getDeviceIp, deviceIp);
|
||||
return baseMapper.selectVoOne(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SisDeviceManage queryByDeviceIp(String deviceId) {
|
||||
LambdaQueryWrapper<SisDeviceManage> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(SisDeviceManage::getDeviceIp, deviceId);
|
||||
return baseMapper.selectOne(lqw);
|
||||
}
|
||||
|
||||
|
@@ -202,6 +202,9 @@ public class SisElevatorFloorRefServiceImpl implements ISisElevatorFloorRefServi
|
||||
lqw.in(SisElevatorFloorRef::getFloorId, info.stream().map(SisElevatorFloorChannelRef::getFloorId).toList());
|
||||
List<SisElevatorFloorRef> list = baseMapper.selectList(lqw);
|
||||
|
||||
// list为空不需要冗余数据,返回true
|
||||
if(list.isEmpty()) return true;
|
||||
|
||||
// 创建Map提高查找效率,避免在循环中重复流操作
|
||||
Map<Long, SisElevatorFloorChannelRef> floorInfoMap = info.stream()
|
||||
.collect(Collectors.toMap(SisElevatorFloorChannelRef::getFloorId, Function.identity()));
|
||||
|
@@ -70,7 +70,7 @@ public class SisElevatorInfoServiceImpl implements ISisElevatorInfoService {
|
||||
SisElevatorInfoVo.DeviceInfo deviceInfo = new SisElevatorInfoVo.DeviceInfo();
|
||||
deviceInfo.setDeviceId(item.getDeviceId());
|
||||
deviceInfo.setDeviceIp(item.getDeviceIp());
|
||||
if (Objects.equals(item.getControlType(), ControlTypeEnum.REMOTE_CALL_ELEVATOR.getCode())) {
|
||||
if (Objects.equals(item.getControlType(), ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode())) {
|
||||
remoteCallElevatorDeviceId.add(deviceInfo);
|
||||
} else {
|
||||
sisElevatorInfoVo.setElevatorControlDeviceId(deviceInfo);
|
||||
@@ -207,7 +207,7 @@ public class SisElevatorInfoServiceImpl implements ISisElevatorInfoService {
|
||||
ref.setDeviceIp(bo.getElevatorControlDeviceId().getDeviceIp());
|
||||
ref.setBindId(bo.getElevatorId());
|
||||
ref.setDeviceFloorId(vo.getFloorId());
|
||||
ref.setControlType(ControlTypeEnum.ELEVATOR_CONTROL.getCode());
|
||||
ref.setControlType(ControlTypeEnum.ELEVATOR_IN_CONTROL.getCode());
|
||||
ls.add(ref);
|
||||
}
|
||||
// 远程呼叫
|
||||
@@ -220,7 +220,7 @@ public class SisElevatorInfoServiceImpl implements ISisElevatorInfoService {
|
||||
ref.setDeviceIp(deviceInfo.getDeviceIp());
|
||||
ref.setBindId(bo.getElevatorId());
|
||||
ref.setDeviceFloorId(vo.getFloorId());
|
||||
ref.setControlType(ControlTypeEnum.REMOTE_CALL_ELEVATOR.getCode());
|
||||
ref.setControlType(ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode());
|
||||
ls.add(ref);
|
||||
}
|
||||
}
|
||||
@@ -252,7 +252,7 @@ public class SisElevatorInfoServiceImpl implements ISisElevatorInfoService {
|
||||
|
||||
// 冗余数据到电梯⇄楼层关联表
|
||||
Boolean update = elevatorFloorRefService.batchUpdateChannel(channelRefs);
|
||||
|
||||
Assert.isTrue(update, "冗余数据到电梯⇄楼层关联表失败!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,27 +1,23 @@
|
||||
package org.dromara.sis.service.impl;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.dromara.common.core.constant.CodePrefixConstants;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
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.resource.api.RemoteFileService;
|
||||
import org.dromara.sis.domain.SisLibImgRef;
|
||||
import org.dromara.sis.domain.SisPersonLibImg;
|
||||
import org.dromara.sis.domain.bo.SisLibImgRefBo;
|
||||
import org.dromara.sis.domain.bo.SisPersonLibImgBo;
|
||||
import org.dromara.sis.domain.vo.*;
|
||||
import org.dromara.sis.mapper.SisPersonLibImgMapper;
|
||||
import org.dromara.sis.sdk.huawei.HuaWeiBoxApi;
|
||||
import org.dromara.sis.sdk.huawei.domain.AddHWPersonReq;
|
||||
import org.dromara.sis.service.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -157,7 +153,7 @@ public class SisPersonLibImgServiceImpl implements ISisPersonLibImgService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
List<SisPersonLibImgVo> list = this.queryListByIds(ids);
|
||||
Collection<Long> remoteIds = list.stream().map(SisPersonLibImgVo::getRemoteImgId).toList();
|
||||
Collection<Long> remoteIds = list.stream().map(SisPersonLibImgVo::getRemoteHWId).toList();
|
||||
|
||||
if (!remoteIds.isEmpty()) {
|
||||
Boolean flag = huaWeiBoxApi.deletePerson(remoteIds);
|
||||
@@ -194,7 +190,7 @@ public class SisPersonLibImgServiceImpl implements ISisPersonLibImgService {
|
||||
LambdaQueryWrapper<SisPersonLibImg> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(SisPersonLibImg::getResidentPersonId, personId);
|
||||
SisPersonLibImg update = new SisPersonLibImg();
|
||||
update.setRemoteImgId(huaweiBoxId);
|
||||
update.setRemoteHWId(huaweiBoxId);
|
||||
update.setImgMd5Value(imgMd5);
|
||||
return baseMapper.update(update, lqw) > 0;
|
||||
}
|
||||
|
@@ -4,10 +4,15 @@ import cn.hutool.core.codec.Base64Encoder;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.dromara.property.api.RemoteFloorService;
|
||||
import org.dromara.property.api.domain.vo.RemoteFloorVo;
|
||||
import org.dromara.sis.domain.SisElevatorFloorChannelRef;
|
||||
import org.dromara.sis.domain.enums.ControlTypeEnum;
|
||||
import org.dromara.sis.domain.enums.RosterTypeEnum;
|
||||
import org.dromara.sis.domain.vo.*;
|
||||
import org.dromara.sis.producer.CleanLiftAuthRocketProducer;
|
||||
import org.dromara.sis.sdk.e8.E8PlatformApi;
|
||||
@@ -15,9 +20,11 @@ import org.dromara.sis.sdk.e8.domain.accessControl.req.RemoteOpenDoorReq;
|
||||
import org.dromara.sis.sdk.hik.HikApiService;
|
||||
import org.dromara.sis.sdk.huawei.HuaWeiBoxApi;
|
||||
import org.dromara.sis.sdk.huawei.domain.HWResult;
|
||||
import org.dromara.sis.sdk.smartDevices.utils.ElevatorControlTcpUtil;
|
||||
import org.dromara.sis.service.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -40,6 +47,7 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
private final ISisDeviceBindRefService deviceBindRefService;
|
||||
private final ISisAccessControlService accessControlService;
|
||||
private final ISisElevatorFloorRefService elevatorFloorRefService;
|
||||
private final ISisElevatorFloorChannelRefService elevatorFloorChannelRefService;
|
||||
private final E8PlatformApi e8PlatformApi;
|
||||
private final ISisAlarmEventsService alarmEventsService;
|
||||
|
||||
@@ -57,7 +65,7 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
if (result.getCode() != 200) {
|
||||
log.info("华为盒子比对失败,msg={}", result.getMessage());
|
||||
// 产生告警数据
|
||||
alarmEventsService.createAlarmRecord(deviceIp, 1, 1, "人脸比对失败", smallImg, bigImg);
|
||||
// alarmEventsService.createAlarmRecord(deviceIp, 1, 1, "人脸比对失败", smallImg, bigImg);
|
||||
return;
|
||||
}
|
||||
log.info("人脸比对执行完成,耗时:{}ms", interval.intervalMs());
|
||||
@@ -68,13 +76,19 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
if (authRecord == null) {
|
||||
log.info("人员[{}]没有授权记录,判定为陌生人", person);
|
||||
// 不是内部人员 产生紧急的告警信息
|
||||
alarmEventsService.createAlarmRecord(deviceIp, 1, 1, "陌生人员入内", smallImg, bigImg);
|
||||
// alarmEventsService.createAlarmRecord(deviceIp, 1, 1, "陌生人员入内", smallImg, bigImg);
|
||||
return;
|
||||
} else {
|
||||
if (Objects.equals(authRecord.getRosterType(), RosterTypeEnum.BLACK_LIST.getCode())) {
|
||||
log.info("人员[{}]在黑名单中,暂不处理。", person);
|
||||
// alarmEventsService.createAlarmRecord(deviceIp, 3, 1, "黑名单人员入内", smallImg, bigImg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
if (DateUtil.compare(now, authRecord.getEndDate()) > 0) {
|
||||
alarmEventsService.createAlarmRecord(deviceIp, 3, 1, "人员授权信息已过期", smallImg, bigImg);
|
||||
// alarmEventsService.createAlarmRecord(deviceIp, 3, 1, "人员授权信息已过期", smallImg, bigImg);
|
||||
log.info("当前人脸已过期,暂不处理。");
|
||||
return;
|
||||
}
|
||||
@@ -102,12 +116,12 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
return;
|
||||
}
|
||||
// 判断绑定设备类型,走不同的处理方法
|
||||
if (item.getControlType() == 1) { // 门禁
|
||||
handleAc(item.getDeviceId());
|
||||
} else if (item.getControlType() == 2) { // 电梯外面面板权限
|
||||
handleEle(item.getDeviceId(), r.getAuthGroupId(), 2, item.getDeviceFloorId());
|
||||
} else if (item.getControlType() == 3) { // 电梯里面的面板
|
||||
handleEle(item.getDeviceId(), r.getAuthGroupId(), 3, item.getDeviceFloorId());
|
||||
if (Objects.equals(item.getControlType(), ControlTypeEnum.ACCESS_CONTROL.getCode())) { // 门禁
|
||||
handleAc(item.getBindId());
|
||||
} else if (item.getControlType().equals(ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode())) { // 电梯外面面板权限
|
||||
handleEle(item.getBindId(), r.getAuthGroupId(), ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode(), item.getDeviceFloorId());
|
||||
} else if (item.getControlType().equals(ControlTypeEnum.ELEVATOR_IN_CONTROL.getCode())) { // 电梯里面的面板
|
||||
handleEle(item.getBindId(), r.getAuthGroupId(), ControlTypeEnum.ELEVATOR_IN_CONTROL.getCode(), item.getDeviceFloorId());
|
||||
} else {
|
||||
log.info("设备绑定了未知的控制类型[{}],不处理", item.getControlType());
|
||||
}
|
||||
@@ -115,6 +129,38 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
log.info("权限下发执行完成,耗时:{}ms", interval.intervalMs());
|
||||
}
|
||||
|
||||
/**
|
||||
* 电梯外部按键触发
|
||||
*
|
||||
* @param deviceIp 设备ip
|
||||
*/
|
||||
@Override
|
||||
public void handleEleOut(String deviceIp, TimeInterval interval) {
|
||||
// 获取当前设备的绑定设备信息
|
||||
List<SisDeviceBindRefVo> bindRefList = deviceBindRefService.queryByDeviceIp(deviceIp);
|
||||
List<SisDeviceBindRefVo> outRefList = bindRefList.stream().filter(item -> Objects.equals(item.getControlType(), ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode())).toList();
|
||||
|
||||
outRefList.forEach(item -> {
|
||||
// 获取当前电梯信息
|
||||
SisElevatorInfoVo ele = elevatorInfoService.queryById(item.getBindId());
|
||||
|
||||
List<SisElevatorFloorChannelRefVo> channelRef = elevatorFloorChannelRefService.queryByFloorIds(List.of(item.getDeviceFloorId()));
|
||||
SisElevatorFloorChannelRefVo channelRefVo = channelRef.stream().filter(o -> Objects.equals(o.getElevatorId(), item.getBindId())).findFirst().orElse(null);
|
||||
|
||||
if (channelRefVo != null) {
|
||||
if (channelRefVo.getDownChannel() != null) {
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), channelRefVo.getDownChannel().intValue(), 2);
|
||||
}
|
||||
|
||||
if (channelRefVo.getUpChannel() != null) {
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), channelRefVo.getUpChannel().intValue(), 2);
|
||||
}
|
||||
}
|
||||
cleanLiftAuthRocketProducer.sendMessage(item.getBindId(), 0L, item.getDeviceFloorId(), "清除电梯" + item.getBindId() + "梯控权限", 3);
|
||||
});
|
||||
log.info("处理电梯外部按键完成,耗时:{}ms", interval.intervalMs());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成告警事件
|
||||
*/
|
||||
@@ -150,13 +196,15 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
|
||||
// 获取权限组下电梯⇄楼层关联信息
|
||||
List<SisElevatorFloorRefVo> groupRef = elevatorFloorRefService.queryByAuthGroupId(groupId);
|
||||
if (ObjectUtil.isEmpty(groupRef)) return;
|
||||
|
||||
// 取出当前电梯的楼层授权信息
|
||||
List<SisElevatorFloorRefVo> eleRef = groupRef.stream().filter(o -> Objects.equals(o.getElevatorId(), deviceId)).toList();
|
||||
if (ObjectUtil.isEmpty(eleRef)) return;
|
||||
|
||||
for (SisElevatorFloorRefVo ref : eleRef){
|
||||
if (controlType == 2){
|
||||
log.info("开始下发外面版梯控权限....");
|
||||
if (Objects.equals(controlType, ControlTypeEnum.ELEVATOR_IN_CONTROL.getCode())) {
|
||||
log.info("开始下发里面版梯控权限....");
|
||||
for (SisElevatorFloorRefVo ref : eleRef) {
|
||||
if (ref.getUpChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)) {
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getUpChannel().intValue(), 2);
|
||||
}
|
||||
@@ -164,12 +212,33 @@ public class ZeroSensationPassageServiceImpl implements IZeroSensationPassageSer
|
||||
if (ref.getDownChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)) {
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getDownChannel().intValue(), 2);
|
||||
}
|
||||
}else {
|
||||
log.info("开始下发里面版梯控权限....");
|
||||
if (ref.getInChannel() != null && Objects.equals(ref.getFloorId(), deviceFloorId)) {
|
||||
HikApiService.getInstance().controlGateway(ele.getControlIp(), ref.getInChannel().intValue(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 获取当前电梯所在建筑的楼层
|
||||
List<RemoteFloorVo> floorList = remoteFloorService.queryByBuildingId(ele.getBuildingId());
|
||||
if (CollUtil.isEmpty(floorList)) return;
|
||||
|
||||
if (Objects.equals(controlType, ControlTypeEnum.ELEVATOR_OUT_CONTROL.getCode())) {
|
||||
SisElevatorFloorRefVo vo;
|
||||
List<Integer> num = new ArrayList<>();
|
||||
for (int i = 1; i < floorList.size(); i++) {
|
||||
int finalI = i;
|
||||
// 取出权限楼层id与实际楼层id相等的数据
|
||||
vo = eleRef.stream().filter(o -> Objects.equals(o.getFloorId(), floorList.get(finalI - 1).getId())).findFirst().orElse(null);
|
||||
// 存在权限楼层,添加到num中,梯控模块从1开始
|
||||
if (vo != null) {
|
||||
num.add(i);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isEmpty(num)) return;
|
||||
|
||||
if (!ElevatorControlTcpUtil.getInstance().isClientConnected()) {
|
||||
log.info("梯控模块未连接,请检查梯控模块是否启动");
|
||||
return;
|
||||
}
|
||||
ElevatorControlTcpUtil.getInstance().sendManualCommand(num);
|
||||
}
|
||||
log.info("梯控下发权限完成");
|
||||
|
||||
|
@@ -5,7 +5,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<mapper namespace="org.dromara.sis.mapper.SisAuthRecordMapper">
|
||||
|
||||
<select id="checkAuth" resultType="org.dromara.sis.domain.vo.SisAuthRecordVo">
|
||||
SELECT c.target_id, c.group_id, c.beg_date, c.end_date
|
||||
SELECT c.target_id, c.group_id, c.beg_date, c.end_date, a.roster_type
|
||||
FROM sis_person_lib_img a
|
||||
LEFT JOIN resident_person b ON a.resident_person_id = b.id and b.state = 1
|
||||
LEFT JOIN sis_auth_record c
|
||||
|
Reference in New Issue
Block a user