Compare commits

..

1 Commits

Author SHA1 Message Date
WangXuewen
14b18794bc
Pre Merge pull request !34 from WangXuewen/master 2025-05-22 13:15:21 +00:00
30 changed files with 467 additions and 702 deletions

View File

@ -98,7 +98,7 @@ public class SubscribeHolder {
for (Platform platform : platformList) { for (Platform platform : platformList) {
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platform.getServerGBId()); String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platform.getServerGBId());
if (redisTemplate.hasKey(key)) { if (redisTemplate.hasKey(key)) {
result.add(platform.getServerGBId()); result.add(platform.getServerId());
} }
} }
return result; return result;
@ -112,7 +112,7 @@ public class SubscribeHolder {
for (Platform platform : platformList) { for (Platform platform : platformList) {
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platform.getServerGBId()); String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platform.getServerGBId());
if (redisTemplate.hasKey(key)) { if (redisTemplate.hasKey(key)) {
result.add(platform.getServerGBId()); result.add(platform.getServerId());
} }
} }
return result; return result;

View File

@ -89,8 +89,8 @@ public interface PlatformMapper {
@Select("SELECT * FROM wvp_platform WHERE id=#{id}") @Select("SELECT * FROM wvp_platform WHERE id=#{id}")
Platform query(int id); Platform query(int id);
@Update("UPDATE wvp_platform SET status=#{online}, server_id = #{serverId} WHERE id=#{id}" ) @Update("UPDATE wvp_platform SET status=#{online} WHERE id=#{id}" )
int updateStatus(@Param("id") int id, @Param("online") boolean online, @Param("serverId") String serverId); int updateStatus(@Param("id") int id, @Param("online") boolean online);
@Select("SELECT server_id FROM wvp_platform WHERE enable=true and server_id != #{serverId} group by server_id") @Select("SELECT server_id FROM wvp_platform WHERE enable=true and server_id != #{serverId} group by server_id")
List<String> queryServerIdsWithEnableAndNotInServer(@Param("serverId") String serverId); List<String> queryServerIdsWithEnableAndNotInServer(@Param("serverId") String serverId);
@ -104,7 +104,7 @@ public interface PlatformMapper {
@Select("SELECT * FROM wvp_platform WHERE enable=true and server_id = #{serverId}") @Select("SELECT * FROM wvp_platform WHERE enable=true and server_id = #{serverId}")
List<Platform> queryServerIdsWithEnableAndServer(@Param("serverId") String serverId); List<Platform> queryServerIdsWithEnableAndServer(@Param("serverId") String serverId);
@Update("UPDATE wvp_platform SET status=false where server_id = #{serverId}" ) @Update("UPDATE wvp_platform SET status=false" )
void offlineAll(@Param("serverId") String serverId); void offlineAll();
} }

View File

@ -12,7 +12,6 @@ import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -27,7 +26,6 @@ import java.util.Set;
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月6日 上午11:30:50 * @date: 2020年5月6日 上午11:30:50
*/ */
@Slf4j
@Component @Component
public class EventPublisher { public class EventPublisher {
@ -74,7 +72,12 @@ public class EventPublisher {
} }
public void catalogEventPublish(Platform platform, List<CommonGBChannel> deviceChannels, String type, boolean share) { public void catalogEventPublish(Platform platform, List<CommonGBChannel> deviceChannels, String type, boolean share) {
if (platform != null && !userSetting.getServerId().equals(platform.getServerId())) { if (platform != null && !userSetting.getServerId().equals(platform.getServerId())) {
log.info("[国标级联] 目录状态推送, 此上级平台由其他服务处理,消息已经忽略"); // 指定了上级平台的推送则发送到指定的设备未指定的则全部发送 接收后各自处理自己的
CatalogEvent outEvent = new CatalogEvent(this);
outEvent.setChannels(deviceChannels);
outEvent.setType(type);
outEvent.setPlatform(platform);
redisRpcService.catalogEventPublish(platform.getServerId(), outEvent);
return; return;
} }
CatalogEvent outEvent = new CatalogEvent(this); CatalogEvent outEvent = new CatalogEvent(this);
@ -93,11 +96,12 @@ public class EventPublisher {
} }
outEvent.setChannels(channels); outEvent.setChannels(channels);
outEvent.setType(type); outEvent.setType(type);
if (platform != null) { outEvent.setPlatform(platform);
outEvent.setPlatform(platform);
}
applicationEventPublisher.publishEvent(outEvent); applicationEventPublisher.publishEvent(outEvent);
if (platform == null && share) {
// 如果没指定上级平台则推送消息到所有在线的wvp处理自己含有的平台的目录更新
redisRpcService.catalogEventPublish(null, outEvent);
}
} }
public void mobilePositionEventPublish(MobilePosition mobilePosition) { public void mobilePositionEventPublish(MobilePosition mobilePosition) {

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog; package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
@ -40,30 +39,22 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
@Autowired @Autowired
private SubscribeHolder subscribeHolder; private SubscribeHolder subscribeHolder;
@Autowired
private UserSetting userSetting;
@Override @Override
public void onApplicationEvent(CatalogEvent event) { public void onApplicationEvent(CatalogEvent event) {
SubscribeInfo subscribe = null; SubscribeInfo subscribe = null;
Platform parentPlatform = null; Platform parentPlatform = null;
log.info("[Catalog事件: {}] 通道数量: {}", event.getType(), event.getChannels().size());
Map<String, List<Platform>> platformMap = new HashMap<>(); Map<String, List<Platform>> parentPlatformMap = new HashMap<>();
Map<String, CommonGBChannel> channelMap = new HashMap<>(); Map<String, CommonGBChannel> channelMap = new HashMap<>();
if (event.getPlatform() != null) { if (event.getPlatform() != null) {
parentPlatform = event.getPlatform(); parentPlatform = event.getPlatform();
if (parentPlatform.getServerGBId() == null) {
log.info("[Catalog事件: {}] 平台服务国标编码未找到", event.getType());
return;
}
subscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); subscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
if (subscribe == null) { if (subscribe == null) {
log.info("[Catalog事件: {}] 未订阅目录事件", event.getType());
return; return;
} }
}else { }else {
List<Platform> allPlatform = platformService.queryAll(userSetting.getServerId()); List<Platform> allPlatform = platformService.queryAll();
// 获取所用订阅 // 获取所用订阅
List<String> platforms = subscribeHolder.getAllCatalogSubscribePlatform(allPlatform); List<String> platforms = subscribeHolder.getAllCatalogSubscribePlatform(allPlatform);
if (event.getChannels() != null) { if (event.getChannels() != null) {
@ -71,14 +62,10 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
for (CommonGBChannel deviceChannel : event.getChannels()) { for (CommonGBChannel deviceChannel : event.getChannels()) {
List<Platform> parentPlatformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId( List<Platform> parentPlatformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId(
deviceChannel.getGbId(), platforms); deviceChannel.getGbId(), platforms);
platformMap.put(deviceChannel.getGbDeviceId(), parentPlatformsForGB); parentPlatformMap.put(deviceChannel.getGbDeviceId(), parentPlatformsForGB);
channelMap.put(deviceChannel.getGbDeviceId(), deviceChannel); channelMap.put(deviceChannel.getGbDeviceId(), deviceChannel);
} }
}else {
log.info("[Catalog事件: {}] 未订阅目录事件", event.getType());
} }
}else {
log.info("[Catalog事件: {}] 事件内通道数为0", event.getType());
} }
} }
switch (event.getType()) { switch (event.getType()) {
@ -87,32 +74,32 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
case CatalogEvent.DEL: case CatalogEvent.DEL:
if (parentPlatform != null) { if (parentPlatform != null) {
List<CommonGBChannel> channels = new ArrayList<>(); List<CommonGBChannel> deviceChannelList = new ArrayList<>();
if (event.getChannels() != null) { if (event.getChannels() != null) {
channels.addAll(event.getChannels()); deviceChannelList.addAll(event.getChannels());
} }
if (!channels.isEmpty()) { if (!deviceChannelList.isEmpty()) {
log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), channels.size()); log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), deviceChannelList.size());
try { try {
sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, channels, subscribe, null); sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
} catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
IllegalAccessException e) { IllegalAccessException e) {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else if (!platformMap.keySet().isEmpty()) { }else if (!parentPlatformMap.keySet().isEmpty()) {
for (String serverGbId : platformMap.keySet()) { for (String gbId : parentPlatformMap.keySet()) {
List<Platform> platformList = platformMap.get(serverGbId); List<Platform> parentPlatforms = parentPlatformMap.get(gbId);
if (platformList != null && !platformList.isEmpty()) { if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
for (Platform platform : platformList) { for (Platform platform : parentPlatforms) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (subscribeInfo == null) { if (subscribeInfo == null) {
continue; continue;
} }
log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), serverGbId); log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId);
List<CommonGBChannel> deviceChannelList = new ArrayList<>(); List<CommonGBChannel> deviceChannelList = new ArrayList<>();
CommonGBChannel deviceChannel = new CommonGBChannel(); CommonGBChannel deviceChannel = new CommonGBChannel();
deviceChannel.setGbDeviceId(serverGbId); deviceChannel.setGbDeviceId(gbId);
deviceChannelList.add(deviceChannel); deviceChannelList.add(deviceChannel);
try { try {
sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null); sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
@ -121,8 +108,6 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else {
log.info("[Catalog事件: {}] 未找到上级平台: {}", event.getType(), serverGbId);
} }
} }
} }
@ -147,9 +132,9 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else if (!platformMap.keySet().isEmpty()) { }else if (!parentPlatformMap.keySet().isEmpty()) {
for (String gbId : platformMap.keySet()) { for (String gbId : parentPlatformMap.keySet()) {
List<Platform> parentPlatforms = platformMap.get(gbId); List<Platform> parentPlatforms = parentPlatformMap.get(gbId);
if (parentPlatforms != null && !parentPlatforms.isEmpty()) { if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
for (Platform platform : parentPlatforms) { for (Platform platform : parentPlatforms) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition; package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
@ -38,15 +37,12 @@ public class MobilePositionEventLister implements ApplicationListener<MobilePosi
@Autowired @Autowired
private SubscribeHolder subscribeHolder; private SubscribeHolder subscribeHolder;
@Autowired
private UserSetting userSetting;
@Override @Override
public void onApplicationEvent(MobilePositionEvent event) { public void onApplicationEvent(MobilePositionEvent event) {
if (event.getMobilePosition().getChannelId() == 0) { if (event.getMobilePosition().getChannelId() == 0) {
return; return;
} }
List<Platform> allPlatforms = platformService.queryAll(userSetting.getServerId()); List<Platform> allPlatforms = platformService.queryAll();
// 获取所用订阅 // 获取所用订阅
List<String> platforms = subscribeHolder.getAllMobilePositionSubscribePlatform(allPlatforms); List<String> platforms = subscribeHolder.getAllMobilePositionSubscribePlatform(allPlatforms);
if (platforms.isEmpty()) { if (platforms.isEmpty()) {

View File

@ -80,6 +80,6 @@ public interface IPlatformService {
void delete(Integer platformId, CommonCallback<Object> callback); void delete(Integer platformId, CommonCallback<Object> callback);
List<Platform> queryAll(String serverId); List<Platform> queryAll();
} }

View File

@ -192,7 +192,6 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
} }
sync(device); sync(device);
}else { }else {
device.setServerId(userSetting.getServerId());
if(!device.isOnLine()){ if(!device.isOnLine()){
device.setOnLine(true); device.setOnLine(true);
device.setCreateTime(now); device.setCreateTime(now);
@ -308,15 +307,15 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
return; return;
} }
for (Device device : deviceList) { for (Device device : deviceList) {
if (device == null || !device.isOnLine() || !device.getServerId().equals(userSetting.getServerId())) { if (device == null || !device.isOnLine()) {
continue; continue;
} }
if (device.getSubscribeCycleForCatalog() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) { if (device.getSubscribeCycleForCatalog() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
log.debug("[订阅丢失] 目录订阅, 编号: {}, 重新发起订阅", device.getDeviceId()); log.info("[订阅丢失] 目录订阅, 编号: {}, 重新发起订阅", device.getDeviceId());
addCatalogSubscribe(device, null); addCatalogSubscribe(device, null);
} }
if (device.getSubscribeCycleForMobilePosition() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) { if (device.getSubscribeCycleForMobilePosition() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
log.debug("[订阅丢失] 移动位置订阅, 编号: {}, 重新发起订阅", device.getDeviceId()); log.info("[订阅丢失] 移动位置订阅, 编号: {}, 重新发起订阅", device.getDeviceId());
addMobilePositionSubscribe(device, null); addMobilePositionSubscribe(device, null);
} }
} }

View File

@ -160,26 +160,30 @@ public class GbChannelServiceImpl implements IGbChannelService {
log.warn("[多个通道离线] 通道数量为0更新失败"); log.warn("[多个通道离线] 通道数量为0更新失败");
return 0; return 0;
} }
log.info("[通道离线] 共 {} 个", commonGBChannelList.size()); List<CommonGBChannel> onlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "ON");
if (onlineChannelList.isEmpty()) {
log.info("[多个通道离线] 更新失败, 参数内通道已经离线, 无需更新");
return 0;
}
int limitCount = 1000; int limitCount = 1000;
int result = 0; int result = 0;
if (commonGBChannelList.size() > limitCount) { if (onlineChannelList.size() > limitCount) {
for (int i = 0; i < commonGBChannelList.size(); i += limitCount) { for (int i = 0; i < onlineChannelList.size(); i += limitCount) {
int toIndex = i + limitCount; int toIndex = i + limitCount;
if (i + limitCount > commonGBChannelList.size()) { if (i + limitCount > onlineChannelList.size()) {
toIndex = commonGBChannelList.size(); toIndex = onlineChannelList.size();
} }
result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList.subList(i, toIndex), "OFF"); result += commonGBChannelMapper.updateStatusForListById(onlineChannelList.subList(i, toIndex), "OFF");
} }
} else { } else {
result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList, "OFF"); result += commonGBChannelMapper.updateStatusForListById(onlineChannelList, "OFF");
} }
if (result > 0) { if (result > 0) {
try { try {
// 发送catalog // 发送catalog
eventPublisher.catalogEventPublish(null, commonGBChannelList, CatalogEvent.OFF); eventPublisher.catalogEventPublish(null, onlineChannelList, CatalogEvent.OFF);
} catch (Exception e) { } catch (Exception e) {
log.warn("[多个通道离线] 发送失败,数量:{}", commonGBChannelList.size(), e); log.warn("[多个通道离线] 发送失败,数量:{}", onlineChannelList.size(), e);
} }
} }
return result; return result;
@ -210,25 +214,32 @@ public class GbChannelServiceImpl implements IGbChannelService {
log.warn("[多个通道上线] 通道数量为0更新失败"); log.warn("[多个通道上线] 通道数量为0更新失败");
return 0; return 0;
} }
List<CommonGBChannel> offlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "OFF");
if (offlineChannelList.isEmpty()) {
log.warn("[多个通道上线] 更新失败, 参数内通道已经上线");
return 0;
}
// 批量更新 // 批量更新
int limitCount = 1000; int limitCount = 1000;
int result = 0; int result = 0;
if (commonGBChannelList.size() > limitCount) { if (offlineChannelList.size() > limitCount) {
for (int i = 0; i < commonGBChannelList.size(); i += limitCount) { for (int i = 0; i < offlineChannelList.size(); i += limitCount) {
int toIndex = i + limitCount; int toIndex = i + limitCount;
if (i + limitCount > commonGBChannelList.size()) { if (i + limitCount > offlineChannelList.size()) {
toIndex = commonGBChannelList.size(); toIndex = offlineChannelList.size();
} }
result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList.subList(i, toIndex), "ON"); result += commonGBChannelMapper.updateStatusForListById(offlineChannelList.subList(i, toIndex), "ON");
} }
} else { } else {
result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList, "ON"); result += commonGBChannelMapper.updateStatusForListById(offlineChannelList, "ON");
} }
try { if (result > 0) {
// 发送catalog try {
eventPublisher.catalogEventPublish(null, commonGBChannelList, CatalogEvent.ON); // 发送catalog
} catch (Exception e) { eventPublisher.catalogEventPublish(null, offlineChannelList, CatalogEvent.ON);
log.warn("[多个通道上线] 发送失败,数量:{}", commonGBChannelList.size(), e); } catch (Exception e) {
log.warn("[多个通道上线] 发送失败,数量:{}", offlineChannelList.size(), e);
}
} }
return result; return result;

View File

@ -63,6 +63,11 @@ import java.util.concurrent.TimeUnit;
@Order(value=15) @Order(value=15)
public class PlatformServiceImpl implements IPlatformService, CommandLineRunner { public class PlatformServiceImpl implements IPlatformService, CommandLineRunner {
private final static String REGISTER_KEY_PREFIX = "platform_register_";
private final static String REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_";
private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_";
@Autowired @Autowired
private PlatformMapper platformMapper; private PlatformMapper platformMapper;
@ -128,7 +133,7 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
sendUnRegister(platform, taskInfo.getSipTransactionInfo()); sendUnRegister(platform, taskInfo.getSipTransactionInfo());
} }
// 启动时所有平台默认离线 // 启动时所有平台默认离线
platformMapper.offlineAll(userSetting.getServerId()); platformMapper.offlineAll();
} }
@Scheduled(fixedDelay = 20, timeUnit = TimeUnit.SECONDS) //每3秒执行一次 @Scheduled(fixedDelay = 20, timeUnit = TimeUnit.SECONDS) //每3秒执行一次
public void statusLostCheck(){ public void statusLostCheck(){
@ -194,7 +199,6 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
return; return;
} }
log.info("[集群] 检测到 {} 已离线", serverId); log.info("[集群] 检测到 {} 已离线", serverId);
redisCatchStorage.removeOfflineWVPInfo(serverId);
String chooseServerId = redisCatchStorage.chooseOneServer(serverId); String chooseServerId = redisCatchStorage.chooseOneServer(serverId);
if (!userSetting.getServerId().equals(chooseServerId)){ if (!userSetting.getServerId().equals(chooseServerId)){
return; return;
@ -386,7 +390,8 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L, PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L,
this::keepaliveExpire); this::keepaliveExpire);
statusTaskRunner.addKeepAliveTask(keepaliveTask); statusTaskRunner.addKeepAliveTask(keepaliveTask);
platformMapper.updateStatus(platform.getId(), true, userSetting.getServerId());
platformMapper.updateStatus(platform.getId(), true);
if (platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) { if (platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) {
if (subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) == null) { if (subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) == null) {
@ -476,7 +481,7 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
subscribeHolder.removeCatalogSubscribe(platform.getServerGBId()); subscribeHolder.removeCatalogSubscribe(platform.getServerGBId());
subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId()); subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId());
platformMapper.updateStatus(platform.getId(), false, userSetting.getServerId()); platformMapper.updateStatus(platform.getId(), false);
// 停止所有推流 // 停止所有推流
log.info("[平台离线] {}({}), 停止所有推流", platform.getName(), platform.getServerGBId()); log.info("[平台离线] {}({}), 停止所有推流", platform.getName(), platform.getServerGBId());
@ -516,6 +521,7 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
gpsMsgInfo = null; gpsMsgInfo = null;
} }
if (gpsMsgInfo == null && !userSetting.isSendPositionOnDemand()){ if (gpsMsgInfo == null && !userSetting.isSendPositionOnDemand()){
gpsMsgInfo = new GPSMsgInfo(); gpsMsgInfo = new GPSMsgInfo();
gpsMsgInfo.setId(channel.getGbDeviceId()); gpsMsgInfo.setId(channel.getGbDeviceId());
@ -864,7 +870,7 @@ public class PlatformServiceImpl implements IPlatformService, CommandLineRunner
} }
@Override @Override
public List<Platform> queryAll(String serverId) { public List<Platform> queryAll() {
return platformMapper.queryByServerId(serverId); return platformMapper.queryAll();
} }
} }

View File

@ -340,7 +340,7 @@ public class PlayServiceImpl implements IPlayService {
InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId());
if (inviteInfoInCatch != null ) { if (inviteInfoInCatch != null ) {
if (inviteInfoInCatch.getStreamInfo() == null) { if (inviteInfoInCatch.getStreamInfo() == null) {
// 释放生成的ssrc使用上一次申请的 // 释放生成的ssrc使用上一次申请的322
ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
// 点播发起了但是尚未成功, 仅注册回调等待结果即可 // 点播发起了但是尚未成功, 仅注册回调等待结果即可

View File

@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.session;
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.conf.UserSetting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -14,7 +13,6 @@ import java.util.Set;
/** /**
* ssrc使用 * ssrc使用
*/ */
@Slf4j
@Component @Component
public class SSRCFactory { public class SSRCFactory {
@ -95,7 +93,6 @@ public class SSRCFactory {
String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
Long size = redisTemplate.opsForSet().size(redisKey); Long size = redisTemplate.opsForSet().size(redisKey);
if (size == null || size == 0) { if (size == null || size == 0) {
log.info("[获取 SSRC 失败] redisKey {}", redisKey);
throw new RuntimeException("ssrc已经用完"); throw new RuntimeException("ssrc已经用完");
} else { } else {
// 在集合中移除并返回一个随机成员 // 在集合中移除并返回一个随机成员

View File

@ -143,44 +143,39 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc()); mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
} }
} }
if (sendRtpItem.getServerId().equals(userSetting.getServerId())) { MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); if (mediaServer != null) {
if (mediaServer != null) { AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId());
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId()); if (audioBroadcastCatch != null && audioBroadcastCatch.getSipTransactionInfo().getCallId().equals(callIdHeader.getCallId())) {
if (audioBroadcastCatch != null && audioBroadcastCatch.getSipTransactionInfo().getCallId().equals(callIdHeader.getCallId())) { // 来自上级平台的停止对讲
// 来自上级平台的停止对讲 log.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId());
log.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId()); audioBroadcastManager.del(sendRtpItem.getChannelId());
audioBroadcastManager.del(sendRtpItem.getChannelId()); }
}
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId); MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId);
if (mediaInfo != null && mediaInfo.getReaderCount() <= 0) { if (mediaInfo.getReaderCount() <= 0) {
log.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); log.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
Device device = deviceService.getDeviceByDeviceId(sendRtpItem.getTargetId()); Device device = deviceService.getDeviceByDeviceId(sendRtpItem.getTargetId());
if (device == null) { if (device == null) {
log.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); log.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
return; return;
} }
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpItem.getChannelId()); DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpItem.getChannelId());
if (deviceChannel == null) { if (deviceChannel == null) {
log.info("[收到bye] {} 通知设备停止推流时未找到通道信息", streamId); log.info("[收到bye] {} 通知设备停止推流时未找到通道信息", streamId);
return; return;
} }
try { try {
log.info("[停止点播] {}/{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId()); log.info("[停止点播] {}/{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId());
cmder.streamByeCmd(device, deviceChannel.getDeviceId(), sendRtpItem.getApp(), sendRtpItem.getStream(), null, null); cmder.streamByeCmd(device, deviceChannel.getDeviceId(), sendRtpItem.getApp(), sendRtpItem.getStream(), null, null);
} catch (InvalidArgumentException | ParseException | SipException | } catch (InvalidArgumentException | ParseException | SipException |
SsrcTransactionNotFoundException e) { SsrcTransactionNotFoundException e) {
log.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); log.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
}
} }
} }
} }
} else {
// TODO 流再其他wvp上时应该通知这个wvp停止推流和发送BYE
} }
} }
// 可能是设备发送的停止 // 可能是设备发送的停止

View File

@ -172,13 +172,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 点播成功 TODO 可以在此处检测cancel命令是否存在存在则不发送 // 点播成功 TODO 可以在此处检测cancel命令是否存在存在则不发送
if (userSetting.getUseCustomSsrcForParentInvite()) { if (userSetting.getUseCustomSsrcForParentInvite()) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式 // 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
MediaServer mediaServer = mediaServerService.getOne(streamInfo.getMediaServer().getId()); String ssrc = "Play".equalsIgnoreCase(inviteInfo.getSessionName())
if (mediaServer != null) {
String ssrc = "Play".equalsIgnoreCase(inviteInfo.getSessionName())
? ssrcFactory.getPlaySsrc(streamInfo.getMediaServer().getId()) ? ssrcFactory.getPlaySsrc(streamInfo.getMediaServer().getId())
: ssrcFactory.getPlayBackSsrc(streamInfo.getMediaServer().getId()); : ssrcFactory.getPlayBackSsrc(streamInfo.getMediaServer().getId());
inviteInfo.setSsrc(ssrc); inviteInfo.setSsrc(ssrc);
}
} }
// 构建sendRTP内容 // 构建sendRTP内容
SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(), SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(),

View File

@ -26,7 +26,7 @@ public interface IRedisRpcPlayService {
String frontEndCommand(String serverId, Integer channelId, int cmdCode, int parameter1, int parameter2, int combindCode2); String frontEndCommand(String serverId, Integer channelId, int cmdCode, int parameter1, int parameter2, int combindCode2);
void playPush(String serverId, Integer id, ErrorCallback<StreamInfo> callback); void playPush(Integer id, ErrorCallback<StreamInfo> callback);
StreamInfo playProxy(String serverId, int id); StreamInfo playProxy(String serverId, int id);

View File

@ -93,11 +93,11 @@ public class RedisAlarmMsgListener implements MessageListener {
log.warn("[REDIS的ALARM通知]消息解析失败"); log.warn("[REDIS的ALARM通知]消息解析失败");
continue; continue;
} }
String chanelId = alarmChannelMessage.getGbId(); String gbId = alarmChannelMessage.getGbId();
DeviceAlarm deviceAlarm = new DeviceAlarm(); DeviceAlarm deviceAlarm = new DeviceAlarm();
deviceAlarm.setCreateTime(DateUtil.getNow()); deviceAlarm.setCreateTime(DateUtil.getNow());
deviceAlarm.setChannelId(chanelId); deviceAlarm.setChannelId(gbId);
deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType()); deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
@ -106,7 +106,7 @@ public class RedisAlarmMsgListener implements MessageListener {
deviceAlarm.setLongitude(0); deviceAlarm.setLongitude(0);
deviceAlarm.setLatitude(0); deviceAlarm.setLatitude(0);
if (ObjectUtils.isEmpty(chanelId)) { if (ObjectUtils.isEmpty(gbId)) {
if (userSetting.getSendToPlatformsWhenIdLost()) { if (userSetting.getSendToPlatformsWhenIdLost()) {
// 发送给所有的上级 // 发送给所有的上级
List<Platform> parentPlatforms = platformService.queryEnablePlatformList(userSetting.getServerId()); List<Platform> parentPlatforms = platformService.queryEnablePlatformList(userSetting.getServerId());
@ -148,26 +148,24 @@ public class RedisAlarmMsgListener implements MessageListener {
} }
} else { } else {
// 获取该通道ID是属于设备还是对应的上级平台 // 获取该通道ID是属于设备还是对应的上级平台
Device device = deviceService.getDeviceBySourceChannelDeviceId(chanelId); Device device = deviceService.getDeviceBySourceChannelDeviceId(gbId);
List<Platform> platforms = platformChannelService.queryByPlatformBySharChannelId(chanelId); List<Platform> platforms = platformChannelService.queryByPlatformBySharChannelId(gbId);
if (device != null && device.getServerId().equals(userSetting.getServerId()) && (platforms == null || platforms.isEmpty())) { if (device != null && (platforms == null || platforms.isEmpty())) {
try { try {
commander.sendAlarmMessage(device, deviceAlarm); commander.sendAlarmMessage(device, deviceAlarm);
} catch (InvalidArgumentException | SipException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 发送报警: {}", e.getMessage()); log.error("[命令发送失败] 发送报警: {}", e.getMessage());
} }
} else if (device == null && (platforms != null && !platforms.isEmpty() )) { } else if (device == null && (platforms != null && !platforms.isEmpty())) {
for (Platform platform : platforms) { for (Platform platform : platforms) {
if (platform.getServerId().equals(userSetting.getServerId())) { try {
try { commanderForPlatform.sendAlarmMessage(platform, deviceAlarm);
commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); } catch (InvalidArgumentException | SipException | ParseException e) {
} catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 发送报警: {}", e.getMessage());
log.error("[命令发送失败] 发送报警: {}", e.getMessage());
}
} }
} }
} else { } else {
log.warn("[REDIS的ALARM通知] 未查询到" + chanelId + "所属的平台或设备"); log.warn("[REDIS的ALARM通知] 未查询到" + gbId + "所属的平台或设备");
} }
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -48,7 +48,7 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
@Override @Override
public void onMessage(Message message, byte[] bytes) { public void onMessage(Message message, byte[] bytes) {
log.info("[REDIS: 流设备状态变化] {}", new String(message.getBody())); log.info("[REDIS: 流设备状态变化] {}", new String(message.getBody()));
taskQueue.offer(message); taskQueue.offer(message);
} }
@ -84,13 +84,11 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
if (streamStatusMessage.getOfflineStreams() != null if (streamStatusMessage.getOfflineStreams() != null
&& !streamStatusMessage.getOfflineStreams().isEmpty()) { && !streamStatusMessage.getOfflineStreams().isEmpty()) {
// 更新部分设备离线 // 更新部分设备离线
log.info("[REDIS: 推流设备状态变化] 更新部分设备离线: {}个", streamStatusMessage.getOfflineStreams().size());
streamPushService.offline(streamStatusMessage.getOfflineStreams()); streamPushService.offline(streamStatusMessage.getOfflineStreams());
} }
if (streamStatusMessage.getOnlineStreams() != null && if (streamStatusMessage.getOnlineStreams() != null &&
!streamStatusMessage.getOnlineStreams().isEmpty()) { !streamStatusMessage.getOnlineStreams().isEmpty()) {
// 更新部分设备上线 // 更新部分设备上线
log.info("[REDIS: 推流设备状态变化] 更新部分设备上线: {}个", streamStatusMessage.getOnlineStreams().size());
streamPushService.online(streamStatusMessage.getOnlineStreams()); streamPushService.online(streamStatusMessage.getOnlineStreams());
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -181,8 +181,7 @@ public class RedisRpcStreamPushController extends RpcController {
*/ */
@RedisRpcMapping("play") @RedisRpcMapping("play")
public RedisRpcResponse play(RedisRpcRequest request) { public RedisRpcResponse play(RedisRpcRequest request) {
JSONObject paramJson = JSONObject.parseObject(request.getParam().toString()); int id = Integer.parseInt(request.getParam().toString());
int id = paramJson.getInteger("id");
RedisRpcResponse response = request.getResponse(); RedisRpcResponse response = request.getResponse();
if (id <= 0) { if (id <= 0) {
response.setStatusCode(ErrorCode.ERROR400.getCode()); response.setStatusCode(ErrorCode.ERROR400.getCode());

View File

@ -193,11 +193,8 @@ public class RedisRpcPlayServiceImpl implements IRedisRpcPlayService {
} }
@Override @Override
public void playPush(String serverId, Integer id, ErrorCallback<StreamInfo> callback) { public void playPush(Integer id, ErrorCallback<StreamInfo> callback) {
JSONObject jsonObject = new JSONObject(); RedisRpcRequest request = buildRequest("streamPush/play", id);
jsonObject.put("id", id);
RedisRpcRequest request = buildRequest("streamPush/play", jsonObject);
request.setToId(serverId);
RedisRpcResponse response = redisRpcConfig.request(request, userSetting.getPlayTimeout(), TimeUnit.SECONDS); RedisRpcResponse response = redisRpcConfig.request(request, userSetting.getPlayTimeout(), TimeUnit.SECONDS);
if (response == null) { if (response == null) {
callback.run(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg(), null); callback.run(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg(), null);

View File

@ -27,8 +27,6 @@ public interface IRedisCatchStorage {
*/ */
void updateWVPInfo(ServerInfo serverInfo, int time); void updateWVPInfo(ServerInfo serverInfo, int time);
void removeOfflineWVPInfo(String serverId);
/** /**
* 发送推流生成与推流消失消息 * 发送推流生成与推流消失消息
* @param jsonObject 消息内容 * @param jsonObject 消息内容

View File

@ -84,13 +84,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
redisTemplate.opsForZSet().add(setKey, userSetting.getServerId(), System.currentTimeMillis()); redisTemplate.opsForZSet().add(setKey, userSetting.getServerId(), System.currentTimeMillis());
} }
@Override
public void removeOfflineWVPInfo(String serverId) {
String setKey = VideoManagerConstants.WVP_SERVER_LIST;
// 首次设置就设置为0, 后续值越小说明越是最近启动的
redisTemplate.opsForZSet().remove(setKey, serverId);
}
@Override @Override
public void sendStreamChangeMsg(String type, JSONObject jsonObject) { public void sendStreamChangeMsg(String type, JSONObject jsonObject) {
String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type;

View File

@ -23,9 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map; import java.util.Map;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -193,23 +190,12 @@ public class StreamProxyController {
@ResponseBody @ResponseBody
@Operation(summary = "启用代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "启用代理", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "id", description = "代理Id", required = true) @Parameter(name = "id", description = "代理Id", required = true)
public StreamContent start(HttpServletRequest request, int id){ public StreamContent start(int id){
log.info("播放代理: {}", id); log.info("播放代理: {}", id);
StreamInfo streamInfo = streamProxyPlayService.start(id, null, null); StreamInfo streamInfo = streamProxyPlayService.start(id, null, null);
if (streamInfo == null) { if (streamInfo == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg()); throw new ControllerException(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg());
}else { }else {
if (userSetting.getUseSourceIpAsStreamIp()) {
streamInfo=streamInfo.clone();//深拷贝
String host;
try {
URL url=new URL(request.getRequestURL().toString());
host=url.getHost();
} catch (MalformedURLException e) {
host=request.getLocalAddr();
}
streamInfo.changeStreamIp(host);
}
return new StreamContent(streamInfo); return new StreamContent(streamInfo);
} }
} }

View File

@ -35,11 +35,8 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -248,7 +245,7 @@ public class StreamPushController {
@GetMapping(value = "/start") @GetMapping(value = "/start")
@ResponseBody @ResponseBody
@Operation(summary = "开始播放", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "开始播放", security = @SecurityRequirement(name = JwtUtils.HEADER))
public DeferredResult<WVPResult<StreamContent>> start(HttpServletRequest request, Integer id){ public DeferredResult<WVPResult<StreamContent>> start(Integer id){
Assert.notNull(id, "推流ID不可为NULL"); Assert.notNull(id, "推流ID不可为NULL");
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
result.onTimeout(()->{ result.onTimeout(()->{
@ -257,17 +254,6 @@ public class StreamPushController {
}); });
streamPushPlayService.start(id, (code, msg, streamInfo) -> { streamPushPlayService.start(id, (code, msg, streamInfo) -> {
if (code == 0 && streamInfo != null) { if (code == 0 && streamInfo != null) {
if (userSetting.getUseSourceIpAsStreamIp()) {
streamInfo=streamInfo.clone();//深拷贝
String host;
try {
URL url=new URL(request.getRequestURL().toString());
host=url.getHost();
} catch (MalformedURLException e) {
host=request.getLocalAddr();
}
streamInfo.changeStreamIp(host);
}
WVPResult<StreamContent> success = WVPResult.success(new StreamContent(streamInfo)); WVPResult<StreamContent> success = WVPResult.success(new StreamContent(streamInfo));
result.setResult(success); result.setResult(success);
} }

View File

@ -91,7 +91,7 @@ public interface StreamPushMapper {
"(#{item.app}, #{item.stream}) " + "(#{item.app}, #{item.stream}) " +
"</foreach>" + "</foreach>" +
")</script>") ")</script>")
List<StreamPush> getListInList(List<StreamPushItemFromRedis> offlineStreams); List<StreamPush> getListFromRedis(List<StreamPushItemFromRedis> offlineStreams);
@Select("SELECT CONCAT(app,stream) from wvp_stream_push") @Select("SELECT CONCAT(app,stream) from wvp_stream_push")

View File

@ -58,7 +58,7 @@ public class StreamPushPlayServiceImpl implements IStreamPushPlayService {
Assert.notNull(streamPush, "推流信息未找到"); Assert.notNull(streamPush, "推流信息未找到");
if (streamPush.isPushing() && !userSetting.getServerId().equals(streamPush.getServerId())) { if (streamPush.isPushing() && !userSetting.getServerId().equals(streamPush.getServerId())) {
redisRpcPlayService.playPush(streamPush.getServerId(), id, callback); redisRpcPlayService.playPush(id, callback);
return; return;
} }

View File

@ -458,27 +458,16 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Override @Override
public void offline(List<StreamPushItemFromRedis> offlineStreams) { public void offline(List<StreamPushItemFromRedis> offlineStreams) {
// 更新部分设备离线 // 更新部分设备离线
List<StreamPush> streamPushList = streamPushMapper.getListInList(offlineStreams); List<StreamPush> streamPushList = streamPushMapper.getListFromRedis(offlineStreams);
if (streamPushList.isEmpty()) {
log.info("[推流设备] 设备离线操作未发现可操作数据。");
return;
}
List<CommonGBChannel> commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList); List<CommonGBChannel> commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList);
gbChannelService.offline(commonGBChannelList); gbChannelService.offline(commonGBChannelList);
} }
@Override @Override
public void online(List<StreamPushItemFromRedis> onlineStreams) { public void online(List<StreamPushItemFromRedis> onlineStreams) {
if (onlineStreams.isEmpty()) {
log.info("[设备上线] 推流设备列表为空");
return;
}
// 更新部分设备上线streamPushService // 更新部分设备上线streamPushService
List<StreamPush> streamPushList = streamPushMapper.getListInList(onlineStreams); List<StreamPush> streamPushList = streamPushMapper.getListFromRedis(onlineStreams);
if (streamPushList.isEmpty()) { if (streamPushList.isEmpty()) {
for (StreamPushItemFromRedis onlineStream : onlineStreams) {
log.info("[设备上线] 未查询到这些通道: {}/{}", onlineStream.getApp(), onlineStream.getStream());
}
return; return;
} }
List<CommonGBChannel> commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList); List<CommonGBChannel> commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList);

View File

@ -1,48 +1,198 @@
module.exports = { module.exports = {
root: true, root: true,
env: {
node: true,
browser: true,
},
extends: ["plugin:vue/essential", "eslint:recommended"],
parserOptions: { parserOptions: {
parser: "babel-eslint", parser: 'babel-eslint',
sourceType: 'module'
}, },
env: {
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: { rules: {
// Disable or downgrade problematic rules "vue/max-attributes-per-line": [2, {
"vue/require-prop-types": "off", "singleline": 10,
"vue/require-default-prop": "off", "multiline": {
"vue/no-unused-vars": "warn", "max": 1,
"no-unused-vars": "warn", "allowFirstLine": false
"no-undef": "warn", }
eqeqeq: "warn", }],
"no-return-assign": "warn", "vue/singleline-html-element-content-newline": "off",
"new-cap": "warn", "vue/multiline-html-element-content-newline":"off",
"vue/html-self-closing": "off", "vue/name-property-casing": ["error", "PascalCase"],
"vue/html-closing-bracket-spacing": "off", "vue/no-v-html": "off",
"vue/this-in-template": "off", 'accessor-pairs': 2,
"vue/require-v-for-key": "warn", 'arrow-spacing': [2, {
"vue/valid-v-model": "warn", 'before': true,
"vue/attributes-order": "off", 'after': true
"no-multiple-empty-lines": "warn", }],
'block-spacing': [2, 'always'],
// Style rules - make them warnings instead of errors 'brace-style': [2, '1tbs', {
quotes: ["warn", "single"], 'allowSingleLine': true
"comma-dangle": ["warn", "never"], }],
"space-in-parens": "warn", 'camelcase': [0, {
"comma-spacing": "warn", 'properties': 'always'
"object-curly-spacing": "warn", }],
"arrow-spacing": "warn", 'comma-dangle': [2, 'never'],
semi: ["warn", "never"], 'comma-spacing': [2, {
"no-multi-spaces": "warn", 'before': false,
'after': true
// Turn off console warnings for development }],
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 'comma-style': [2, 'last'],
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 'constructor-super': 2,
}, 'curly': [2, 'multi-line'],
globals: { 'dot-location': [2, 'property'],
// Define global variables to prevent 'undefined' errors 'eol-last': 2,
ZLMRTCClient: "readonly", 'eqeqeq': ["error", "always", {"null": "ignore"}],
jessibuca: "readonly", 'generator-star-spacing': [2, {
}, 'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
} }

View File

@ -1,8 +1,8 @@
<template> <template>
<div id="DeviceTree" class="device-tree-container"> <div id="DeviceTree" style="width: 100%;height: 100%; background-color: #FFFFFF; overflow: auto; padding: 30px">
<div class="device-tree-header"> <div style="height: 30px; display: grid; grid-template-columns: auto auto">
<div class="header-title">通道列表</div> <div>通道列表</div>
<div class="header-switch"> <div>
<el-switch <el-switch
v-model="showRegion" v-model="showRegion"
active-color="#13ce66" active-color="#13ce66"
@ -12,27 +12,9 @@
/> />
</div> </div>
</div> </div>
<div class="tree-content"> <div>
<div class="tree-wrapper"> <RegionTree v-if="showRegion" ref="regionTree" :edit="false" :show-header="false" :has-channel="true" :click-event="treeNodeClickEvent" />
<RegionTree <GroupTree v-if="!showRegion" ref="groupTree" :edit="false" :show-header="false" :has-channel="true" :click-event="treeNodeClickEvent" />
v-if="showRegion"
ref="regionTree"
:edit="false"
:show-header="false"
:has-channel="true"
:click-event="treeNodeClickEvent"
:default-expanded-keys="[]"
/>
<GroupTree
v-if="!showRegion"
ref="groupTree"
:edit="false"
:show-header="false"
:has-channel="true"
:click-event="treeNodeClickEvent"
:default-expanded-keys="[]"
/>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -44,24 +26,7 @@ import GroupTree from './GroupTree.vue'
export default { export default {
name: 'DeviceTree', name: 'DeviceTree',
components: { GroupTree, RegionTree }, components: { GroupTree, RegionTree },
props: { props: ['device', 'onlyCatalog', 'clickEvent', 'contextMenuEvent'],
device: {
type: Object,
default: () => ({})
},
onlyCatalog: {
type: Boolean,
default: false
},
clickEvent: {
type: Function,
default: null
},
contextMenuEvent: {
type: Function,
default: null
}
},
data() { data() {
return { return {
showRegion: true, showRegion: true,
@ -72,67 +37,15 @@ export default {
} }
} }
}, },
mounted() { destroyed() {
// Apply fix for Element UI tree scrollbars after component is mounted // if (this.jessibuca) {
this.$nextTick(() => { // this.jessibuca.destroy();
this.fixTreeScrollbars() // }
this.adjustTreeHeight() // this.playing = false;
// this.loaded = false;
// Add resize event listener to handle window resizing // this.performance = "";
window.addEventListener('resize', this.adjustTreeHeight)
})
},
updated() {
// Re-apply fix when component updates (e.g., when switching between RegionTree and GroupTree)
this.$nextTick(() => {
this.fixTreeScrollbars()
this.adjustTreeHeight()
})
},
beforeDestroy() {
// Remove event listener when component is destroyed
window.removeEventListener('resize', this.adjustTreeHeight)
}, },
methods: { methods: {
adjustTreeHeight() {
// Get the container height
const containerHeight = this.$el.clientHeight
// Get the header height
const headerHeight = this.$el.querySelector('.device-tree-header').clientHeight
// Calculate available height for tree
const availableHeight = containerHeight - headerHeight - 30 // 30px for padding
// Set the tree content height
const treeContent = this.$el.querySelector('.tree-content')
if (treeContent) {
treeContent.style.height = `${availableHeight}px`
}
// Ensure tree components adapt to the available height
const treeComponents = this.$el.querySelectorAll('.el-tree')
treeComponents.forEach(tree => {
tree.style.height = '100%'
tree.style.maxHeight = '100%'
})
},
fixTreeScrollbars() {
// Find all el-tree elements within this component and fix their scrolling behavior
const trees = this.$el.querySelectorAll('.el-tree')
trees.forEach(tree => {
tree.style.overflow = 'visible'
tree.style.width = '100%'
// Also fix any scrollable containers within the tree
const scrollContainers = tree.querySelectorAll('[style*="overflow"]')
scrollContainers.forEach(container => {
if (container.style.overflow === 'auto' || container.style.overflow === 'scroll') {
container.style.overflow = 'visible'
}
})
})
},
handleClick: function(tab, event) { handleClick: function(tab, event) {
}, },
treeNodeClickEvent: function(data) { treeNodeClickEvent: function(data) {
@ -149,130 +62,13 @@ export default {
</script> </script>
<style> <style>
.device-tree-container { .device-tree-main-box{
width: 100%;
height: 100%;
background-color: #FFFFFF;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 15px;
overflow: hidden !important; /* Force no overflow on container */
}
.device-tree-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
min-height: 30px;
}
.header-title {
font-size: 16px;
font-weight: 500;
}
.tree-content {
flex: 1;
overflow: hidden !important;
width: 100%;
margin: 0;
padding: 0;
position: relative;
}
.tree-wrapper {
width: 100%;
height: 100%;
min-width: 0; /* Prevent flex items from overflowing */
position: relative;
}
/* Global fixes for Element UI tree components */
.el-tree {
overflow: visible !important;
width: 100% !important;
min-width: 0 !important;
height: 100% !important;
}
.el-tree-node {
width: 100% !important;
min-width: 0 !important;
}
.el-tree-node__content {
width: 100% !important;
min-width: 0 !important;
}
.el-tree-node__label {
word-break: break-word !important;
white-space: normal !important;
}
/* Fix for any scrollable containers */
[style*="overflow: auto"],
[style*="overflow:auto"],
[style*="overflow: scroll"],
[style*="overflow:scroll"] {
overflow: visible !important;
}
/* Make sure tree nodes are fully visible */
.el-tree-node__children {
overflow: visible !important;
}
/* Ensure tree nodes can be expanded/collapsed */
.el-tree-node__expand-icon {
cursor: pointer;
}
.device-tree-main-box {
text-align: left; text-align: left;
} }
.device-online{
.device-online {
color: #252525; color: #252525;
} }
.device-offline{
.device-offline {
color: #727272; color: #727272;
} }
/* Responsive adjustments */
@media (max-width: 768px) {
.device-tree-container {
padding: 10px;
}
.device-tree-header {
flex-direction: column;
align-items: flex-start;
}
.header-switch {
width: 100%;
margin-top: 5px;
}
}
@media (max-width: 480px) {
.device-tree-container {
padding: 8px;
}
.header-title {
font-size: 14px;
}
/* Adjust el-switch text size for mobile */
.el-switch__label {
font-size: 12px;
}
}
</style> </style>

View File

@ -1,26 +1,25 @@
<template> <template>
<div id="live" class="live-container"> <div id="live" style="height: calc(100vh - 124px)">
<div v-loading="loading" class="live-content" element-loading-text="拼命加载中"> <div v-loading="loading" style="height: 100%; display: grid; grid-template-columns: 400px auto" element-loading-text="拼命加载中">
<div class="device-tree-container"> <div style="background-color: #ffffff">
<DeviceTree :click-event="clickEvent" :context-menu-event="contextMenuEvent" /> <DeviceTree :click-event="clickEvent" :context-menu-event="contextMenuEvent" />
</div> </div>
<div class="video-container"> <div style="display: grid; grid-template-rows: 5vh auto">
<div class="control-bar"> <div style="font-size: 17px;line-height:5vh; display: grid; grid-template-columns: 1fr 1fr">
<div class="split-controls"> <div style="text-align: left">
分屏: 分屏:
<i class="iconfont icon-a-mti-1fenpingshi btn" :class="{active:spiltIndex === 0}" @click="spiltIndex=0" /> <i class="iconfont icon-a-mti-1fenpingshi btn" :class="{active:spiltIndex === 0}" @click="spiltIndex=0" />
<i class="iconfont icon-a-mti-4fenpingshi btn" :class="{active: spiltIndex === 1}" @click="spiltIndex=1" /> <i class="iconfont icon-a-mti-4fenpingshi btn" :class="{active: spiltIndex === 1}" @click="spiltIndex=1" />
<i class="iconfont icon-a-mti-6fenpingshi btn" :class="{active: spiltIndex === 2}" @click="spiltIndex=2" /> <i class="iconfont icon-a-mti-6fenpingshi btn" :class="{active: spiltIndex === 2}" @click="spiltIndex=2" />
<i class="iconfont icon-a-mti-9fenpingshi btn" :class="{active: spiltIndex === 3}" @click="spiltIndex=3" /> <i class="iconfont icon-a-mti-9fenpingshi btn" :class="{active: spiltIndex === 3}" @click="spiltIndex=3" />
</div> </div>
<div class="fullscreen-control"> <div style="text-align: right; margin-right: 10px;">
<i class="el-icon-full-screen btn" @click="fullScreen()" /> <i class="el-icon-full-screen btn" @click="fullScreen()" />
</div> </div>
</div> </div>
<div class="player-container"> <div style="padding: 0; margin: 0 auto;">
<div <div
ref="playBox" ref="playBox"
class="play-grid"
:style="liveStyle" :style="liveStyle"
> >
<div <div
@ -30,7 +29,7 @@
:class="getPlayerClass(spiltIndex, i)" :class="getPlayerClass(spiltIndex, i)"
@click="playerIdx = (i-1)" @click="playerIdx = (i-1)"
> >
<div v-if="!videoUrl[i-1]" class="no-signal">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div> <div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 15px;font-weight: bold;">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div>
<player <player
v-else v-else
:ref="'player'[i-1]" :ref="'player'[i-1]"
@ -112,13 +111,21 @@ export default {
computed: { computed: {
liveStyle() { liveStyle() {
return { if (!this.$store.getters.sidebar.opened) {
display: 'grid', return { width: '151vh', height: '85vh', display: 'grid', gridTemplateColumns: this.layout[this.spiltIndex].columns,
gridTemplateColumns: this.layout[this.spiltIndex].columns, gridTemplateRows: this.layout[this.spiltIndex].rows, gap: '4px', backgroundColor: '#a9a8a8' }
gridTemplateRows: this.layout[this.spiltIndex].rows, } else {
gap: '4px', return { width: '140vh', height: '79vh', display: 'grid', gridTemplateColumns: this.layout[this.spiltIndex].columns,
backgroundColor: '#a9a8a8' gridTemplateRows: this.layout[this.spiltIndex].rows, gap: '4px', backgroundColor: '#a9a8a8' }
} }
// this.$nextTick(() => {
// for (let i = 0; i < this.spilt; i++) {
// const player = this.$refs.player
// player && player[i] && player[i].updatePlayerDomSize()
// }
// })
// return style
} }
}, },
watch: { watch: {
@ -142,37 +149,15 @@ export default {
'$route.fullPath': 'checkPlayByParam' '$route.fullPath': 'checkPlayByParam'
}, },
mounted() { mounted() {
// Add window resize event listener to handle responsive behavior
window.addEventListener('resize', this.handleResize)
this.handleResize()
}, },
created() { created() {
this.checkPlayByParam() this.checkPlayByParam()
}, },
destroyed() { destroyed() {
clearTimeout(this.updateLooper) clearTimeout(this.updateLooper)
// Remove event listener when component is destroyed
window.removeEventListener('resize', this.handleResize)
}, },
methods: { methods: {
handleResize() {
// Force update to recalculate responsive layout
this.$forceUpdate()
// Resize any active players
this.$nextTick(() => {
for (let i = 0; i < this.layout[this.spiltIndex].spilt; i++) {
const playerRef = this.$refs[`player${i + 1}`]
if (playerRef) {
if (playerRef instanceof Array) {
playerRef[0].resize && playerRef[0].resize()
} else {
playerRef.resize && playerRef.resize()
}
}
}
})
},
destroy(idx) { destroy(idx) {
console.log(idx) console.log(idx)
this.clear(idx.substring(idx.length - 1)) this.clear(idx.substring(idx.length - 1))
@ -227,6 +212,8 @@ export default {
} }
}, },
shot(e) { shot(e) {
// console.log(e)
// send({code:'image',data:e})
var base64ToBlob = function(code) { var base64ToBlob = function(code) {
const parts = code.split(';base64,') const parts = code.split(';base64,')
const contentType = parts[0].split(':')[1] const contentType = parts[0].split(':')[1]
@ -270,86 +257,9 @@ export default {
} }
</script> </script>
<style> <style>
.live-container {
height: calc(100vh - 124px);
width: 100%;
}
.live-content {
height: 100%;
display: flex;
flex-direction: row;
}
.device-tree-container {
width: 300px;
min-width: 250px;
max-width: 400px;
background-color: #ffffff;
overflow: auto;
resize: horizontal;
}
@media (max-width: 768px) {
.live-content {
flex-direction: column;
}
.device-tree-container {
width: 100%;
max-width: 100%;
height: 200px;
min-height: 150px;
max-height: 300px;
resize: vertical;
}
}
.video-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.control-bar {
height: 5vh;
min-height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 17px;
}
.split-controls {
text-align: left;
padding-left: 10px;
}
.fullscreen-control {
text-align: right;
padding-right: 10px;
}
.player-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
overflow: hidden;
}
.play-grid {
width: 100%;
height: 100%;
max-height: calc(100vh - 180px);
aspect-ratio: 16/9;
}
.btn { .btn {
margin: 0 10px; margin: 0 10px;
cursor: pointer;
} }
.btn:hover { .btn:hover {
@ -358,6 +268,7 @@ export default {
.btn.active { .btn.active {
color: #409EFF; color: #409EFF;
} }
.redborder { .redborder {
@ -369,39 +280,13 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden;
} }
.no-signal {
color: #ffffff;
font-size: 15px;
font-weight: bold;
}
.play-box-2-1 { .play-box-2-1 {
grid-column: 1 / span 2; grid-column: 1 / span 2;
grid-row: 1 / span 2; grid-row: 1 / span 2;
} }
</style>
/* Responsive adjustments for smaller screens */ <style>
@media (max-width: 576px) {
.control-bar {
flex-direction: column;
height: auto;
padding: 5px 0;
}
.split-controls, .fullscreen-control {
width: 100%;
text-align: center;
padding: 5px 0;
}
.btn {
margin: 0 5px;
}
}
.videoList { .videoList {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -1,11 +1,12 @@
const path = require("path") 'use strict'
const defaultSettings = require("./src/settings.js") const path = require('path')
const defaultSettings = require('./src/settings.js')
function resolve(dir) { function resolve(dir) {
return path.join(__dirname, dir) return path.join(__dirname, dir)
} }
const name = defaultSettings.title || "WVP视频平台" // page title const name = defaultSettings.title || 'WVP视频平台' // page title
// If your port is set to 80, // If your port is set to 80,
// use administrator privileges to execute the command line. // use administrator privileges to execute the command line.
@ -23,37 +24,37 @@ module.exports = {
* In most cases please use '/' !!! * In most cases please use '/' !!!
* Detail: https://cli.vuejs.org/config/#publicpath * Detail: https://cli.vuejs.org/config/#publicpath
*/ */
publicPath: "/", publicPath: '/',
outputDir: "../src/main/resources/static", outputDir: '../src/main/resources/static',
assetsDir: "static", assetsDir: 'static',
lintOnSave: false, // Disable ESLint to avoid warnings lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false, productionSourceMap: false,
devServer: { devServer: {
public: "localhost:" + port, public: 'localhost:' + port,
host: "localhost", host: 'localhost',
port: port, port: port,
open: true, open: true,
overlay: { overlay: {
warnings: false, warnings: false,
errors: false, // Changed to false to hide ESLint errors errors: true
}, },
// before: require('./mock/mock-server.js'), // before: require('./mock/mock-server.js'),
proxy: { proxy: {
"/dev-api": { '/dev-api': {
target: "http://127.0.0.1:18080", target: 'http://127.0.0.1:18080',
changeOrigin: true, changeOrigin: true,
pathRewrite: { pathRewrite: {
"^/dev-api": "/", '^/dev-api': '/'
}, }
}, },
"/static/snap": { '/static/snap': {
target: "http://127.0.0.1:18080", target: 'http://127.0.0.1:18080',
changeOrigin: true, changeOrigin: true
// pathRewrite: { // pathRewrite: {
// '^/static/snap': '/static/snap' // '^/static/snap': '/static/snap'
// } // }
}, }
}, }
}, },
configureWebpack: { configureWebpack: {
// provide the app's title in webpack's name field, so that // provide the app's title in webpack's name field, so that
@ -61,75 +62,80 @@ module.exports = {
name: name, name: name,
resolve: { resolve: {
alias: { alias: {
"@": resolve("src"), '@': resolve('src')
}, }
}, }
}, },
chainWebpack(config) { chainWebpack(config) {
// it can improve the speed of the first screen, it is recommended to turn on preload // it can improve the speed of the first screen, it is recommended to turn on preload
config.plugin("preload").tap(() => [ config.plugin('preload').tap(() => [
{ {
rel: "preload", rel: 'preload',
// to ignore runtime.js // to ignore runtime.js
// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171 // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/], fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: "initial", include: 'initial'
}, }
]) ])
// when there are many pages, it will cause too many meaningless requests // when there are many pages, it will cause too many meaningless requests
config.plugins.delete("prefetch") config.plugins.delete('prefetch')
// set svg-sprite-loader // set svg-sprite-loader
config.module.rule("svg").exclude.add(resolve("src/icons")).end()
config.module config.module
.rule("icons") .rule('svg')
.test(/\.svg$/) .exclude.add(resolve('src/icons'))
.include.add(resolve("src/icons"))
.end() .end()
.use("svg-sprite-loader") config.module
.loader("svg-sprite-loader") .rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({ .options({
symbolId: "icon-[name]", symbolId: 'icon-[name]'
}) })
.end() .end()
config.when(process.env.NODE_ENV !== "development", (config) => { config
config .when(process.env.NODE_ENV !== 'development',
.plugin("ScriptExtHtmlWebpackPlugin") config => {
.after("html") config
.use("script-ext-html-webpack-plugin", [ .plugin('ScriptExtHtmlWebpackPlugin')
{ .after('html')
.use('script-ext-html-webpack-plugin', [{
// `runtime` must same as runtimeChunk name. default is `runtime` // `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/, inline: /runtime\..*\.js$/
}, }])
]) .end()
.end() config
config.optimization.splitChunks({ .optimization.splitChunks({
chunks: "all", chunks: 'all',
cacheGroups: { cacheGroups: {
libs: { libs: {
name: "chunk-libs", name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/, test: /[\\/]node_modules[\\/]/,
priority: 10, priority: 10,
chunks: "initial", // only package third parties that are initially dependent chunks: 'initial' // only package third parties that are initially dependent
}, },
elementUI: { elementUI: {
name: "chunk-elementUI", // split elementUI into a single package name: 'chunk-elementUI', // split elementUI into a single package
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
}, },
commons: { commons: {
name: "chunk-commons", name: 'chunk-commons',
test: resolve("src/components"), // can customize your rules test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number minChunks: 3, // minimum common number
priority: 5, priority: 5,
reuseExistingChunk: true, reuseExistingChunk: true
}, }
}, }
}) })
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
config.optimization.runtimeChunk("single") config.optimization.runtimeChunk('single')
}) }
}, )
}
} }

View File

@ -89,21 +89,15 @@ Vue.prototype.$channelTypeList = {
new Vue({ new Vue({
beforeCreate: function () { beforeCreate: function () {
// 获取本平台的服务ID // 获取本平台的服务ID
console.log("获取本平台的服务ID") axios({
if (!this.$myServerId) { method: 'get',
axios({ url: `/api/server/system/configInfo`,
method: 'get', }).then( (res)=> {
url: `/api/server/system/configInfo`, if (res.data.code === 0) {
}).then( (res)=> { Vue.prototype.$myServerId = res.data.data.addOn.serverId;
if (res.data.code === 0) { }
console.log(res.data) }).catch( (error)=> {
console.log("当前服务ID " + res.data.data.addOn.serverId) });
Vue.prototype.$myServerId = res.data.data.addOn.serverId;
}
}).catch( (error)=> {
});
}
}, },
router: router, router: router,
render: h => h(App), render: h => h(App),