Merge branch 'master' into dev/1078-newUI
# Conflicts: # src/main/resources/application.yml
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
@Data
|
||||
public class StatisticsInfo {
|
||||
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 分支
|
||||
*/
|
||||
private String branch;
|
||||
|
||||
/**
|
||||
* git提交版本ID
|
||||
*/
|
||||
private String gitCommitId;
|
||||
|
||||
/**
|
||||
* git地址
|
||||
*/
|
||||
private String gitUrl;
|
||||
|
||||
/**
|
||||
* 构建版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 操作系统名称
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 是否是docker环境
|
||||
*/
|
||||
private Boolean docker;
|
||||
|
||||
/**
|
||||
* 架构
|
||||
*/
|
||||
private String arch;
|
||||
|
||||
/**
|
||||
* jdk版本
|
||||
*/
|
||||
private String jdkVersion;
|
||||
|
||||
/**
|
||||
* redis版本
|
||||
*/
|
||||
private String redisVersion;
|
||||
|
||||
/**
|
||||
* sql数据库版本
|
||||
*/
|
||||
private String sqlVersion;
|
||||
|
||||
/**
|
||||
* sql数据库类型, mysql/postgresql/金仓等
|
||||
*/
|
||||
private String sqlType;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String time;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatisticsInfo{" +
|
||||
"id=" + id +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
", branch='" + branch + '\'' +
|
||||
", gitCommitId='" + gitCommitId + '\'' +
|
||||
", gitUrl='" + gitUrl + '\'' +
|
||||
", version='" + version + '\'' +
|
||||
", osName='" + osName + '\'' +
|
||||
", docker=" + docker +
|
||||
", arch='" + arch + '\'' +
|
||||
", jdkVersion='" + jdkVersion + '\'' +
|
||||
", redisVersion='" + redisVersion + '\'' +
|
||||
", sqlVersion='" + sqlVersion + '\'' +
|
||||
", sqlType='" + sqlType + '\'' +
|
||||
", time='" + time + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.nio.file.Files;
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Order(value=14)
|
||||
@Order(value=15)
|
||||
public class CivilCodeFileConf implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
@@ -59,11 +60,14 @@ public class CloudRecordTimer {
|
||||
// TODO 后续可以删除空了的过期日期文件夹
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
}
|
||||
try {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
}
|
||||
}catch (ControllerException ignored) {}
|
||||
|
||||
}
|
||||
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ public class SipConfig {
|
||||
private String id;
|
||||
|
||||
private String password;
|
||||
|
||||
|
||||
Integer ptzSpeed = 50;
|
||||
|
||||
Integer registerTimeInterval = 120;
|
||||
|
||||
private boolean alarm = false;
|
||||
|
||||
private long timeout = 500;
|
||||
private long timeout = 1000;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class SpringDocConfig {
|
||||
Contact contact = new Contact();
|
||||
contact.setName("pan");
|
||||
contact.setEmail("648540858@qq.com");
|
||||
|
||||
return new OpenAPI()
|
||||
.components(new Components()
|
||||
.addSecuritySchemes(JwtUtils.HEADER, new SecurityScheme()
|
||||
@@ -37,7 +38,11 @@ public class SpringDocConfig {
|
||||
.bearerFormat("JWT")))
|
||||
.info(new Info().title("WVP-PRO 接口文档")
|
||||
.contact(contact)
|
||||
.description("开箱即用的28181协议视频平台")
|
||||
.description("开箱即用的28181协议视频平台。 <br/>" +
|
||||
"1. 打开http://127.0.0.1:18080/doc.html#/1.%20全部/用户管理/login_1" +
|
||||
" 登录成功后返回AccessToken。 <br/>" +
|
||||
"2. 填写到AccessToken到参数值 http://127.0.0.1:18080/doc.html#/Authorize/1.%20全部 <br/>" +
|
||||
"后续接口就可以直接测试了")
|
||||
.version("v3.1.0")
|
||||
.license(new License().name("Apache 2.0").url("http://springdoc.org")));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.StatisticsInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
@Order(value=100)
|
||||
@Slf4j
|
||||
public class StatisticsInfoTask implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private GitUtil gitUtil;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
try {
|
||||
StatisticsInfo statisticsInfo = new StatisticsInfo();
|
||||
statisticsInfo.setDeviceId(SystemInfoUtils.getHardwareId());
|
||||
statisticsInfo.setBranch(gitUtil.getBranch());
|
||||
statisticsInfo.setGitCommitId(gitUtil.getGitCommitId());
|
||||
statisticsInfo.setGitUrl(gitUtil.getGitUrl());
|
||||
statisticsInfo.setVersion(gitUtil.getBuildVersion());
|
||||
|
||||
statisticsInfo.setOsName(System.getProperty("os.name"));
|
||||
statisticsInfo.setArch(System.getProperty("os.arch"));
|
||||
statisticsInfo.setJdkVersion(System.getProperty("java.version"));
|
||||
|
||||
statisticsInfo.setDocker(new File("/.dockerenv").exists());
|
||||
try {
|
||||
statisticsInfo.setRedisVersion(getRedisVersion());
|
||||
}catch (Exception ignored) {}
|
||||
try {
|
||||
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
|
||||
statisticsInfo.setSqlVersion(metaData.getDatabaseProductVersion());
|
||||
statisticsInfo.setSqlType(metaData.getDriverName());
|
||||
}catch (Exception ignored) {}
|
||||
statisticsInfo.setTime(DateUtil.getNow());
|
||||
sendPost(statisticsInfo);
|
||||
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error("[获取信息失败] ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedisVersion() {
|
||||
if (redisTemplate.getConnectionFactory() == null) {
|
||||
return null;
|
||||
}
|
||||
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
|
||||
if (connection.info() == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.info().getProperty("redis_version");
|
||||
}
|
||||
|
||||
public void sendPost(StatisticsInfo statisticsInfo) {
|
||||
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
|
||||
OkHttpClient client = httpClientBuilder.build();
|
||||
|
||||
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(statisticsInfo));
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.post(requestBodyJson)
|
||||
.url("http://api.wvp-pro.cn:136/api/statistics/ping")
|
||||
// .url("http://127.0.0.1:11236/api/statistics/ping")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
response.close();
|
||||
Objects.requireNonNull(response.body()).close();
|
||||
|
||||
}catch (Exception ignored){}
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public class UserSetting {
|
||||
/**
|
||||
* 登录超时时间(分钟),
|
||||
*/
|
||||
private long loginTimeout = 30;
|
||||
private long loginTimeout = 60;
|
||||
|
||||
/**
|
||||
* jwk文件路径,若不指定则使用resources目录下的jwk.json
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.message.Response;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -97,7 +97,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
// return;
|
||||
default:
|
||||
}
|
||||
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
User user = new User();
|
||||
user.setId(jwtUser.getUserId());
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
@@ -24,7 +23,6 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -58,33 +56,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
|
||||
**/
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
if (userSetting.getInterfaceAuthentication()) {
|
||||
ArrayList<String> matchers = new ArrayList<>();
|
||||
matchers.add("/");
|
||||
matchers.add("/#/**");
|
||||
matchers.add("/static/**");
|
||||
matchers.add("/swagger-ui.html");
|
||||
matchers.add("/swagger-ui/");
|
||||
matchers.add("/index.html");
|
||||
matchers.add("/doc.html");
|
||||
matchers.add("/webjars/**");
|
||||
matchers.add("/swagger-resources/**");
|
||||
matchers.add("/v3/api-docs/**");
|
||||
matchers.add("/js/**");
|
||||
matchers.add("/api/device/query/snap/**");
|
||||
matchers.add("/record_proxy/*/**");
|
||||
matchers.add("/api/emit");
|
||||
matchers.add("/favicon.ico");
|
||||
// 可以直接访问的静态数据
|
||||
web.ignoring().antMatchers(matchers.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置认证方式
|
||||
*
|
||||
@@ -105,15 +76,36 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
List<String> defaultExcludes = new ArrayList<>();
|
||||
defaultExcludes.add("/");
|
||||
defaultExcludes.add("/#/**");
|
||||
defaultExcludes.add("/static/**");
|
||||
|
||||
List<String> defaultExcludes = userSetting.getInterfaceAuthenticationExcludes();
|
||||
defaultExcludes.add("/swagger-ui.html");
|
||||
defaultExcludes.add("/swagger-ui/**");
|
||||
defaultExcludes.add("/swagger-resources/**");
|
||||
defaultExcludes.add("/doc.html");
|
||||
defaultExcludes.add("/doc.html#/**");
|
||||
defaultExcludes.add("/v3/api-docs/**");
|
||||
|
||||
defaultExcludes.add("/index.html");
|
||||
defaultExcludes.add("/webjars/**");
|
||||
|
||||
defaultExcludes.add("/js/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/record_proxy/*/**");
|
||||
defaultExcludes.add("/api/emit");
|
||||
defaultExcludes.add("/favicon.ico");
|
||||
defaultExcludes.add("/api/user/login");
|
||||
defaultExcludes.add("/index/hook/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/index/hook/abl/**");
|
||||
defaultExcludes.add("/swagger-ui/**");
|
||||
defaultExcludes.add("/doc.html#/**");
|
||||
// defaultExcludes.add("/channel/log");
|
||||
|
||||
|
||||
|
||||
if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) {
|
||||
defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes());
|
||||
}
|
||||
|
||||
http.headers().contentTypeOptions().disable()
|
||||
.and().cors().configurationSource(configurationSource())
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.conf.security.dto;
|
||||
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.security.core.CredentialsContainer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
@@ -19,8 +21,13 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
*/
|
||||
private User user;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String accessToken;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
@@ -104,11 +111,4 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
return user.getPushKey();
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 业务分组
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(description = "业务分组树")
|
||||
public class GroupTree extends Group{
|
||||
|
||||
@@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 区域
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(description = "区域树")
|
||||
public class RegionTree extends Region {
|
||||
|
||||
@@ -79,7 +79,7 @@ public class DeviceQuery {
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@GetMapping("/devices/{deviceId}")
|
||||
public Device devices(@PathVariable String deviceId){
|
||||
|
||||
|
||||
return deviceService.getDeviceByDeviceId(deviceId);
|
||||
}
|
||||
|
||||
@@ -224,16 +224,19 @@ public class DeviceQuery {
|
||||
"UDP(udp传输),TCP-ACTIVE(tcp主动模式),TCP-PASSIVE(tcp被动模式)", required = true)
|
||||
@PostMapping("/transport/{deviceId}/{streamMode}")
|
||||
public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){
|
||||
Assert.isTrue(streamMode.equalsIgnoreCase("UDP")
|
||||
|| streamMode.equalsIgnoreCase("TCP-ACTIVE")
|
||||
|| streamMode.equalsIgnoreCase("TCP-PASSIVE"), "数据流传输模式, 取值:UDP/TCP-ACTIVE/TCP-PASSIVE");
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
device.setStreamMode(streamMode);
|
||||
device.setStreamMode(streamMode.toUpperCase());
|
||||
deviceService.updateCustomDevice(device);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "device", description = "设备", required = true)
|
||||
@PostMapping("/device/add/")
|
||||
public void addDevice(Device device){
|
||||
@PostMapping("/device/add")
|
||||
public void addDevice(@RequestBody Device device){
|
||||
|
||||
if (device == null || device.getDeviceId() == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
@@ -250,8 +253,8 @@ public class DeviceQuery {
|
||||
|
||||
@Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "device", description = "设备", required = true)
|
||||
@PostMapping("/device/update/")
|
||||
public void updateDevice(Device device){
|
||||
@PostMapping("/device/update")
|
||||
public void updateDevice(@RequestBody Device device){
|
||||
if (device == null || device.getDeviceId() == null || device.getId() <= 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -89,7 +88,7 @@ public class MediaController {
|
||||
}
|
||||
|
||||
if (streamInfo != null){
|
||||
return new StreamContent(streamInfo);
|
||||
return new StreamContent(streamInfo);
|
||||
}else {
|
||||
//获取流失败,重启拉流后重试一次
|
||||
streamProxyService.stopByAppAndStream(app,stream);
|
||||
|
||||
@@ -215,7 +215,7 @@ public class PlaybackController {
|
||||
|
||||
@Operation(summary = "回放倍速播放", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "streamId", description = "回放流ID", required = true)
|
||||
@Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true)
|
||||
@Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4、8", required = true)
|
||||
@GetMapping("/speed/{streamId}/{speed}")
|
||||
public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) {
|
||||
log.info("playSpeed: "+streamId+", "+speed);
|
||||
@@ -225,10 +225,6 @@ public class PlaybackController {
|
||||
log.warn("streamId不存在!");
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
|
||||
}
|
||||
if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) {
|
||||
log.warn("不支持的speed: " + speed);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)");
|
||||
}
|
||||
Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId());
|
||||
DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId());
|
||||
try {
|
||||
|
||||
@@ -381,7 +381,7 @@ public interface DeviceMapper {
|
||||
" OR device_id LIKE concat('%',#{query},'%') escape '/' " +
|
||||
" OR ip LIKE concat('%',#{query},'%') escape '/')" +
|
||||
"</if> " +
|
||||
" order by create_time desc "+
|
||||
" order by create_time desc, device_id " +
|
||||
" </script>")
|
||||
List<Device> getDeviceList(@Param("dataType") Integer dataType, @Param("query") String query, @Param("status") Boolean status);
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ public class DeviceChannelProvider {
|
||||
}
|
||||
sqlBuild.append(" )");
|
||||
}
|
||||
sqlBuild.append("ORDER BY device_id");
|
||||
return sqlBuild.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
@@ -41,9 +40,6 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.message.Response;
|
||||
@@ -117,6 +113,9 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
|
||||
@Override
|
||||
public int updateChannels(Device device, List<DeviceChannel> channels) {
|
||||
if (CollectionUtils.isEmpty(channels)) {
|
||||
return 0;
|
||||
}
|
||||
List<DeviceChannel> addChannels = new ArrayList<>();
|
||||
List<DeviceChannel> updateChannels = new ArrayList<>();
|
||||
HashMap<String, DeviceChannel> channelsInStore = new HashMap<>();
|
||||
@@ -442,7 +441,7 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
deviceMobilePositionMapper.insertNewPosition(mobilePosition);
|
||||
}
|
||||
|
||||
if (deviceChannel.getDeviceId().equals(deviceChannel.getDeviceId())) {
|
||||
if (deviceChannel.getDeviceId().equals(device.getDeviceId())) {
|
||||
deviceChannel.setDeviceId(null);
|
||||
}
|
||||
if (deviceChannel.getGpsTime() == null) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.common.InviteSessionType;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.exception.ServiceException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.common.enums.ChannelDataType;
|
||||
@@ -145,6 +146,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
|
||||
deviceChannelPlayService.play(channel, record, callback);
|
||||
} catch (PlayException e) {
|
||||
callback.run(e.getCode(), e.getMsg(), null);
|
||||
} catch (ControllerException e) {
|
||||
log.error("[点播失败] {}({}), {}", channel.getGbName(), channel.getGbDeviceId(), e.getMsg());
|
||||
callback.run(Response.BUSY_HERE, "busy here", null);
|
||||
} catch (Exception e) {
|
||||
log.error("[点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e);
|
||||
callback.run(Response.BUSY_HERE, "busy here", null);
|
||||
|
||||
@@ -669,7 +669,6 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
|
||||
mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
|
||||
sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream());
|
||||
mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
|
||||
}
|
||||
}
|
||||
}, userSetting.getPlayTimeout());
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.IReceiveRtpServerService;
|
||||
import com.genersoft.iot.vmp.service.ISendRtpServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
@@ -1047,8 +1048,8 @@ public class PlayServiceImpl implements IPlayService {
|
||||
null);
|
||||
return;
|
||||
}
|
||||
log.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}({}), SSRC校验:{}",
|
||||
device.getDeviceId(), channel.getDeviceId(), downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(),
|
||||
log.info("[录像下载] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}({}), SSRC校验:{}",
|
||||
device.getDeviceId(), channel.getDeviceId(), startTime, endTime, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(),
|
||||
ssrcInfo.getSsrc(), String.format("%08x", Long.parseLong(ssrcInfo.getSsrc())).toUpperCase(),
|
||||
device.isSsrcCheck());
|
||||
|
||||
@@ -1723,7 +1724,14 @@ public class PlayServiceImpl implements IPlayService {
|
||||
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
|
||||
}
|
||||
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId());
|
||||
play(device, deviceChannel, callback);
|
||||
|
||||
MediaServer mediaServerItem = getNewMediaServerItem(device);
|
||||
if (mediaServerItem == null) {
|
||||
log.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", device.getDeviceId(), deviceChannel.getDeviceId());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
|
||||
}
|
||||
play(mediaServerItem, device, deviceChannel, null, record, callback);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ public class MediaInfo {
|
||||
private Integer audioChannels;
|
||||
@Schema(description = "音频采样率")
|
||||
private Integer audioSampleRate;
|
||||
@Schema(description = "音频采样率")
|
||||
@Schema(description = "时长")
|
||||
private Long duration;
|
||||
@Schema(description = "在线")
|
||||
private Boolean online;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.genersoft.iot.vmp.media.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RecordInfo {
|
||||
private String fileName;
|
||||
private String filePath;
|
||||
@@ -24,70 +26,6 @@ public class RecordInfo {
|
||||
return recordInfo;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public double getTimeLen() {
|
||||
return timeLen;
|
||||
}
|
||||
|
||||
public void setTimeLen(double timeLen) {
|
||||
this.timeLen = timeLen;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecordInfo{" +
|
||||
|
||||
@@ -70,4 +70,9 @@ public interface IMediaNodeServerService {
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -159,4 +159,10 @@ public interface IMediaServerService {
|
||||
int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, boolean onlyAuto, boolean disableAudio, boolean reUsePort, Integer tcpMode);
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
StreamInfo loadMP4File(MediaServer mediaServer, String app, String stream, String datePath);
|
||||
|
||||
void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema);
|
||||
|
||||
void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema);
|
||||
}
|
||||
|
||||
@@ -311,6 +311,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) {
|
||||
ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null);
|
||||
}
|
||||
if (mediaSerItem.getSecret() != null && !mediaServerInDataBase.getSecret().equals(mediaSerItem.getSecret())) {
|
||||
mediaServerInDataBase.setSecret(mediaSerItem.getSecret());
|
||||
}
|
||||
mediaServerInDataBase.setSecret(mediaSerItem.getSecret());
|
||||
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId();
|
||||
redisTemplate.opsForHash().put(key, mediaServerInDataBase.getId(), mediaServerInDataBase);
|
||||
if (mediaServerInDataBase.isStatus()) {
|
||||
@@ -966,4 +970,35 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
mediaNodeServerService.stopProxy(mediaServer, streamKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamInfo loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[loadMP4File] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.loadMP4File(mediaServer, app, stream, datePath);
|
||||
return getStreamInfoByAppAndStream(mediaServer, app, stream, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[seekRecordStamp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
log.info("[setRecordSpeed] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
|
||||
}
|
||||
mediaNodeServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@ public class ZLMHttpHookListener {
|
||||
@ResponseBody
|
||||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
|
||||
|
||||
MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
return HookResult.SUCCESS();
|
||||
@@ -190,6 +189,8 @@ public class ZLMHttpHookListener {
|
||||
|
||||
JSONObject ret = new JSONObject();
|
||||
boolean close = mediaService.closeStreamOnNoneReader(param.getMediaServerId(), param.getApp(), param.getStream(), param.getSchema());
|
||||
log.info("[ZLM HOOK]流无人观看是否触发关闭:{}, {}->{}->{}/{}", close, param.getMediaServerId(), param.getSchema(),
|
||||
param.getApp(), param.getStream());
|
||||
ret.put("code", 0);
|
||||
ret.put("close", close);
|
||||
return ret;
|
||||
|
||||
@@ -169,7 +169,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
return true;
|
||||
}else {
|
||||
log.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject);
|
||||
return false;
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "删除磁盘文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,4 +549,37 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.loadMP4File(mediaServer, app, stream, datePath);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.seekRecordStamp(mediaServer, app, stream, stamp, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(MediaServer mediaServer, String app, String stream, Integer speed, String schema) {
|
||||
JSONObject jsonObject = zlmresTfulUtils.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
if (jsonObject == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败");
|
||||
}
|
||||
if (jsonObject.getInteger("code") != 0) {
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
private OkHttpClient client;
|
||||
|
||||
|
||||
public interface RequestCallback{
|
||||
void run(JSONObject response);
|
||||
}
|
||||
@@ -415,4 +416,34 @@ public class ZLMRESTfulUtils {
|
||||
param.put("name", fileName);
|
||||
return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
|
||||
}
|
||||
|
||||
public JSONObject loadMP4File(MediaServer mediaServer, String app, String stream, String datePath) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("file_path", datePath);
|
||||
param.put("file_repeat", "0");
|
||||
return sendPost(mediaServer, "loadMP4File",param, null);
|
||||
}
|
||||
|
||||
public JSONObject setRecordSpeed(MediaServer mediaServer, String app, String stream, int speed, String schema) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("speed", speed);
|
||||
param.put("schema", schema);
|
||||
return sendPost(mediaServer, "setRecordSpeed",param, null);
|
||||
}
|
||||
|
||||
public JSONObject seekRecordStamp(MediaServer mediaServer, String app, String stream, Double stamp, String schema) {
|
||||
Map<String, Object> param = new HashMap<>(1);
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
param.put("stamp", stamp);
|
||||
param.put("schema", schema);
|
||||
return sendPost(mediaServer, "seekRecordStamp",param, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.genersoft.iot.vmp.gb28181.service;
|
||||
package com.genersoft.iot.vmp.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 云端录像管理
|
||||
@@ -17,7 +20,7 @@ public interface ICloudRecordService {
|
||||
/**
|
||||
* 分页回去云端录像列表
|
||||
*/
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId);
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder);
|
||||
|
||||
/**
|
||||
* 获取所有的日期
|
||||
@@ -52,4 +55,15 @@ public interface ICloudRecordService {
|
||||
DownloadFileInfo getPlayUrlPath(Integer recordId);
|
||||
|
||||
List<CloudRecordItem> getAllList(String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, List<Integer> ids);
|
||||
|
||||
/**
|
||||
* 加载录像文件,形成录像流
|
||||
*/
|
||||
void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback);
|
||||
|
||||
void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema);
|
||||
|
||||
void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema);
|
||||
|
||||
void deleteFileByIds(Set<Integer> ids);
|
||||
}
|
||||
@@ -15,52 +15,52 @@ public class CloudRecordItem {
|
||||
* 主键
|
||||
*/
|
||||
private int id;
|
||||
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String app;
|
||||
|
||||
|
||||
/**
|
||||
* 流
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
|
||||
/**
|
||||
* 健全ID
|
||||
*/
|
||||
private String callId;
|
||||
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private long startTime;
|
||||
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private long endTime;
|
||||
|
||||
|
||||
/**
|
||||
* ZLM Id
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
|
||||
/**
|
||||
* 文件夹
|
||||
*/
|
||||
private String folder;
|
||||
|
||||
|
||||
/**
|
||||
* 收藏,收藏的文件不移除
|
||||
*/
|
||||
@@ -70,16 +70,16 @@ public class CloudRecordItem {
|
||||
* 保留,收藏的文件不移除
|
||||
*/
|
||||
private Boolean reserve;
|
||||
|
||||
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private long fileSize;
|
||||
|
||||
|
||||
/**
|
||||
* 文件时长
|
||||
*/
|
||||
private long timeLen;
|
||||
private double timeLen;
|
||||
|
||||
/**
|
||||
* 所属服务ID
|
||||
@@ -96,7 +96,7 @@ public class CloudRecordItem {
|
||||
cloudRecordItem.setFileSize(param.getRecordInfo().getFileSize());
|
||||
cloudRecordItem.setFilePath(param.getRecordInfo().getFilePath());
|
||||
cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
|
||||
cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
|
||||
cloudRecordItem.setTimeLen(param.getRecordInfo().getTimeLen() * 1000);
|
||||
cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
|
||||
Map<String, String> paramsMap = MediaServerUtils.urlParamToMap(param.getRecordInfo().getParams());
|
||||
if (paramsMap.get("callId") != null) {
|
||||
|
||||
@@ -2,16 +2,22 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
|
||||
@@ -28,12 +34,10 @@ import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -57,9 +61,12 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
@Autowired
|
||||
private IRedisRpcPlayService redisRpcPlayService;
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe subscribe;
|
||||
|
||||
@Override
|
||||
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime,
|
||||
String endTime, List<MediaServer> mediaServerItems, String callId) {
|
||||
String endTime, List<MediaServer> mediaServerItems, String callId, Boolean ascOrder) {
|
||||
// 开始时间和结束时间在数据库中都是以秒为单位的
|
||||
Long startTimeStamp = null;
|
||||
Long endTimeStamp = null;
|
||||
@@ -84,7 +91,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
.replaceAll("_", "/_");
|
||||
}
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, null);
|
||||
callId, mediaServerItems, null, ascOrder);
|
||||
return new PageInfo<>(all);
|
||||
}
|
||||
|
||||
@@ -100,7 +107,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
|
||||
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
|
||||
endTimeStamp, null, mediaServerItems, null);
|
||||
endTimeStamp, null, mediaServerItems, null, null);
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@@ -213,7 +220,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}
|
||||
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, null);
|
||||
callId, mediaServerItems, null, null);
|
||||
if (all.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
|
||||
}
|
||||
@@ -273,6 +280,96 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
|
||||
}
|
||||
return cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, ids);
|
||||
callId, mediaServerItems, ids, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRecord(String app, String stream, String date, ErrorCallback<StreamInfo> callback) {
|
||||
long startTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(date + " 00:00:00");
|
||||
long endTimestamp = startTimestamp + 24 * 60 * 60 * 1000;
|
||||
|
||||
List<CloudRecordItem> recordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimestamp, endTimestamp, null, null, null, false);
|
||||
if (recordItemList.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "此时间无录像");
|
||||
}
|
||||
String mediaServerId = recordItemList.get(0).getMediaServerId();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
String buildApp = "mp4_record";
|
||||
String buildStream = app + "_" + stream + "_" + date;
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, buildApp, buildStream);
|
||||
if (mediaInfo != null) {
|
||||
if (callback != null) {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, mediaInfo, null);
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, buildApp, buildStream, mediaServerId);
|
||||
subscribe.addSubscribe(hook, (hookData) -> {
|
||||
StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServer, buildApp, buildStream, hookData.getMediaInfo(), null);
|
||||
if (callback != null) {
|
||||
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||
}
|
||||
});
|
||||
String dateDir = recordItemList.get(0).getFilePath().substring(0, recordItemList.get(0).getFilePath().lastIndexOf("/"));
|
||||
mediaServerService.loadMP4File(mediaServer, buildApp, buildStream, dateDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekRecord(String mediaServerId,String app, String stream, Double seek, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.seekRecordStamp(mediaServer, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordSpeed(String mediaServerId, String app, String stream, Integer speed, String schema) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在: " + mediaServerId);
|
||||
}
|
||||
mediaServerService.setRecordSpeed(mediaServer, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteFileByIds(Set<Integer> ids) {
|
||||
log.info("[删除录像文件] ids: {}", ids.toArray());
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordByIds(ids);
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<CloudRecordItem> cloudRecordItemIdListForDelete = new ArrayList<>();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
|
||||
MediaServer mediaServer = mediaServerService.getOne(cloudRecordItem.getMediaServerId());
|
||||
try {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServer, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
cloudRecordItemIdListForDelete.add(cloudRecordItem);
|
||||
}
|
||||
}catch (ControllerException e) {
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
stringBuilder.append(cloudRecordItem.getFileName());
|
||||
}
|
||||
|
||||
}
|
||||
if (!cloudRecordItemIdListForDelete.isEmpty()) {
|
||||
cloudRecordServiceMapper.deleteList(cloudRecordItemIdListForDelete);
|
||||
}
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(" 删除失败");
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), stringBuilder.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,8 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
|
||||
|
||||
Map<Integer, StreamInfo> recordStreamMap = new HashMap<>();
|
||||
|
||||
@Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES)
|
||||
@Scheduled(fixedRate = 1, timeUnit = TimeUnit.MINUTES)
|
||||
public void execution() {
|
||||
log.info("[录制计划] 执行");
|
||||
// 查询现在需要录像的通道Id
|
||||
List<Integer> startChannelIdList = queryCurrentChannelRecord();
|
||||
|
||||
@@ -133,7 +132,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
|
||||
// 获取当前时间在一周内的序号, 数据库存储的从第几个30分钟开始, 0-47, 包括首尾
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
int week = now.getDayOfWeek().getValue();
|
||||
int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
|
||||
int index = now.getHour() * 60 + now.getMinute();
|
||||
|
||||
// 查询现在需要录像的通道Id
|
||||
return recordPlanMapper.queryRecordIng(week, index);
|
||||
@@ -221,7 +220,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
|
||||
}
|
||||
}
|
||||
// TODO 更新录像队列
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcController;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RedisRpcMapping;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface CloudRecordServiceMapper {
|
||||
@@ -55,12 +56,13 @@ public interface CloudRecordServiceMapper {
|
||||
" <if test= 'ids != null ' > and id in " +
|
||||
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
|
||||
" </if>" +
|
||||
" order by start_time desc" +
|
||||
" <if test= 'ascOrder != null and ascOrder == true'> order by start_time asc</if>" +
|
||||
" <if test= 'ascOrder == null or ascOrder == false'> order by start_time desc</if>" +
|
||||
" </script>")
|
||||
List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
|
||||
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
|
||||
@Param("callId")String callId, List<MediaServer> mediaServerItemList,
|
||||
List<Integer> ids);
|
||||
List<Integer> ids, @Param("ascOrder") Boolean ascOrder);
|
||||
|
||||
|
||||
@Select(" <script>" +
|
||||
@@ -124,4 +126,26 @@ public interface CloudRecordServiceMapper {
|
||||
"where id = #{id}" +
|
||||
" </script>")
|
||||
CloudRecordItem queryOne(@Param("id") Integer id);
|
||||
|
||||
@Select(" <script>" +
|
||||
"select media_server_id " +
|
||||
" from wvp_cloud_record " +
|
||||
" where 0 = 0" +
|
||||
" <if test= 'app != null '> and app=#{app}</if>" +
|
||||
" <if test= 'stream != null '> and stream=#{stream}</if>" +
|
||||
" <if test= 'startTimeStamp != null '> and end_time >= #{startTimeStamp}</if>" +
|
||||
" <if test= 'endTimeStamp != null '> and start_time <= #{endTimeStamp}</if>" +
|
||||
" group by media_server_id" +
|
||||
" </script>")
|
||||
List<String> queryMediaServerId(@Param("app") String app,
|
||||
@Param("stream") String stream,
|
||||
@Param("startTimeStamp")Long startTimeStamp,
|
||||
@Param("endTimeStamp")Long endTimeStamp);
|
||||
|
||||
@Select(" <script>" +
|
||||
"select * " +
|
||||
" from wvp_cloud_record where id in " +
|
||||
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
|
||||
" </script>")
|
||||
List<CloudRecordItem> queryRecordByIds(Set<Integer> ids);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
@@ -123,25 +124,21 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
public void onApplicationEvent(MediaDepartureEvent event) {
|
||||
|
||||
// 兼容流注销时类型从redis记录获取
|
||||
MediaInfo mediaInfo = redisCatchStorage.getStreamInfo(
|
||||
event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
MediaInfo mediaInfo = redisCatchStorage.getPushListItem(event.getApp(), event.getStream());
|
||||
|
||||
if (mediaInfo != null) {
|
||||
log.info("[推流信息] 查询到redis存在推流缓存, 开始清理,{}/{}", event.getApp(), event.getStream());
|
||||
String type = OriginType.values()[mediaInfo.getOriginType()].getType();
|
||||
redisCatchStorage.removeStream(event.getMediaServer().getId(), type, event.getApp(), event.getStream());
|
||||
if ("PUSH".equalsIgnoreCase(type)) {
|
||||
// 冗余数据,自己系统中自用
|
||||
redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
}
|
||||
if (type != null) {
|
||||
// 发送流变化redis消息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("serverId", userSetting.getServerId());
|
||||
jsonObject.put("app", event.getApp());
|
||||
jsonObject.put("stream", event.getStream());
|
||||
jsonObject.put("register", false);
|
||||
jsonObject.put("mediaServerId", event.getMediaServer().getId());
|
||||
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
|
||||
}
|
||||
// 冗余数据,自己系统中自用
|
||||
redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
// 发送流变化redis消息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("serverId", userSetting.getServerId());
|
||||
jsonObject.put("app", event.getApp());
|
||||
jsonObject.put("stream", event.getStream());
|
||||
jsonObject.put("register", false);
|
||||
jsonObject.put("mediaServerId", event.getMediaServer().getId());
|
||||
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
|
||||
}
|
||||
StreamPush streamPush = getPush(event.getApp(), event.getStream());
|
||||
if (streamPush == null) {
|
||||
@@ -576,13 +573,13 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
if (streamPushList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<CommonGBChannel> commonGBChannelList = new ArrayList<>();
|
||||
Set<Integer> channelIds = new HashSet<>();
|
||||
streamPushList.stream().forEach(streamPush -> {
|
||||
if (streamPush.getGbDeviceId() != null) {
|
||||
commonGBChannelList.add(streamPush.buildCommonGBChannel());
|
||||
channelIds.add(streamPush.getGbId());
|
||||
}
|
||||
});
|
||||
streamPushMapper.batchDel(streamPushList);
|
||||
gbChannelService.delete(ids);
|
||||
gbChannelService.delete(channelIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
@@ -9,6 +10,7 @@ import oshi.hardware.NetworkIF;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -142,4 +144,19 @@ public class SystemInfoUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getHardwareId(){
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hardware = systemInfo.getHardware();
|
||||
// CPU ID
|
||||
String cpuId = hardware.getProcessor().getProcessorIdentifier().getProcessorID();
|
||||
// 主板序号
|
||||
String serialNumber = hardware.getComputerSystem().getSerialNumber();
|
||||
|
||||
return DigestUtils.md5DigestAsHex(
|
||||
(
|
||||
DigestUtils.md5DigestAsHex(cpuId.getBytes(StandardCharsets.UTF_8)) +
|
||||
DigestUtils.md5DigestAsHex(serialNumber.getBytes(StandardCharsets.UTF_8))
|
||||
).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.genersoft.iot.vmp.vmanager.cloudRecord;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
|
||||
import com.genersoft.iot.vmp.streamPush.bean.BatchRemoveParam;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -20,12 +27,15 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
@@ -46,6 +56,9 @@ public class CloudRecordController {
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/date/list")
|
||||
@@ -55,7 +68,12 @@ public class CloudRecordController {
|
||||
@Parameter(name = "year", description = "年,置空则查询当年", required = false)
|
||||
@Parameter(name = "month", description = "月,置空则查询当月", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部", required = false)
|
||||
public List<String> openRtpServer(@RequestParam(required = true) String app, @RequestParam(required = true) String stream, @RequestParam(required = false) Integer year, @RequestParam(required = false) Integer month, @RequestParam(required = false) String mediaServerId
|
||||
public List<String> openRtpServer(
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = false) Integer year,
|
||||
@RequestParam(required = false) Integer month,
|
||||
@RequestParam(required = false) String mediaServerId
|
||||
|
||||
) {
|
||||
log.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", app, stream, mediaServerId, year, month);
|
||||
@@ -96,7 +114,17 @@ public class CloudRecordController {
|
||||
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
|
||||
@Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false)
|
||||
public PageInfo<CloudRecordItem> openRtpServer(@RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId
|
||||
@Parameter(name = "ascOrder", description = "是否升序排序, 升序: true, 降序: false", required = false)
|
||||
public PageInfo<CloudRecordItem> openRtpServer(@RequestParam(required = false) String query,
|
||||
@RequestParam(required = false) String app,
|
||||
@RequestParam(required = false) String stream,
|
||||
@RequestParam int page,
|
||||
@RequestParam int count,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) String mediaServerId,
|
||||
@RequestParam(required = false) String callId,
|
||||
@RequestParam(required = false) Boolean ascOrder
|
||||
|
||||
) {
|
||||
log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId);
|
||||
@@ -130,7 +158,7 @@ public class CloudRecordController {
|
||||
if (callId != null && ObjectUtils.isEmpty(callId.trim())) {
|
||||
callId = null;
|
||||
}
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId, ascOrder);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@@ -220,6 +248,113 @@ public class CloudRecordController {
|
||||
return cloudRecordService.getPlayUrlPath(recordId);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/loadRecord")
|
||||
@Operation(summary = "加载录像文件形成播放地址")
|
||||
@Parameter(name = "app", description = "应用名", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "date", description = "日期, 例如 2025-04-10", required = true)
|
||||
public DeferredResult<WVPResult<StreamContent>> loadRecord(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) String date
|
||||
) {
|
||||
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>();
|
||||
|
||||
result.onTimeout(()->{
|
||||
log.info("[加载录像文件超时] app={}, stream={}, date={}", app, stream, date);
|
||||
WVPResult<StreamContent> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(ErrorCode.ERROR100.getCode());
|
||||
wvpResult.setMsg("加载录像文件超时");
|
||||
result.setResult(wvpResult);
|
||||
});
|
||||
|
||||
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
||||
|
||||
WVPResult<StreamContent> wvpResult = new WVPResult<>();
|
||||
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
||||
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
|
||||
wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
|
||||
|
||||
if (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);
|
||||
}
|
||||
if (!org.springframework.util.ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
||||
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
||||
}
|
||||
wvpResult.setData(new StreamContent(streamInfo));
|
||||
}else {
|
||||
wvpResult.setCode(code);
|
||||
wvpResult.setMsg(msg);
|
||||
}
|
||||
}else {
|
||||
wvpResult.setCode(code);
|
||||
wvpResult.setMsg(msg);
|
||||
}
|
||||
result.setResult(wvpResult);
|
||||
};
|
||||
|
||||
cloudRecordService.loadRecord(app, stream, date, callback);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/seek")
|
||||
@Operation(summary = "定位录像播放到制定位置")
|
||||
@Parameter(name = "mediaServerId", description = "使用的节点Id", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "seek", description = "要定位的时间位置,从录像开始的时间算起", required = true)
|
||||
public void seekRecord(
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Double seek,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
cloudRecordService.seekRecord(mediaServerId, app, stream, seek, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("/speed")
|
||||
@Operation(summary = "设置录像播放速度")
|
||||
@Parameter(name = "mediaServerId", description = "使用的节点Id", required = true)
|
||||
@Parameter(name = "stream", description = "流ID", required = true)
|
||||
@Parameter(name = "speed", description = "要设置的录像倍速", required = true)
|
||||
public void setRecordSpeed(
|
||||
@RequestParam(required = true) String mediaServerId,
|
||||
@RequestParam(required = true) String app,
|
||||
@RequestParam(required = true) String stream,
|
||||
@RequestParam(required = true) Integer speed,
|
||||
@RequestParam(required = false) String schema
|
||||
) {
|
||||
if (schema == null) {
|
||||
schema = "ts";
|
||||
}
|
||||
|
||||
cloudRecordService.setRecordSpeed(mediaServerId, app, stream, speed, schema);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除录像文件")
|
||||
@Parameter(name = "ids", description = "文件ID集合", required = true)
|
||||
public void deleteFileByIds(@RequestBody BatchRemoveParam ids) {
|
||||
cloudRecordService.deleteFileByIds(ids.getIds());
|
||||
}
|
||||
|
||||
/************************* 以下这些接口只适合wvp和zlm部署在同一台服务器的情况,且wvp只有一个zlm节点的情况 ***************************************/
|
||||
|
||||
/**
|
||||
@@ -282,7 +417,7 @@ public class CloudRecordController {
|
||||
try {
|
||||
ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
File file = new File(cloudRecordItem.getFilePath());
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
continue;
|
||||
@@ -366,7 +501,7 @@ public class CloudRecordController {
|
||||
if (remoteHost == null) {
|
||||
remoteHost = request.getScheme() + "://" + request.getLocalAddr() + ":" + (request.getScheme().equals("https") ? mediaServer.getHttpSSlPort() : mediaServer.getHttpPort());
|
||||
}
|
||||
PageInfo<CloudRecordItem> cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
PageInfo<CloudRecordItem> cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId, null);
|
||||
PageInfo<CloudRecordUrl> cloudRecordUrlPageInfo = new PageInfo<>();
|
||||
if (!ObjectUtils.isEmpty(cloudRecordItemPageInfo)) {
|
||||
cloudRecordUrlPageInfo.setPageNum(cloudRecordItemPageInfo.getPageNum());
|
||||
@@ -391,7 +526,7 @@ public class CloudRecordController {
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
CloudRecordUrl cloudRecordUrl = new CloudRecordUrl();
|
||||
cloudRecordUrl.setId(cloudRecordItem.getId());
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss((long)cloudRecordItem.getStartTime()));
|
||||
cloudRecordUrl.setPlayUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath());
|
||||
cloudRecordUrlList.add(cloudRecordUrl);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
package com.genersoft.iot.vmp.vmanager.log;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.ILogService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.LogFileInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
@@ -23,24 +12,17 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "日志文件查询接口")
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -41,13 +42,14 @@ import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.NetworkIF;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "服务控制")
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/server")
|
||||
public class ServerController {
|
||||
@@ -80,6 +82,7 @@ public class ServerController {
|
||||
@Value("${server.port}")
|
||||
private int serverPort;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@@ -176,33 +179,14 @@ public class ServerController {
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "重启服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/restart")
|
||||
@Operation(summary = "关闭服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/shutdown")
|
||||
@ResponseBody
|
||||
public void restart() {
|
||||
// taskExecutor.execute(()-> {
|
||||
// try {
|
||||
// Thread.sleep(3000);
|
||||
// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
|
||||
// SipStackImpl stack = (SipStackImpl) up.getSipStack();
|
||||
// stack.stop();
|
||||
// Iterator listener = stack.getListeningPoints();
|
||||
// while (listener.hasNext()) {
|
||||
// stack.deleteListeningPoint((ListeningPoint) listener.next());
|
||||
// }
|
||||
// Iterator providers = stack.getSipProviders();
|
||||
// while (providers.hasNext()) {
|
||||
// stack.deleteSipProvider((SipProvider) providers.next());
|
||||
// }
|
||||
// VManageBootstrap.restart();
|
||||
// } catch (InterruptedException | ObjectInUseException e) {
|
||||
// throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
|
||||
// }
|
||||
// });
|
||||
public void shutdown() {
|
||||
log.info("正在关闭服务。。。");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
@Operation(summary = "获取系统配置信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@GetMapping(value = "/system/configInfo")
|
||||
@ResponseBody
|
||||
@@ -293,7 +277,7 @@ public class ServerController {
|
||||
@GetMapping(value = "/info")
|
||||
@ResponseBody
|
||||
@Operation(summary = "获取系统信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
public Map<String, Map<String, String>> getInfo() {
|
||||
public Map<String, Map<String, String>> getInfo(HttpServletRequest request) {
|
||||
Map<String, Map<String, String>> result = new LinkedHashMap<>();
|
||||
Map<String, String> hardwareMap = new LinkedHashMap<>();
|
||||
result.put("硬件信息", hardwareMap);
|
||||
@@ -341,6 +325,12 @@ public class ServerController {
|
||||
platformMap.put("GIT版本", version.getGIT_Revision_SHORT());
|
||||
platformMap.put("DOCKER环境", new File("/.dockerenv").exists()?"是":"否");
|
||||
|
||||
Map<String, String> docmap = new LinkedHashMap<>();
|
||||
result.put("文档地址", docmap);
|
||||
docmap.put("部署文档", "https://doc.wvp-pro.cn");
|
||||
docmap.put("接口文档", String.format("%s://%s:%s/doc.html", request.getScheme(), request.getServerName(), request.getServerPort()));
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,9 +63,9 @@ public class UserApiKeyController {
|
||||
|
||||
Long expirationTime = null;
|
||||
if (expiresAt != null) {
|
||||
long timestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
expirationTime = (timestamp - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (expirationTime < 0) {
|
||||
expirationTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
|
||||
long difference = (expirationTime - System.currentTimeMillis()) / (60 * 1000);
|
||||
if (difference < 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "过期时间不能早于当前时间");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.genersoft.iot.vmp.vmanager.user;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.SecurityUtils;
|
||||
@@ -42,6 +43,9 @@ public class UserController {
|
||||
@Autowired
|
||||
private IRoleService roleService;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@GetMapping("/login")
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," +
|
||||
@@ -62,6 +66,7 @@ public class UserController {
|
||||
String jwt = JwtUtils.createToken(username);
|
||||
response.setHeader(JwtUtils.getHeader(), jwt);
|
||||
user.setAccessToken(jwt);
|
||||
user.setServerId(userSetting.getServerId());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -37,10 +37,9 @@ public class ApiControlController {
|
||||
* @param channel 通道序号
|
||||
* @param code 通道编号
|
||||
* @param speed 速度(0~255) 默认值: 129
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/ptz")
|
||||
private void list(String serial,String command,
|
||||
private void ptz(String serial,String command,
|
||||
@RequestParam(required = false)Integer channel,
|
||||
@RequestParam(required = false)String code,
|
||||
@RequestParam(required = false)Integer speed){
|
||||
@@ -55,7 +54,7 @@ public class ApiControlController {
|
||||
if (device == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到");
|
||||
}
|
||||
int cmdCode = 0;
|
||||
int cmdCode = -1;
|
||||
switch (command){
|
||||
case "left":
|
||||
cmdCode = 2;
|
||||
@@ -93,6 +92,9 @@ public class ApiControlController {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (cmdCode == -1) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未识别的指令:" + command);
|
||||
}
|
||||
// 默认值 50
|
||||
try {
|
||||
cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed);
|
||||
@@ -110,7 +112,6 @@ public class ApiControlController {
|
||||
* @param command 控制指令 允许值: set, goto, remove
|
||||
* @param preset 预置位编号(1~255)
|
||||
* @param name 预置位名称, command=set 时有效
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/preset")
|
||||
private void list(String serial,String command,
|
||||
|
||||
@@ -2,4 +2,4 @@ spring:
|
||||
application:
|
||||
name: wvp
|
||||
profiles:
|
||||
active: 1078
|
||||
active: 1078
|
||||
|
||||
57
src/main/resources/install.sh
Executable file
57
src/main/resources/install.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#! /bin/sh
|
||||
|
||||
WORD_DIR=$(cd $(dirname $0); pwd)
|
||||
SERVICE_NAME="wvp"
|
||||
|
||||
# 检查是否为 root 用户
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "提示: 建议使用 root 用户执行此脚本,否则可能权限不足!"
|
||||
read -p "继续?(y/n) " -n 1 -r
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
|
||||
# 当前目录直接搜索(不含子目录)
|
||||
jar_files=(*.jar)
|
||||
|
||||
if [ ${#jar_files[@]} -eq 0 ]; then
|
||||
echo "当前目录无 JAR 文件!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 遍历结果
|
||||
for jar in "${jar_files[@]}"; do
|
||||
echo "找到 JAR 文件: $jar"
|
||||
done
|
||||
|
||||
# 写文件
|
||||
# 生成 Systemd 服务文件内容
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
cat << EOF | sudo tee "$SERVICE_FILE" > /dev/null
|
||||
[Unit]
|
||||
Description=${SERVICE_NAME}
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
User=$USER
|
||||
WorkingDirectory=${WORD_DIR}
|
||||
ExecStart=java -jar ${jar_files}
|
||||
SuccessExitStatus=143
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
Environment=SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# 重载 Systemd 并启动服务
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable "$SERVICE_NAME"
|
||||
sudo systemctl start "$SERVICE_NAME"
|
||||
|
||||
# 验证服务状态
|
||||
echo "服务已安装!执行以下命令查看状态:"
|
||||
echo "sudo systemctl status $SERVICE_NAME"
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
|
||||
spring:
|
||||
cache:
|
||||
type: redis
|
||||
thymeleaf:
|
||||
cache: false
|
||||
# 设置接口超时时间
|
||||
mvc:
|
||||
async:
|
||||
@@ -113,8 +117,8 @@ sip:
|
||||
# keepalliveToOnline: false
|
||||
# 是否存储alarm信息
|
||||
alarm: false
|
||||
# 命令发送等待回复的超时时间, 单位:秒
|
||||
timeout: 15
|
||||
# 命令发送等待回复的超时时间, 单位:毫秒
|
||||
timeout: 1000
|
||||
|
||||
# 做为JT1078服务器的配置
|
||||
jt1078:
|
||||
|
||||
Reference in New Issue
Block a user