From 2515d5dbfd3d8cf294f21caec7d622ed5cad4af1 Mon Sep 17 00:00:00 2001 From: 18792927508 <1322446236@qq.com> Date: Fri, 27 Oct 2023 16:24:42 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wisdomarbitrate/VideoController.java | 63 +++++ .../com/ruoyi/common/config/RuoYiConfig.java | 9 + .../mapper/CaseApplicationMapper.java | 13 + .../wisdomarbitrate/service/VideoService.java | 20 ++ .../service/impl/VideoServiceImpl.java | 253 ++++++++++++++++++ .../wisdomarbitrate/CaseApplicationMapper.xml | 8 +- 6 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/VideoController.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/VideoService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/VideoServiceImpl.java diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/VideoController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/VideoController.java new file mode 100644 index 0000000..92f88cc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/VideoController.java @@ -0,0 +1,63 @@ +package com.ruoyi.web.controller.wisdomarbitrate; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication; +import com.ruoyi.wisdomarbitrate.domain.vo.SendRoomNoMessageVO; +import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO; +import com.ruoyi.wisdomarbitrate.service.VideoService; +import com.ruoyi.wisdomarbitrate.service.WeChatUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + +/** + * @author wangqiong + * @description 视频录制 + * @date 2023-10-26 11:25 + */ +@RestController +@RequestMapping("/video") +public class VideoController extends BaseController { + @Autowired + private VideoService videoService; + + /** + * 从腾讯云下载文件到本地 + * @param + * @return + */ + @Anonymous + @PostMapping("/videoRollBack") + public AjaxResult videoRollBack( @RequestBody String body, HttpServletRequest request) { + videoService.videoRollBack(body,request); + return success(); + } + /** + * 根据房间号绑定案件ID + * @param + * @return + */ + @Anonymous + @PostMapping("/bindCaseId") + public AjaxResult bindCaseId(@Valid @RequestBody SendRoomNoMessageVO vo) { + + return videoService.bindCaseId(vo.getId(),vo.getRoomNo()); + } + /** + * 根据案件ID查询视频 + * @param + * @return + */ + @Anonymous + @GetMapping("/videoList") + public AjaxResult videoList( @RequestParam Long caseId) { + + return videoService.videoList(caseId); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java index 00f70f6..c5891c0 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -132,4 +132,13 @@ public class RuoYiConfig { return getProfile() + "/upload"; } + + /** + * 获取上传路径 + */ + public static String getVideoUploadPath() + { + return getProfile() + "/video"; + } + // https://1304001529.vod-qcloud.com/b78823bbvodcq1304001529/3ce565bf3270835011486046286/f0.mp4 } diff --git a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/mapper/CaseApplicationMapper.java b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/mapper/CaseApplicationMapper.java index b517d58..c775ae9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/mapper/CaseApplicationMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/mapper/CaseApplicationMapper.java @@ -84,4 +84,17 @@ public interface CaseApplicationMapper { */ int batchDeletecaseApplication(@Param("ids") List ids); + /** + * 绑定房间号 + * @param caseId + * @param roomId + */ + void bindCaseId(@Param("caseId")Long caseId,@Param("roomId") String roomId); + + /** + * 根据房间号查询案件id + * @param roomId + * @return + */ + Long selectCaseIdByRoomId(@Param("roomId")String roomId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/VideoService.java b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/VideoService.java new file mode 100644 index 0000000..9242840 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/VideoService.java @@ -0,0 +1,20 @@ +package com.ruoyi.wisdomarbitrate.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication; +import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO; + +import javax.servlet.http.HttpServletRequest; + +/** + * 视频录制 + */ +public interface VideoService { + + + void videoRollBack(String body, HttpServletRequest request) ; + + AjaxResult bindCaseId(Long caseId, String roomId); + + AjaxResult videoList(Long caseId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/VideoServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/VideoServiceImpl.java new file mode 100644 index 0000000..77c4979 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/VideoServiceImpl.java @@ -0,0 +1,253 @@ +package com.ruoyi.wisdomarbitrate.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.SmsUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysUserMapper; +import com.ruoyi.wisdomarbitrate.domain.CaseApplication; +import com.ruoyi.wisdomarbitrate.domain.CaseAttach; +import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication; +import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO; +import com.ruoyi.wisdomarbitrate.mapper.CaseApplicationMapper; +import com.ruoyi.wisdomarbitrate.mapper.CaseAttachMapper; +import com.ruoyi.wisdomarbitrate.mapper.IdentityAuthenticationMapper; +import com.ruoyi.wisdomarbitrate.mapper.WeChatUserMapper; +import com.ruoyi.wisdomarbitrate.service.VideoService; +import com.ruoyi.wisdomarbitrate.service.WeChatUserService; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.vod.v20180717.VodClient; +import com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosRequest; +import com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ResourceUtils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static com.ruoyi.common.utils.file.FileUploadUtils.getAbsoluteFile; +import static com.ruoyi.common.utils.file.FileUploadUtils.getPathFileName; + +/** + * @author wangqiong + * @description 视频录制 + * @date 2023-10-26 11:45 + */ +@Service +@Slf4j +public class VideoServiceImpl implements VideoService { + @Autowired + private CaseApplicationMapper caseApplicationMapper; + @Autowired + private CaseAttachMapper caseAttachMapper; + + /** + * 功能:第三方回调sign校验 + * 参数: + * key:控制台配置的密钥key + * body:腾讯云回调返回的body体 + * sign:腾讯云回调返回的签名值sign + * 返回值: + * Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info + * Info:成功/失败信息 + * @param body + * @param request + * @throws Exception + */ + @Override + public void videoRollBack(String body, HttpServletRequest request) { + String key = "key"; + String sdkAppId = request.getHeader("SdkAppId"); + String sign = request.getHeader("Sign"); + // String resultSign = getResultSign(key,body); + // log.info("resultSign:"+resultSign); + // if (resultSign.equals(sign)) { + JSONObject jsonObject = (JSONObject) JSON.parse(body); + Integer eventType = jsonObject.getInteger("EventType"); // 事件类型 + String eventInfo = jsonObject.getString("EventInfo"); // 事件信息 + JSONObject jsonObject1 = (JSONObject) JSON.parse(eventInfo); + String roomId = jsonObject1.getString("RoomId"); + String taskId = jsonObject1.getString("TaskId"); // 任务ID + String payload = jsonObject1.getString("Payload"); // 根据不同事件类型定义不同 + JSONObject jsonObject2 = (JSONObject) JSON.parse(payload); + String tencentVod = jsonObject2.getString("TencentVod"); // 点播平台信息 + JSONObject jsonObject3 = (JSONObject) JSON.parse(tencentVod); + // 录制视频上传成功 + if (eventType == 311) { + // 点播平台的唯一 ID + String fileId = jsonObject3.getString("FileId"); + // 点播平台的播放地址 + String videoUrl = jsonObject3.getString("VideoUrl"); + // 主辅流标识,main 代表主流(摄像头),aux 代表辅流(屏幕分享),mix 代表混流录制 + String mediaId = jsonObject3.getString("MediaId"); + // 建立相关的数据库用来存储音视频录制地址并和相关的业务ID绑定,用于后续下载 + try { + downloadImage(fileId,videoUrl,roomId); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + } + + @Override + public AjaxResult bindCaseId(Long caseId, String roomId) { + caseApplicationMapper .bindCaseId(caseId,roomId); + return AjaxResult.success(); + } + + @Override + public AjaxResult videoList(Long caseId) { + CaseApplication caseApplication = new CaseApplication(); + caseApplication.setId(caseId); + caseApplication.setAnnexType(9); + List caseAttachList = caseAttachMapper.queryCaseAttachList(caseApplication); + if(CollectionUtil.isEmpty(caseAttachList)){ + return AjaxResult.success(); + } + for (CaseAttach caseAttach : caseAttachList) { + String annexName = caseAttach.getAnnexName(); + String prefix = "/profile"; + int startIndex = annexName.indexOf(prefix); + startIndex += prefix.length(); + String annexPath = "/uploadPath" + annexName.substring(startIndex); + caseAttach.setAnnexPath(annexPath); + int startIndexnew = annexName.lastIndexOf("/"); + if (startIndexnew != -1) { + String annexNamenew = annexName.substring(startIndexnew + 1); + caseAttach.setAnnexName(annexNamenew); + } + } + return AjaxResult.success(caseAttachList); + } + + /** + * 查询出音视频集合,并下载,在将云点播上面的音视频删除 + * @param fileIds 点播平台唯一ID集合 + * @throws Exception + */ + private void downloadVideo(String [] fileIds) throws Exception { + try{ + //创建文件对象 + Properties properties = new Properties(); + //加载文件获取数据 文件带后缀 + properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream + ("application.properties")); + //根据key来获取value + String secretId = properties.getProperty("secretid"); + String secretKey = properties.getProperty("secretkey"); + // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 + // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 + // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 + Credential cred = new Credential(secretId, secretKey); + // 实例化一个http选项,可选的,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("vod.tencentcloudapi.com"); + // 实例化一个client选项,可选的,没有特殊需求可以跳过 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品的client对象,clientProfile是可选的 + VodClient client = new VodClient(cred, "ap-beijing", clientProfile); + // 实例化一个请求对象,每个接口都会对应一个request对象 + DescribeMediaInfosRequest req = new DescribeMediaInfosRequest(); + req.setFileIds(fileIds); + String[] basicInfos = {"basicInfo"}; + req.setFilters(basicInfos); + // 返回的resp是一个DescribeMediaInfosResponse的实例,与请求对象对应 + DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req); + // 输出json格式的字符串回包 + log.info(DescribeMediaInfosResponse.toJsonString(resp)); + String json = DescribeMediaInfosResponse.toJsonString(resp); + JSONObject jsonObject = (JSONObject) JSON.parse(json); + JSONArray jsonArray = jsonObject.getJSONArray("MediaInfoSet"); // 媒体文件信息列表。 + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject1 = jsonArray.getJSONObject(i); + String fileId = jsonObject1.getString("FileId"); // 点播平台的唯一 ID + String basicInfo = jsonObject1.getString("BasicInfo"); // 基础信息 + JSONObject jsonObject2 = (JSONObject) JSON.parse(basicInfo); + String mediaUrl = jsonObject2.getString("MediaUrl"); // 文件地址 + String downPath = downloadImage(null,null,mediaUrl); // 下载音视频(返回本地下载地址) + // 将未下载的音视频列表查询出来,进行下载到服务器上面,并更新数据库数据 + log.info(downPath); // 本地地址 + } + log.info("下载音视频成功"); + } catch (TencentCloudSDKException e) { + log.info(e.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + log.info("腾讯云测试成功"); + } + + /** + * 将视频下载到本地 + * @param fileUrl 视频路径 + * @return + */ + @Transactional + public String downloadImage(String fileId,String fileUrl,String roomId) throws IOException { + String staticAndMksDir = null; + if (fileUrl != null) { + //下载时文件名称 + String fileName = fileUrl.substring(fileUrl.lastIndexOf("/")); + fileName = fileName.replace("/", ""); + fileName=fileId+fileName; + String absPath = getAbsoluteFile(RuoYiConfig.getVideoUploadPath(), fileName).getAbsolutePath(); + staticAndMksDir = Paths.get(absPath).toFile().toString(); + HttpUtil.downloadFile(fileUrl, staticAndMksDir ); + Long caseId= caseApplicationMapper.selectCaseIdByRoomId(roomId); + String annexName = getPathFileName(RuoYiConfig.getVideoUploadPath(), fileName); + // 存入数据库 + CaseAttach caseAttach = CaseAttach.builder().caseAppliId(caseId) + .annexName(annexName) + .annexPath(RuoYiConfig.getVideoUploadPath()) + .annexType(9) + .build(); + caseAttachMapper.save(caseAttach); + return annexName; + } + return ""; + + } + /** + * @param key 回调秘钥 + * @param body 入参 + * @return 签名 Sign 计算公式中 key 为计算签名 Sign 用的加密密钥。 + * @throws Exception + */ + private static String getResultSign(String key, String body) throws Exception { + Mac hmacSha256 = Mac.getInstance("HmacSHA256"); + SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256"); + hmacSha256.init(secret_key); + return Base64.getEncoder().encodeToString(hmacSha256.doFinal(body.getBytes())); + } + +} diff --git a/ruoyi-system/src/main/resources/mapper/wisdomarbitrate/CaseApplicationMapper.xml b/ruoyi-system/src/main/resources/mapper/wisdomarbitrate/CaseApplicationMapper.xml index 26d26cb..14550ef 100644 --- a/ruoyi-system/src/main/resources/mapper/wisdomarbitrate/CaseApplicationMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/wisdomarbitrate/CaseApplicationMapper.xml @@ -453,6 +453,9 @@ update case_application set lock_status=#{lockStatus} where id = #{id} + + update case_application set room_id=#{roomId} where id = #{caseId} + delete from case_application where id = #{id} @@ -559,7 +562,10 @@ - +