18792927508 2 лет назад
Родитель
Сommit
2515d5dbfd

+ 63
- 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/VideoController.java Просмотреть файл

@@ -0,0 +1,63 @@
1
+package com.ruoyi.web.controller.wisdomarbitrate;
2
+
3
+import cn.hutool.core.util.StrUtil;
4
+import com.ruoyi.common.annotation.Anonymous;
5
+import com.ruoyi.common.core.controller.BaseController;
6
+import com.ruoyi.common.core.domain.AjaxResult;
7
+import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication;
8
+import com.ruoyi.wisdomarbitrate.domain.vo.SendRoomNoMessageVO;
9
+import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO;
10
+import com.ruoyi.wisdomarbitrate.service.VideoService;
11
+import com.ruoyi.wisdomarbitrate.service.WeChatUserService;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.web.bind.annotation.*;
14
+import org.springframework.web.multipart.MultipartFile;
15
+
16
+import javax.servlet.http.HttpServletRequest;
17
+import javax.validation.Valid;
18
+
19
+/**
20
+ * @author wangqiong
21
+ * @description 视频录制
22
+ * @date 2023-10-26 11:25
23
+ */
24
+@RestController
25
+@RequestMapping("/video")
26
+public class VideoController extends BaseController {
27
+    @Autowired
28
+    private VideoService videoService;
29
+
30
+    /**
31
+     * 从腾讯云下载文件到本地
32
+     * @param
33
+     * @return
34
+     */
35
+    @Anonymous
36
+    @PostMapping("/videoRollBack")
37
+    public AjaxResult videoRollBack(  @RequestBody String body, HttpServletRequest request) {
38
+        videoService.videoRollBack(body,request);
39
+       return success();
40
+    }
41
+    /**
42
+     * 根据房间号绑定案件ID
43
+     * @param
44
+     * @return
45
+     */
46
+    @Anonymous
47
+    @PostMapping("/bindCaseId")
48
+    public AjaxResult bindCaseId(@Valid @RequestBody SendRoomNoMessageVO vo) {
49
+
50
+        return videoService.bindCaseId(vo.getId(),vo.getRoomNo());
51
+    }
52
+    /**
53
+     * 根据案件ID查询视频
54
+     * @param
55
+     * @return
56
+     */
57
+    @Anonymous
58
+    @GetMapping("/videoList")
59
+    public AjaxResult videoList(  @RequestParam Long caseId) {
60
+
61
+        return videoService.videoList(caseId);
62
+    }
63
+}

+ 9
- 0
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java Просмотреть файл

@@ -132,4 +132,13 @@ public class RuoYiConfig
132 132
     {
133 133
         return getProfile() + "/upload";
134 134
     }
135
+
136
+    /**
137
+     * 获取上传路径
138
+     */
139
+    public static String getVideoUploadPath()
140
+    {
141
+        return getProfile() + "/video";
142
+    }
143
+    // https://1304001529.vod-qcloud.com/b78823bbvodcq1304001529/3ce565bf3270835011486046286/f0.mp4
135 144
 }

+ 13
- 0
ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/mapper/CaseApplicationMapper.java Просмотреть файл

@@ -84,4 +84,17 @@ public interface CaseApplicationMapper {
84 84
      */
85 85
     int batchDeletecaseApplication(@Param("ids") List<Long> ids);
86 86
 
87
+    /**
88
+     * 绑定房间号
89
+     * @param caseId
90
+     * @param roomId
91
+     */
92
+    void bindCaseId(@Param("caseId")Long caseId,@Param("roomId") String roomId);
93
+
94
+    /**
95
+     * 根据房间号查询案件id
96
+     * @param roomId
97
+     * @return
98
+     */
99
+    Long selectCaseIdByRoomId(@Param("roomId")String roomId);
87 100
 }

+ 20
- 0
ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/VideoService.java Просмотреть файл

@@ -0,0 +1,20 @@
1
+package com.ruoyi.wisdomarbitrate.service;
2
+
3
+import com.ruoyi.common.core.domain.AjaxResult;
4
+import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication;
5
+import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO;
6
+
7
+import javax.servlet.http.HttpServletRequest;
8
+
9
+/**
10
+ * 视频录制
11
+ */
12
+public interface VideoService {
13
+
14
+
15
+    void videoRollBack(String body, HttpServletRequest request) ;
16
+
17
+    AjaxResult bindCaseId(Long caseId, String roomId);
18
+
19
+    AjaxResult videoList(Long caseId);
20
+}

+ 253
- 0
ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/VideoServiceImpl.java Просмотреть файл

@@ -0,0 +1,253 @@
1
+package com.ruoyi.wisdomarbitrate.service.impl;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import cn.hutool.http.HttpUtil;
6
+import com.alibaba.fastjson.JSON;
7
+import com.alibaba.fastjson.JSONArray;
8
+import com.alibaba.fastjson.JSONObject;
9
+import com.ruoyi.common.config.RuoYiConfig;
10
+import com.ruoyi.common.constant.CacheConstants;
11
+import com.ruoyi.common.core.domain.AjaxResult;
12
+import com.ruoyi.common.core.domain.entity.SysUser;
13
+import com.ruoyi.common.core.domain.model.LoginUser;
14
+import com.ruoyi.common.core.redis.RedisCache;
15
+import com.ruoyi.common.utils.SecurityUtils;
16
+import com.ruoyi.common.utils.SmsUtils;
17
+import com.ruoyi.common.utils.StringUtils;
18
+import com.ruoyi.common.utils.spring.SpringUtils;
19
+import com.ruoyi.system.mapper.SysUserMapper;
20
+import com.ruoyi.wisdomarbitrate.domain.CaseApplication;
21
+import com.ruoyi.wisdomarbitrate.domain.CaseAttach;
22
+import com.ruoyi.wisdomarbitrate.domain.IdentityAuthentication;
23
+import com.ruoyi.wisdomarbitrate.domain.vo.WeChatUserVO;
24
+import com.ruoyi.wisdomarbitrate.mapper.CaseApplicationMapper;
25
+import com.ruoyi.wisdomarbitrate.mapper.CaseAttachMapper;
26
+import com.ruoyi.wisdomarbitrate.mapper.IdentityAuthenticationMapper;
27
+import com.ruoyi.wisdomarbitrate.mapper.WeChatUserMapper;
28
+import com.ruoyi.wisdomarbitrate.service.VideoService;
29
+import com.ruoyi.wisdomarbitrate.service.WeChatUserService;
30
+import com.tencentcloudapi.common.Credential;
31
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
32
+import com.tencentcloudapi.common.profile.ClientProfile;
33
+import com.tencentcloudapi.common.profile.HttpProfile;
34
+import com.tencentcloudapi.vod.v20180717.VodClient;
35
+import com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosRequest;
36
+import com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosResponse;
37
+import lombok.extern.slf4j.Slf4j;
38
+import org.springframework.beans.factory.annotation.Autowired;
39
+import org.springframework.stereotype.Service;
40
+import org.springframework.transaction.annotation.Transactional;
41
+import org.springframework.util.ResourceUtils;
42
+
43
+import javax.crypto.Mac;
44
+import javax.crypto.spec.SecretKeySpec;
45
+import javax.servlet.http.HttpServletRequest;
46
+import java.io.File;
47
+import java.io.IOException;
48
+import java.nio.file.Paths;
49
+import java.text.SimpleDateFormat;
50
+import java.util.*;
51
+import java.util.concurrent.ExecutorService;
52
+import java.util.concurrent.Executors;
53
+import java.util.concurrent.TimeUnit;
54
+
55
+import static com.ruoyi.common.utils.file.FileUploadUtils.getAbsoluteFile;
56
+import static com.ruoyi.common.utils.file.FileUploadUtils.getPathFileName;
57
+
58
+/**
59
+ * @author wangqiong
60
+ * @description 视频录制
61
+ * @date 2023-10-26 11:45
62
+ */
63
+@Service
64
+@Slf4j
65
+public class VideoServiceImpl implements VideoService {
66
+    @Autowired
67
+    private CaseApplicationMapper caseApplicationMapper;
68
+    @Autowired
69
+    private CaseAttachMapper caseAttachMapper;
70
+
71
+    /**
72
+     *  功能:第三方回调sign校验
73
+     *     参数:
74
+     *     key:控制台配置的密钥key
75
+     *     body:腾讯云回调返回的body体
76
+     *      sign:腾讯云回调返回的签名值sign
77
+     *     返回值:
78
+     *      Status:OK 表示校验通过,FAIL 表示校验失败,具体原因参考Info
79
+     *      Info:成功/失败信息
80
+     * @param body
81
+     * @param request
82
+     * @throws Exception
83
+     */
84
+    @Override
85
+    public void videoRollBack(String body, HttpServletRequest request)  {
86
+        String key = "key";
87
+        String sdkAppId = request.getHeader("SdkAppId");
88
+        String sign = request.getHeader("Sign");
89
+     //   String resultSign = getResultSign(key,body);
90
+    //    log.info("resultSign:"+resultSign);
91
+    //    if (resultSign.equals(sign)) {
92
+            JSONObject jsonObject = (JSONObject) JSON.parse(body);
93
+            Integer eventType = jsonObject.getInteger("EventType"); // 事件类型
94
+            String eventInfo = jsonObject.getString("EventInfo"); // 事件信息
95
+            JSONObject jsonObject1 = (JSONObject) JSON.parse(eventInfo);
96
+        String roomId = jsonObject1.getString("RoomId");
97
+        String taskId = jsonObject1.getString("TaskId"); // 任务ID
98
+            String payload = jsonObject1.getString("Payload"); // 根据不同事件类型定义不同
99
+            JSONObject jsonObject2 = (JSONObject) JSON.parse(payload);
100
+            String tencentVod = jsonObject2.getString("TencentVod"); // 点播平台信息
101
+            JSONObject jsonObject3 = (JSONObject) JSON.parse(tencentVod);
102
+        // 录制视频上传成功
103
+            if (eventType == 311) {
104
+                // 点播平台的唯一 ID
105
+                String fileId = jsonObject3.getString("FileId");
106
+                // 点播平台的播放地址
107
+                String videoUrl = jsonObject3.getString("VideoUrl");
108
+                // 主辅流标识,main 代表主流(摄像头),aux 代表辅流(屏幕分享),mix 代表混流录制
109
+                String mediaId = jsonObject3.getString("MediaId");
110
+                // 建立相关的数据库用来存储音视频录制地址并和相关的业务ID绑定,用于后续下载
111
+                try {
112
+                    downloadImage(fileId,videoUrl,roomId);
113
+
114
+                } catch (IOException e) {
115
+                    throw new RuntimeException(e);
116
+                }
117
+            }
118
+
119
+    }
120
+
121
+    @Override
122
+    public AjaxResult bindCaseId(Long caseId, String roomId) {
123
+        caseApplicationMapper .bindCaseId(caseId,roomId);
124
+        return AjaxResult.success();
125
+    }
126
+
127
+    @Override
128
+    public AjaxResult videoList(Long caseId) {
129
+        CaseApplication caseApplication = new CaseApplication();
130
+        caseApplication.setId(caseId);
131
+        caseApplication.setAnnexType(9);
132
+        List<CaseAttach> caseAttachList = caseAttachMapper.queryCaseAttachList(caseApplication);
133
+        if(CollectionUtil.isEmpty(caseAttachList)){
134
+            return AjaxResult.success();
135
+        }
136
+        for (CaseAttach caseAttach : caseAttachList) {
137
+            String annexName = caseAttach.getAnnexName();
138
+            String prefix = "/profile";
139
+            int startIndex = annexName.indexOf(prefix);
140
+            startIndex += prefix.length();
141
+            String annexPath = "/uploadPath" + annexName.substring(startIndex);
142
+            caseAttach.setAnnexPath(annexPath);
143
+            int startIndexnew = annexName.lastIndexOf("/");
144
+            if (startIndexnew != -1) {
145
+                String annexNamenew = annexName.substring(startIndexnew + 1);
146
+                caseAttach.setAnnexName(annexNamenew);
147
+            }
148
+        }
149
+        return  AjaxResult.success(caseAttachList);
150
+    }
151
+
152
+    /**
153
+     *  查询出音视频集合,并下载,在将云点播上面的音视频删除
154
+     * @param fileIds 点播平台唯一ID集合
155
+     * @throws Exception
156
+     */
157
+    private void downloadVideo(String [] fileIds) throws Exception {
158
+        try{
159
+            //创建文件对象
160
+            Properties properties = new Properties();
161
+            //加载文件获取数据 文件带后缀
162
+            properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream
163
+                    ("application.properties"));
164
+            //根据key来获取value
165
+            String secretId = properties.getProperty("secretid");
166
+            String secretKey = properties.getProperty("secretkey");
167
+            // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
168
+            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
169
+            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
170
+            Credential cred = new Credential(secretId, secretKey);
171
+            // 实例化一个http选项,可选的,没有特殊需求可以跳过
172
+            HttpProfile httpProfile = new HttpProfile();
173
+            httpProfile.setEndpoint("vod.tencentcloudapi.com");
174
+            // 实例化一个client选项,可选的,没有特殊需求可以跳过
175
+            ClientProfile clientProfile = new ClientProfile();
176
+            clientProfile.setHttpProfile(httpProfile);
177
+            // 实例化要请求产品的client对象,clientProfile是可选的
178
+            VodClient client = new VodClient(cred, "ap-beijing", clientProfile);
179
+            // 实例化一个请求对象,每个接口都会对应一个request对象
180
+            DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
181
+            req.setFileIds(fileIds);
182
+            String[] basicInfos = {"basicInfo"};
183
+            req.setFilters(basicInfos);
184
+            // 返回的resp是一个DescribeMediaInfosResponse的实例,与请求对象对应
185
+            DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
186
+            // 输出json格式的字符串回包
187
+            log.info(DescribeMediaInfosResponse.toJsonString(resp));
188
+            String json = DescribeMediaInfosResponse.toJsonString(resp);
189
+            JSONObject jsonObject = (JSONObject) JSON.parse(json);
190
+            JSONArray jsonArray = jsonObject.getJSONArray("MediaInfoSet"); // 媒体文件信息列表。
191
+            for (int i = 0; i < jsonArray.size(); i++) {
192
+                JSONObject jsonObject1 = jsonArray.getJSONObject(i);
193
+                String fileId = jsonObject1.getString("FileId"); // 点播平台的唯一 ID
194
+                String basicInfo = jsonObject1.getString("BasicInfo"); // 基础信息
195
+                JSONObject jsonObject2 = (JSONObject) JSON.parse(basicInfo);
196
+                String mediaUrl = jsonObject2.getString("MediaUrl"); // 文件地址
197
+                String downPath = downloadImage(null,null,mediaUrl); // 下载音视频(返回本地下载地址)
198
+                // 将未下载的音视频列表查询出来,进行下载到服务器上面,并更新数据库数据
199
+                log.info(downPath); // 本地地址
200
+            }
201
+            log.info("下载音视频成功");
202
+        } catch (TencentCloudSDKException e) {
203
+            log.info(e.toString());
204
+        } catch (IOException e) {
205
+            e.printStackTrace();
206
+        }
207
+        log.info("腾讯云测试成功");
208
+    }
209
+
210
+    /**
211
+     * 将视频下载到本地
212
+     * @param fileUrl 视频路径
213
+     * @return
214
+     */
215
+    @Transactional
216
+    public  String downloadImage(String fileId,String fileUrl,String roomId) throws IOException {
217
+        String staticAndMksDir = null;
218
+        if (fileUrl != null) {
219
+            //下载时文件名称
220
+            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/"));
221
+            fileName = fileName.replace("/", "");
222
+            fileName=fileId+fileName;
223
+                String absPath = getAbsoluteFile(RuoYiConfig.getVideoUploadPath(), fileName).getAbsolutePath();
224
+                staticAndMksDir = Paths.get(absPath).toFile().toString();
225
+                HttpUtil.downloadFile(fileUrl, staticAndMksDir );
226
+            Long caseId= caseApplicationMapper.selectCaseIdByRoomId(roomId);
227
+            String annexName = getPathFileName(RuoYiConfig.getVideoUploadPath(), fileName);
228
+            // 存入数据库
229
+            CaseAttach caseAttach = CaseAttach.builder().caseAppliId(caseId)
230
+                    .annexName(annexName)
231
+                    .annexPath(RuoYiConfig.getVideoUploadPath())
232
+                    .annexType(9)
233
+                    .build();
234
+             caseAttachMapper.save(caseAttach);
235
+            return annexName;
236
+        }
237
+        return "";
238
+
239
+    }
240
+    /**
241
+     * @param key 回调秘钥
242
+     * @param body 入参
243
+     * @return 签名 Sign 计算公式中 key 为计算签名 Sign 用的加密密钥。
244
+     * @throws Exception
245
+     */
246
+    private static String getResultSign(String key, String body) throws Exception {
247
+        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
248
+        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
249
+        hmacSha256.init(secret_key);
250
+        return Base64.getEncoder().encodeToString(hmacSha256.doFinal(body.getBytes()));
251
+    }
252
+
253
+}

+ 7
- 1
ruoyi-system/src/main/resources/mapper/wisdomarbitrate/CaseApplicationMapper.xml Просмотреть файл

@@ -453,6 +453,9 @@
453 453
     <update id="updateCaseLockStatus">
454 454
         update case_application set lock_status=#{lockStatus} where id = #{id}
455 455
     </update>
456
+    <update id="bindCaseId">
457
+        update case_application set room_id=#{roomId} where id = #{caseId}
458
+    </update>
456 459
 
457 460
     <delete id="deletecaseApplication" parameterType="CaseApplication">
458 461
         delete from case_application where id = #{id}
@@ -559,7 +562,10 @@
559 562
     <select id="selectArbitratorList" resultType="java.lang.String">
560 563
         select a.arbitrator_id  id from case_application a where a.id=#{id}
561 564
     </select>
562
-
565
+    <select id="selectCaseIdByRoomId" resultType="java.lang.Long">
566
+        select id
567
+        from case_application where room_id=#{roomId} limit 1
568
+    </select>
563 569
 
564 570
 
565 571
 </mapper>