mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-05-25 22:47:49 +08:00
增加多端口模式支持随机SSRC配置
This commit is contained in:
parent
545d667fad
commit
324f75ce76
@ -128,6 +128,11 @@ public class UserSetting {
|
|||||||
*/
|
*/
|
||||||
private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
|
private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多端口模式使用随机SSRC,端口区分流,SSRC允许重复
|
||||||
|
*/
|
||||||
|
private Boolean ssrcRandom = Boolean.FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开启接口文档页面。 默认开启,生产环境建议关闭,遇到swagger相关的漏洞时也可以关闭
|
* 开启接口文档页面。 默认开启,生产环境建议关闭,遇到swagger相关的漏洞时也可以关闭
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.session;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||||
|
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||||
@ -24,6 +25,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class SSRCFactory {
|
public class SSRCFactory {
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, BitSet> usedMap = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, BitSet> usedMap = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
|
||||||
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
Thread t = new Thread(r, "ssrc-rebuild");
|
Thread t = new Thread(r, "ssrc-rebuild");
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
@ -39,6 +41,9 @@ public class SSRCFactory {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SipConfig sipConfig;
|
private SipConfig sipConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserSetting userSetting;
|
||||||
|
|
||||||
private String domainPart;
|
private String domainPart;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -58,53 +63,68 @@ public class SSRCFactory {
|
|||||||
return suffix != null ? "1" + suffix : null;
|
return suffix != null ? "1" + suffix : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPlaySsrcRandom() {
|
||||||
|
return "0" + domainPart + String.format("%04d", ThreadLocalRandom.current().nextInt(10000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayBackSsrcRandom() {
|
||||||
|
return "1" + domainPart + String.format("%04d", ThreadLocalRandom.current().nextInt(10000));
|
||||||
|
}
|
||||||
|
|
||||||
private String allocate(String mediaServerId) {
|
private String allocate(String mediaServerId) {
|
||||||
BitSet bits = usedMap.computeIfAbsent(mediaServerId, k -> new BitSet(10000));
|
synchronized (lockMap.computeIfAbsent(mediaServerId, k -> new Object())) {
|
||||||
int start = ThreadLocalRandom.current().nextInt(10000);
|
BitSet bits = usedMap.computeIfAbsent(mediaServerId, k -> new BitSet(10000));
|
||||||
int index = start;
|
int start = ThreadLocalRandom.current().nextInt(10000);
|
||||||
do {
|
int index = start;
|
||||||
if (!bits.get(index)) {
|
do {
|
||||||
bits.set(index);
|
if (!bits.get(index)) {
|
||||||
return domainPart + String.format("%04d", index);
|
bits.set(index);
|
||||||
}
|
return domainPart + String.format("%04d", index);
|
||||||
index = (index + 1) % 10000;
|
}
|
||||||
} while (index != start);
|
index = (index + 1) % 10000;
|
||||||
log.warn("[SSRC] 媒体节点 {} 的SSRC已用尽", mediaServerId);
|
} while (index != start);
|
||||||
return null;
|
log.warn("[SSRC] 媒体节点 {} 的SSRC已用尽", mediaServerId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rebuild() {
|
void rebuild() {
|
||||||
List<MediaServer> servers = mediaServerService.getAll();
|
List<MediaServer> servers = mediaServerService.getAll();
|
||||||
for (MediaServer server : servers) {
|
for (MediaServer server : servers) {
|
||||||
BitSet bits = new BitSet(10000);
|
if (server.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
int count = 0;
|
continue;
|
||||||
try {
|
}
|
||||||
ZLMResult<?> result = zlmresTfulUtils.getMediaList(server, null, null, "rtsp", null);
|
synchronized (lockMap.computeIfAbsent(server.getId(), k -> new Object())) {
|
||||||
if (result != null && result.getCode() == 0 && result.getData() != null) {
|
BitSet bits = new BitSet(10000);
|
||||||
List<JSONObject> list = (List<JSONObject>) result.getData();
|
int count = 0;
|
||||||
for (JSONObject obj : list) {
|
try {
|
||||||
if (obj.getIntValue("originType") != 3) continue;
|
ZLMResult<?> result = zlmresTfulUtils.getMediaList(server, null, null, "rtsp", null);
|
||||||
String originUrl = obj.getString("originUrl");
|
if (result != null && result.getCode() == 0 && result.getData() != null) {
|
||||||
if (originUrl == null) continue;
|
List<JSONObject> list = (List<JSONObject>) result.getData();
|
||||||
int idx = originUrl.lastIndexOf("/rtp/");
|
for (JSONObject obj : list) {
|
||||||
if (idx == -1) continue;
|
if (obj.getIntValue("originType") != 3) continue;
|
||||||
try {
|
String originUrl = obj.getString("originUrl");
|
||||||
int suffix = (int) (Long.parseLong(originUrl.substring(idx + 5), 16) % 10000);
|
if (originUrl == null) continue;
|
||||||
bits.set(suffix);
|
int idx = originUrl.lastIndexOf("/rtp/");
|
||||||
count++;
|
if (idx == -1) continue;
|
||||||
} catch (NumberFormatException ignored) {
|
try {
|
||||||
|
int suffix = (int) (Long.parseLong(originUrl.substring(idx + 5), 16) % 10000);
|
||||||
|
bits.set(suffix);
|
||||||
|
count++;
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[SSRC重建] 查询媒体节点 {} 失败: {}", server.getId(), e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
usedMap.put(server.getId(), bits);
|
||||||
log.warn("[SSRC重建] 查询媒体节点 {} 失败: {}", server.getId(), e.getMessage());
|
if (count > 8000) {
|
||||||
}
|
log.info("[SSRC重建] 媒体节点 {} 的SSRC使用率已超过80%,请注意扩展服务提升性能", server.getId());
|
||||||
usedMap.put(server.getId(), bits);
|
} else {
|
||||||
if (count > 8000) {
|
if (log.isDebugEnabled()) {
|
||||||
log.info("[SSRC重建] 媒体节点 {} 的SSRC使用率已超过80%,请注意扩展服务提升性能", server.getId());
|
log.debug("[SSRC重建] 节点 {} 已占用 {} 个SSRC", server.getId(), count);
|
||||||
}else {
|
}
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("[SSRC重建] 节点 {} 已占用 {} 个SSRC", server.getId(), count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,10 @@ public class RTPServerParam {
|
|||||||
private MediaServer mediaServer;
|
private MediaServer mediaServer;
|
||||||
private String app;
|
private String app;
|
||||||
private String streamId;
|
private String streamId;
|
||||||
|
/**
|
||||||
|
* 传递给zlm创建rtp server的streamId,不填则使用streamId
|
||||||
|
*/
|
||||||
|
private String zlmStreamId;
|
||||||
/**
|
/**
|
||||||
* 开启rtpServer时使用的ssrc,开启rtpServer时会根据这个ssrc进行校验,如果不填则不校验
|
* 开启rtpServer时使用的ssrc,开启rtpServer时会根据这个ssrc进行校验,如果不填则不校验
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -90,12 +90,10 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
final String ssrc;
|
final String ssrc;
|
||||||
if (presetSSRC != null) {
|
if (presetSSRC != null) {
|
||||||
ssrc = presetSSRC;
|
ssrc = presetSSRC;
|
||||||
}else {
|
} else if (mediaServer.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
if (playback) {
|
ssrc = playback ? ssrcFactory.getPlayBackSsrcRandom() : ssrcFactory.getPlaySsrcRandom();
|
||||||
ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
} else {
|
||||||
}else {
|
ssrc = playback ? ssrcFactory.getPlayBackSsrc(mediaServer.getId()) : ssrcFactory.getPlaySsrc(mediaServer.getId());
|
||||||
ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (streamId == null) {
|
if (streamId == null) {
|
||||||
streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
||||||
@ -139,18 +137,14 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
final String ssrc;
|
final String ssrc;
|
||||||
if (presetSSRC != null) {
|
if (presetSSRC != null) {
|
||||||
ssrc = presetSSRC;
|
ssrc = presetSSRC;
|
||||||
}else {
|
} else if (mediaServer.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
|
ssrc = ssrcFactory.getPlaySsrcRandom();
|
||||||
|
} else {
|
||||||
ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
|
ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
String streamId;
|
String streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
||||||
String streamReplace = null;
|
String streamReplace = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId());
|
||||||
if (mediaServer.isRtpEnable()) {
|
|
||||||
streamId = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId());
|
|
||||||
}else {
|
|
||||||
streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
|
||||||
streamReplace = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
||||||
|
|
||||||
@ -161,8 +155,8 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
|
|
||||||
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
||||||
|
|
||||||
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamReplace != null ? streamReplace : streamId);
|
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamReplace);
|
||||||
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback);
|
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback, streamId);
|
||||||
addAuthenticateInfo(streamId, streamReplace, channel.isHasAudio(), record, null);
|
addAuthenticateInfo(streamId, streamReplace, channel.isHasAudio(), record, null);
|
||||||
return ssrcInfo;
|
return ssrcInfo;
|
||||||
}
|
}
|
||||||
@ -180,17 +174,16 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取 mediaServer 可用的 ssrc
|
// 获取 mediaServer 可用的 ssrc
|
||||||
String ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
String ssrc;
|
||||||
|
if (mediaServer.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
String streamId;
|
ssrc = ssrcFactory.getPlayBackSsrcRandom();
|
||||||
String streamReplace = null;
|
} else {
|
||||||
if (mediaServer.isRtpEnable()) {
|
ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
||||||
streamId = getPlaybackStream(device, channel, startTime, endTime);
|
|
||||||
}else {
|
|
||||||
streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
|
||||||
streamReplace = getPlaybackStream(device, channel, startTime, endTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
||||||
|
String streamReplace = getPlaybackStream(device, channel, startTime, endTime);
|
||||||
|
|
||||||
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
||||||
|
|
||||||
if (device.isSsrcCheck() && tcpMode > 0) {
|
if (device.isSsrcCheck() && tcpMode > 0) {
|
||||||
@ -200,8 +193,8 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
|
|
||||||
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
||||||
|
|
||||||
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamReplace != null ? streamReplace : streamId);
|
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamReplace);
|
||||||
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback);
|
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback, streamId);
|
||||||
addAuthenticateInfo(streamId, streamReplace, channel.isHasAudio(), false,null);
|
addAuthenticateInfo(streamId, streamReplace, channel.isHasAudio(), false,null);
|
||||||
return ssrcInfo;
|
return ssrcInfo;
|
||||||
}
|
}
|
||||||
@ -233,8 +226,18 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0);
|
||||||
|
|
||||||
// 获取 mediaServer 可用的 ssrc
|
// 获取 mediaServer 可用的 ssrc
|
||||||
String ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
String ssrc;
|
||||||
|
if (mediaServer.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
|
ssrc = ssrcFactory.getPlayBackSsrcRandom();
|
||||||
|
} else {
|
||||||
|
ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
String streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
String streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
|
||||||
|
String streamReplace = String.format("%s_%s_%s_%s", device.getDeviceId(), channel.getDeviceId(),
|
||||||
|
startTime.replace("-", "").replace(":", "").replace(" ", ""),
|
||||||
|
endTime.replace("-", "").replace(":", "").replace(" ", ""));
|
||||||
|
|
||||||
if (device.isSsrcCheck() && tcpMode > 0) {
|
if (device.isSsrcCheck() && tcpMode > 0) {
|
||||||
// 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
|
// 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
|
||||||
log.warn("[开启国标录像下载RTP收流] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
|
log.warn("[开启国标录像下载RTP收流] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
|
||||||
@ -242,12 +245,12 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
|
|
||||||
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
Long checkSsrc = device.isSsrcCheck() ? Long.parseLong(ssrc) : 0L;
|
||||||
|
|
||||||
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamId);
|
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamReplace);
|
||||||
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback);
|
openRtpServer(mediaServer, ssrcInfo, checkSsrc, !channel.isHasAudio(), false, tcpMode, callback, streamId);
|
||||||
|
|
||||||
long difference = DateUtil.getDifference(startTime, endTime) / 1000;
|
long difference = DateUtil.getDifference(startTime, endTime) / 1000;
|
||||||
|
|
||||||
addAuthenticateInfo(streamId, null, channel.isHasAudio(), true, (int) difference);
|
addAuthenticateInfo(streamId, streamReplace, channel.isHasAudio(), true, (int) difference);
|
||||||
return ssrcInfo;
|
return ssrcInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +281,12 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取 mediaServer 可用的 ssrc
|
// 获取 mediaServer 可用的 ssrc
|
||||||
String ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
|
String ssrc;
|
||||||
|
if (mediaServer.isRtpEnable() && userSetting.getSsrcRandom()) {
|
||||||
|
ssrc = ssrcFactory.getPlaySsrcRandom();
|
||||||
|
} else {
|
||||||
|
ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamId);
|
SSRCInfo ssrcInfo = new SSRCInfo(0, ssrc, MediaStreamUtil.RTP_APP, streamId);
|
||||||
openRtpServer(mediaServer, ssrcInfo, 0L, false, true, tcpMode, callback);
|
openRtpServer(mediaServer, ssrcInfo, 0L, false, true, tcpMode, callback);
|
||||||
@ -287,8 +295,14 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
|
|
||||||
private void openRtpServer(MediaServer mediaServer, SSRCInfo ssrcInfo, Long checkSsrc, boolean disableAuto, boolean onlyAuto, int tcpMode,
|
private void openRtpServer(MediaServer mediaServer, SSRCInfo ssrcInfo, Long checkSsrc, boolean disableAuto, boolean onlyAuto, int tcpMode,
|
||||||
ErrorCallback<OpenRTPServerResult> callback) {
|
ErrorCallback<OpenRTPServerResult> callback) {
|
||||||
|
openRtpServer(mediaServer, ssrcInfo, checkSsrc, disableAuto, onlyAuto, tcpMode, callback, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openRtpServer(MediaServer mediaServer, SSRCInfo ssrcInfo, Long checkSsrc, boolean disableAuto, boolean onlyAuto, int tcpMode,
|
||||||
|
ErrorCallback<OpenRTPServerResult> callback, String zlmStreamId) {
|
||||||
|
|
||||||
RTPServerParam rtpServerParam = new RTPServerParam(mediaServer, MediaStreamUtil.RTP_APP, ssrcInfo.getStream(), checkSsrc, null, onlyAuto, disableAuto, false, tcpMode);
|
RTPServerParam rtpServerParam = new RTPServerParam(mediaServer, MediaStreamUtil.RTP_APP, ssrcInfo.getStream(), checkSsrc, null, onlyAuto, disableAuto, false, tcpMode);
|
||||||
|
rtpServerParam.setZlmStreamId(zlmStreamId);
|
||||||
int rtpServerPort = openCommonRTPServer(rtpServerParam, ((code, msg, data) -> {
|
int rtpServerPort = openCommonRTPServer(rtpServerParam, ((code, msg, data) -> {
|
||||||
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
||||||
OpenRTPServerResult openRTPServerResult = new OpenRTPServerResult();
|
OpenRTPServerResult openRTPServerResult = new OpenRTPServerResult();
|
||||||
@ -336,7 +350,8 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
|
|||||||
|
|
||||||
int rtpServerPort;
|
int rtpServerPort;
|
||||||
if (rtpServerParam.getMediaServer().isRtpEnable()) {
|
if (rtpServerParam.getMediaServer().isRtpEnable()) {
|
||||||
rtpServerPort = mediaServerService.createRTPServer(rtpServerParam.getMediaServer(), rtpServerParam.getApp(), rtpServerParam.getStreamId(),
|
String effectiveStreamId = rtpServerParam.getZlmStreamId() != null ? rtpServerParam.getZlmStreamId() : rtpServerParam.getStreamId();
|
||||||
|
rtpServerPort = mediaServerService.createRTPServer(rtpServerParam.getMediaServer(), rtpServerParam.getApp(), effectiveStreamId,
|
||||||
Objects.requireNonNullElse(rtpServerParam.getSsrc(), 0L), rtpServerParam.getPort(), rtpServerParam.isOnlyAuto(),
|
Objects.requireNonNullElse(rtpServerParam.getSsrc(), 0L), rtpServerParam.getPort(), rtpServerParam.isOnlyAuto(),
|
||||||
rtpServerParam.isDisableAudio(), rtpServerParam.isReUsePort(), rtpServerParam.getTcpMode());
|
rtpServerParam.isDisableAudio(), rtpServerParam.isReUsePort(), rtpServerParam.getTcpMode());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -99,6 +99,8 @@ media:
|
|||||||
user-settings:
|
user-settings:
|
||||||
# 点播/录像回放 等待超时时间,单位:毫秒
|
# 点播/录像回放 等待超时时间,单位:毫秒
|
||||||
play-timeout: 180000
|
play-timeout: 180000
|
||||||
|
# [可选] 多端口模式使用随机SSRC,SSRC允许重复(默认false)
|
||||||
|
ssrc-random: false
|
||||||
# [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true
|
# [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true
|
||||||
auto-apply-play: true
|
auto-apply-play: true
|
||||||
# 推流直播是否录制
|
# 推流直播是否录制
|
||||||
|
|||||||
@ -0,0 +1,293 @@
|
|||||||
|
package com.genersoft.iot.vmp.gb28181.dao.provider;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.Group;
|
||||||
|
import com.genersoft.iot.vmp.web.custom.bean.CameraGroup;
|
||||||
|
import com.genersoft.iot.vmp.web.custom.bean.Point;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class ChannelProviderTest {
|
||||||
|
|
||||||
|
private final ChannelProvider provider = new ChannelProvider();
|
||||||
|
|
||||||
|
// ========== queryByGbDeviceIds ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryByGbDeviceIds_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("deviceIds", Arrays.asList("DEV001", "DEV002"));
|
||||||
|
String sql = provider.queryByGbDeviceIds(params);
|
||||||
|
assertTrue(sql.contains("#{deviceIds[0]}"), "should use #{deviceIds[0]}");
|
||||||
|
assertTrue(sql.contains("#{deviceIds[1]}"), "should use #{deviceIds[1]}");
|
||||||
|
assertFalse(sql.contains("'DEV001'"), "should not contain raw quoted value");
|
||||||
|
assertFalse(sql.contains("'DEV002'"), "should not contain raw quoted value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryByGbDeviceIds_shouldNotQuoteBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("deviceIds", Collections.singletonList("INJECT' OR 1=1 --"));
|
||||||
|
String sql = provider.queryByGbDeviceIds(params);
|
||||||
|
assertTrue(sql.contains("#{deviceIds[0]}"), "should use bind variable for injection attempt");
|
||||||
|
assertFalse(sql.contains("1=1"), "should not contain injection payload in SQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryByGroupList ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryByGroupList_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
Group g1 = new Group();
|
||||||
|
g1.setDeviceId("GRP001");
|
||||||
|
Group g2 = new Group();
|
||||||
|
g2.setDeviceId("GRP002");
|
||||||
|
params.put("groupList", Arrays.asList(g1, g2));
|
||||||
|
String sql = provider.queryByGroupList(params);
|
||||||
|
assertTrue(sql.contains("#{groupList[0].deviceId}"), "should use #{groupList[0].deviceId}");
|
||||||
|
assertTrue(sql.contains("#{groupList[1].deviceId}"), "should use #{groupList[1].deviceId}");
|
||||||
|
assertFalse(sql.contains("GRP001"), "should not contain raw deviceId");
|
||||||
|
assertFalse(sql.contains("GRP002"), "should not contain raw deviceId");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryOnlineListsByGbDeviceIds ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryOnlineListsByGbDeviceIds_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
Device d1 = new Device();
|
||||||
|
d1.setId(101);
|
||||||
|
Device d2 = new Device();
|
||||||
|
d2.setId(102);
|
||||||
|
params.put("deviceList", Arrays.asList(d1, d2));
|
||||||
|
String sql = provider.queryOnlineListsByGbDeviceIds(params);
|
||||||
|
assertTrue(sql.contains("#{deviceList[0].id}"), "should use #{deviceList[0].id}");
|
||||||
|
assertTrue(sql.contains("#{deviceList[1].id}"), "should use #{deviceList[1].id}");
|
||||||
|
assertFalse(sql.contains("101"), "should not contain raw id");
|
||||||
|
assertFalse(sql.contains("102"), "should not contain raw id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryOnlineListsByGbDeviceIds_withEmptyList_shouldNotHaveInClause() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("deviceList", Collections.emptyList());
|
||||||
|
String sql = provider.queryOnlineListsByGbDeviceIds(params);
|
||||||
|
assertFalse(sql.contains("data_device_id in ("), "should not have IN clause when empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryOnlineListsByGbDeviceIds_withNullList_shouldNotHaveInClause() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("deviceList", null);
|
||||||
|
String sql = provider.queryOnlineListsByGbDeviceIds(params);
|
||||||
|
assertFalse(sql.contains("data_device_id in ("), "should not have IN clause when null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListWithChildForSy ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListWithChildForSy_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg1 = new CameraGroup();
|
||||||
|
cg1.setDeviceId("CG001");
|
||||||
|
CameraGroup cg2 = new CameraGroup();
|
||||||
|
cg2.setDeviceId("CG002");
|
||||||
|
params.put("groupList", Arrays.asList(cg1, cg2));
|
||||||
|
String sql = provider.queryListWithChildForSy(params);
|
||||||
|
assertTrue(sql.contains("#{groupList[0].deviceId}"), "should use #{groupList[0].deviceId}");
|
||||||
|
assertTrue(sql.contains("#{groupList[1].deviceId}"), "should use #{groupList[1].deviceId}");
|
||||||
|
assertFalse(sql.contains("'CG001'"), "should not contain raw quoted value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListWithChildForSy_withQuery_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("query", "search-term");
|
||||||
|
params.put("groupList", Collections.singletonList(new CameraGroup()));
|
||||||
|
String sql = provider.queryListWithChildForSy(params);
|
||||||
|
assertTrue(sql.contains("#{query}"), "should use #{query} bind variable");
|
||||||
|
assertFalse(sql.contains("search-term"), "should not contain raw query");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListWithChildForSy_withSort_shouldUseWhitelist() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("groupList", Collections.singletonList(new CameraGroup()));
|
||||||
|
params.put("sortName", "gbId");
|
||||||
|
params.put("order", true);
|
||||||
|
String sql = provider.queryListWithChildForSy(params);
|
||||||
|
assertTrue(sql.contains("order by gb_id"), "should sort by gb_id");
|
||||||
|
assertTrue(sql.contains("ASC"), "should be ascending");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListWithChildForSy_withSortDesc_shouldUseDesc() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("groupList", Collections.singletonList(new CameraGroup()));
|
||||||
|
params.put("sortName", "gbId");
|
||||||
|
params.put("order", false);
|
||||||
|
String sql = provider.queryListWithChildForSy(params);
|
||||||
|
assertTrue(sql.contains("DESC"), "should be descending");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInBox ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInBox_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("BOX001");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
params.put("level", 3);
|
||||||
|
String sql = provider.queryListInBox(params);
|
||||||
|
assertTrue(sql.contains("#{groupList[0].deviceId}"), "should use bind variable");
|
||||||
|
assertFalse(sql.contains("'BOX001'"), "should not contain raw value");
|
||||||
|
assertTrue(sql.contains("#{level}"), "should use #{level} bind variable");
|
||||||
|
assertTrue(sql.contains("#{minLongitude}"), "should use #{minLongitude}");
|
||||||
|
assertTrue(sql.contains("#{maxLatitude}"), "should use #{maxLatitude}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInCircleForMysql ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInCircleForMysql_shouldUseBindVariablesForGeometry() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("CIRCLE001");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
params.put("centerLongitude", 116.397);
|
||||||
|
params.put("centerLatitude", 39.908);
|
||||||
|
params.put("radius", 1000);
|
||||||
|
|
||||||
|
String sql = provider.queryListInCircleForMysql(params);
|
||||||
|
assertTrue(sql.contains("#{centerLongitude}"), "should use #{centerLongitude} bind variable");
|
||||||
|
assertTrue(sql.contains("#{centerLatitude}"), "should use #{centerLatitude} bind variable");
|
||||||
|
assertTrue(sql.contains("#{radius}"), "should use #{radius} bind variable");
|
||||||
|
assertFalse(sql.contains("116.397"), "should not contain raw longitude");
|
||||||
|
assertFalse(sql.contains("39.908"), "should not contain raw latitude");
|
||||||
|
assertTrue(sql.contains("CONCAT('point(', #{centerLongitude}, ' ', #{centerLatitude}, ')')"),
|
||||||
|
"should build WKT via CONCAT with bind variables");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInCircleForKingBase ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInCircleForKingBase_shouldUseBindVariablesForGeometry() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("CIRCLE002");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
params.put("centerLongitude", 121.473);
|
||||||
|
params.put("centerLatitude", 31.230);
|
||||||
|
params.put("radius", 500);
|
||||||
|
|
||||||
|
String sql = provider.queryListInCircleForKingBase(params);
|
||||||
|
assertTrue(sql.contains("#{centerLongitude}"), "should use #{centerLongitude}");
|
||||||
|
assertTrue(sql.contains("#{centerLatitude}"), "should use #{centerLatitude}");
|
||||||
|
assertTrue(sql.contains("#{radius}"), "should use #{radius}");
|
||||||
|
assertFalse(sql.contains("121.473"), "should not contain raw longitude");
|
||||||
|
assertFalse(sql.contains("31.230"), "should not contain raw latitude");
|
||||||
|
assertTrue(sql.contains("CONCAT('point(', #{centerLongitude}, ' ', #{centerLatitude}, ')')"),
|
||||||
|
"should build WKT via CONCAT with bind variables");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInPolygonForMysql ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInPolygonForMysql_shouldUseBindVariablesForPoints() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("POLY001");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
|
||||||
|
List<Point> points = new ArrayList<>();
|
||||||
|
Point p1 = new Point();
|
||||||
|
p1.setLng(116.0);
|
||||||
|
p1.setLat(39.0);
|
||||||
|
Point p2 = new Point();
|
||||||
|
p2.setLng(117.0);
|
||||||
|
p2.setLat(40.0);
|
||||||
|
points.add(p1);
|
||||||
|
points.add(p2);
|
||||||
|
params.put("pointList", points);
|
||||||
|
|
||||||
|
String sql = provider.queryListInPolygonForMysql(params);
|
||||||
|
assertTrue(sql.contains("#{pointList[0].lng}"), "should use #{pointList[0].lng}");
|
||||||
|
assertTrue(sql.contains("#{pointList[0].lat}"), "should use #{pointList[0].lat}");
|
||||||
|
assertTrue(sql.contains("#{pointList[1].lng}"), "should use #{pointList[1].lng}");
|
||||||
|
assertTrue(sql.contains("#{pointList[1].lat}"), "should use #{pointList[1].lat}");
|
||||||
|
assertFalse(sql.contains("116.0"), "should not contain raw lng");
|
||||||
|
assertFalse(sql.contains("117.0"), "should not contain raw lat");
|
||||||
|
assertTrue(sql.contains("CONCAT('POLYGON(('"), "should use CONCAT to build polygon WKT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInPolygonForKingBase ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInPolygonForKingBase_shouldUseBindVariablesForPoints() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("POLY002");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
|
||||||
|
List<Point> points = new ArrayList<>();
|
||||||
|
Point p1 = new Point();
|
||||||
|
p1.setLng(116.0);
|
||||||
|
p1.setLat(39.0);
|
||||||
|
points.add(p1);
|
||||||
|
params.put("pointList", points);
|
||||||
|
|
||||||
|
String sql = provider.queryListInPolygonForKingBase(params);
|
||||||
|
assertTrue(sql.contains("#{pointList[0].lng}"), "should use #{pointList[0].lng}");
|
||||||
|
assertTrue(sql.contains("#{pointList[0].lat}"), "should use #{pointList[0].lat}");
|
||||||
|
assertFalse(sql.contains("116.0"), "should not contain raw lng");
|
||||||
|
assertFalse(sql.contains("39.0"), "should not contain raw lat");
|
||||||
|
assertTrue(sql.contains("ST_MakePoint"), "should use KingBase specific function");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryListInCircleForMysql with injection attempt ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryListInCircleForMysql_shouldNotContainInjectionPayload() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
CameraGroup cg = new CameraGroup();
|
||||||
|
cg.setDeviceId("NORMAL");
|
||||||
|
params.put("groupList", Collections.singletonList(cg));
|
||||||
|
params.put("centerLongitude", "0) OR 1=1 -- ");
|
||||||
|
params.put("centerLatitude", "0");
|
||||||
|
params.put("radius", 1000);
|
||||||
|
|
||||||
|
String sql = provider.queryListInCircleForMysql(params);
|
||||||
|
assertTrue(sql.contains("#{centerLongitude}"), "should use bind variable for injection payload");
|
||||||
|
assertFalse(sql.contains("1=1"), "should not contain 1=1 in SQL text");
|
||||||
|
assertFalse(sql.contains("OR 1=1"), "should not contain injection");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== queryByGbDeviceIds single element ==========
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryByGbDeviceIds_withSingleElement() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("deviceIds", Collections.singletonList("SINGLE01"));
|
||||||
|
String sql = provider.queryByGbDeviceIds(params);
|
||||||
|
assertEquals(1, countOccurrences(sql, "#{deviceIds[0]}"),
|
||||||
|
"should have exactly one bind variable for single element");
|
||||||
|
assertFalse(sql.contains("#{deviceIds[0]},"), "should not have trailing comma in IN clause");
|
||||||
|
assertFalse(sql.contains(",#{deviceIds[0]}"), "should not have leading comma in IN clause");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== helper ==========
|
||||||
|
|
||||||
|
private int countOccurrences(String str, String substr) {
|
||||||
|
int count = 0;
|
||||||
|
int idx = 0;
|
||||||
|
while ((idx = str.indexOf(substr, idx)) != -1) {
|
||||||
|
count++;
|
||||||
|
idx += substr.length();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
package com.genersoft.iot.vmp.gb28181.dao.provider;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class DeviceChannelProviderTest {
|
||||||
|
|
||||||
|
private final DeviceChannelProvider provider = new DeviceChannelProvider();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withChannelIds_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("channelIds", Arrays.asList("CH001", "CH002", "CH003"));
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("#{channelIds[0]}"), "should use #{channelIds[0]}");
|
||||||
|
assertTrue(sql.contains("#{channelIds[1]}"), "should use #{channelIds[1]}");
|
||||||
|
assertTrue(sql.contains("#{channelIds[2]}"), "should use #{channelIds[2]}");
|
||||||
|
assertFalse(sql.contains("CH001"), "should not contain raw channel id");
|
||||||
|
assertFalse(sql.contains("CH002"), "should not contain raw channel id");
|
||||||
|
assertTrue(sql.contains("dc.device_id in ("), "should have IN clause");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withoutChannelIds_shouldNotContainInClause() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertFalse(sql.contains("device_id in ("), "should not have IN clause when no channelIds");
|
||||||
|
assertTrue(sql.contains("ORDER BY"), "should have ORDER BY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withEmptyChannelIds_shouldNotContainInClause() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("channelIds", Collections.emptyList());
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertFalse(sql.contains("device_id in ("), "should not have IN clause when channelIds empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withDataDeviceId_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("dataDeviceId", 42);
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("#{dataDeviceId}"), "should use #{dataDeviceId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withQuery_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("query", "test");
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("#{query}"), "should use #{query} bind variable");
|
||||||
|
assertFalse(sql.contains("'test'"), "should not contain raw query value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withOnline_shouldFilterStatus() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("online", true);
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("'ON'"), "should filter for ON status");
|
||||||
|
assertFalse(sql.contains("'OFF'"), "should not filter for OFF status");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withOffline_shouldFilterStatus() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("online", false);
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("'OFF'"), "should filter for OFF status");
|
||||||
|
assertFalse(sql.contains("'ON'"), "should not filter for ON status");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withBusinessGroupId_shouldFilter() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("businessGroupId", "group-1");
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("#{businessGroupId}"), "should use bind variable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannelsByDeviceDbId_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("dataDeviceId", 99);
|
||||||
|
String sql = provider.queryChannelsByDeviceDbId(params);
|
||||||
|
assertTrue(sql.contains("#{dataDeviceId}"), "should use #{dataDeviceId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannelsByDeviceDbId_shouldFilterByDataType() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("dataDeviceId", 1);
|
||||||
|
String sql = provider.queryChannelsByDeviceDbId(params);
|
||||||
|
assertTrue(sql.contains("data_type = 1"), "should filter by GB28181 data type");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getOne_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("id", 123);
|
||||||
|
String sql = provider.getOne(params);
|
||||||
|
assertTrue(sql.contains("#{id}"), "should use #{id} bind variable");
|
||||||
|
assertTrue(sql.contains("where"), "should have WHERE clause");
|
||||||
|
assertTrue(sql.contains("#{id}"), "should have bind variable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getOneByDeviceId_shouldUseBindVariables() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("dataDeviceId", 10);
|
||||||
|
params.put("channelId", "CH999");
|
||||||
|
String sql = provider.getOneByDeviceId(params);
|
||||||
|
assertTrue(sql.contains("#{dataDeviceId}"), "should use #{dataDeviceId}");
|
||||||
|
assertTrue(sql.contains("#{channelId}"), "should use #{channelId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryByDeviceId_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("gbDeviceId", "GB-TEST-123");
|
||||||
|
String sql = provider.queryByDeviceId(params);
|
||||||
|
assertTrue(sql.contains("#{gbDeviceId}"), "should use #{gbDeviceId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryById_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("gbId", 456);
|
||||||
|
String sql = provider.queryById(params);
|
||||||
|
assertTrue(sql.contains("#{gbId}"), "should use #{gbId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryList_withQuery_shouldUseBindVariable() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("query", "search-term");
|
||||||
|
String sql = provider.queryList(params);
|
||||||
|
assertTrue(sql.contains("#{query}"), "should use #{query} bind variable");
|
||||||
|
assertFalse(sql.contains("search-term"), "should not contain raw query value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryList_withOnline_shouldFilter() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("online", true);
|
||||||
|
String sql = provider.queryList(params);
|
||||||
|
assertTrue(sql.contains("'ON'"), "should filter for ON");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryList_withHasCivilCode_shouldFilter() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("hasCivilCode", true);
|
||||||
|
String sql = provider.queryList(params);
|
||||||
|
assertTrue(sql.contains("civil_code) is not null"), "should filter for not null civil code");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryList_withHasGroup_shouldFilter() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("hasGroup", true);
|
||||||
|
String sql = provider.queryList(params);
|
||||||
|
assertTrue(sql.contains("parent_id) is not null"), "should filter for not null parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void queryChannels_withHasStream_shouldFilter() {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("hasStream", true);
|
||||||
|
String sql = provider.queryChannels(params);
|
||||||
|
assertTrue(sql.contains("stream_id IS NOT NULL"), "should filter for not null stream_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user