面试录制视频上传回调接口

This commit is contained in:
pserimal
2023-06-21 20:26:39 +08:00
parent d38d2d87cd
commit dc220e12a6
13 changed files with 347 additions and 1 deletions

View File

@@ -0,0 +1,15 @@
package com.cool.store.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface TRTCVideoCallBackMapper {
/**
* 在面试信息表中添加面试录制视频链接
* @param roomId
* @param videoUrl
*/
void addVideoUrl(@Param("roomId") String roomId, @Param("videoUrl") String videoUrl);
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cool.store.mapper.TRTCVideoCallBackMapper">
<update id="addVideoUrl">
UPDATE hy_partner_interview
SET process_info = IF(process_info IS NULL, #{videoUrl}, CONCAT(process_info, ',' ,#{videoUrl}))
WHERE interview_plan_id = (
SELECT id
FROM hy_partner_interview_plan
WHERE room_id = #{roomId}
)
</update>
</mapper>

View File

@@ -0,0 +1,26 @@
package com.cool.store.dto.trtc;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class TRTCVideoCallBackPayLoad {
@ApiModelProperty("参数:" +
"0代表本录制文件正常上传至点播平台" +
"1代表本录制文件滞留在服务器或者备份存储上" +
"2代表本录制文件上传点播任务异常")
private String Status;
@ApiModelProperty("audio 音频 / video 纯视频 / audio_video 音视频")
private String TrackType;
@ApiModelProperty("主辅流标识main代表主流摄像头aux代表辅流屏幕分享mix代表混流录制")
private String MediaId;
@ApiModelProperty("statue 不为0时对应的错误信息")
private String Errmsg;
@ApiModelProperty("TencentVod")
private TRTCVideoCallBackTencentVod TencentVod;
}

View File

@@ -0,0 +1,12 @@
package com.cool.store.dto.trtc;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class TRTCVideoCallBackTencentVod {
@ApiModelProperty("本录制文件在点播平台的播放地址")
private String VideoUrl;
}

View File

@@ -0,0 +1,15 @@
package com.cool.store.dto.trtc;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class VideoCallBackEventInfo {
@ApiModelProperty("房间号")
private String RoomId;
@ApiModelProperty("信息参数")
private TRTCVideoCallBackPayLoad PayLoad;
}

View File

@@ -0,0 +1,18 @@
package com.cool.store.request;
import com.cool.store.dto.trtc.VideoCallBackEventInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "腾讯音视频录制文件上传回调")
public class TRTCVideoCallBackReq {
@ApiModelProperty("回调通知的事件类型311 为音视频上传事件")
private String EventType;
@ApiModelProperty("事件信息")
private VideoCallBackEventInfo EventInfo;
}

View File

@@ -0,0 +1,12 @@
package com.cool.store.service;
import com.cool.store.request.TRTCVideoCallBackReq;
public interface TRTCVideoService {
/**
* 音视频上传成功后的回调处理
*/
void handleVideoCallBack(TRTCVideoCallBackReq req);
}

View File

@@ -0,0 +1,26 @@
package com.cool.store.service.impl;
import com.cool.store.mapper.TRTCVideoCallBackMapper;
import com.cool.store.request.TRTCVideoCallBackReq;
import com.cool.store.service.TRTCVideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TRTCVideoServiceImpl implements TRTCVideoService {
@Autowired
private TRTCVideoCallBackMapper videoCallBackMapper;
/**
* 音视频上传成功后的回调处理
*/
@Override
public void handleVideoCallBack(TRTCVideoCallBackReq req) {
//将视频播放地址拼接到对应的面试信息字段中
String videoUrl = req.getEventInfo().getPayLoad().getTencentVod().getVideoUrl();
String roomId = req.getEventInfo().getRoomId();
videoCallBackMapper.addVideoUrl(roomId, videoUrl);
}
}

View File

@@ -0,0 +1,31 @@
package com.cool.store.config;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author: JCccc
* @Date: 2022-6-12 10:35
* @Description:
*/
@Component
@Order(2)
public class BodyWrapperFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest) {
requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest);
}
if(requestWrapper == null) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
filterChain.doFilter(requestWrapper, servletResponse);
}
}
}

View File

@@ -0,0 +1,60 @@
package com.cool.store.config;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @Author: JCccc
* @Date: 2022-6-12 10:36
* @Description: 重写一个自己的 RequestWrapper 拿出body给自己用
*/
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
BufferedReader reader = request.getReader();
try (StringWriter writer = new StringWriter()) {
int read;
char[] buf = new char[1024 * 8];
while ((read = reader.read(buf)) != -1) {
writer.write(buf, 0, read);
}
this.body = writer.getBuffer().toString().getBytes();
}
}
public String getBody(){
return new String(body, StandardCharsets.UTF_8);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}

View File

@@ -0,0 +1,73 @@
package com.cool.store.config;
import com.cool.store.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Base64;
import java.util.stream.Collectors;
/**
* 对腾讯云音视频回调单独验签
*/
@Component
@Order(4)
@Slf4j
public class TRTCCallbackFilter implements Filter {
@Value("${trtc.secretKey}")
private String secretkey;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// HttpServletRequest request = (HttpServletRequest) servletRequest;
// String uri = request.getRequestURI();
// //不是腾讯云音视频回调请求
// if (!uri.startsWith("/partner/pc/video")) {
// filterChain.doFilter(servletRequest, servletResponse);
// return;
// }
// if (!"POST".equals(request.getMethod())) {
// return;
// }
// String requestBody = request.getReader().lines().collect(Collectors.joining());
// //回调的签名
// String sign = request.getHeader("Sign");
// //服务器端生成的签名
// String newSign = "";
// try {
// newSign = getResultSign(secretkey, requestBody);
// } catch (Exception e) {
// log.error("腾讯云音视频回调签名生成错误e\t{}", e.getMessage());
// return;
// }
// if (StringUtil.isEmpty(sign) || !newSign.equals(sign)) {
// log.error("腾讯云音视频回调签名错误, sign\t{}, newSign\t{}", sign, newSign);
// return;
// }
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
private 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()));
}
}

View File

@@ -44,7 +44,9 @@ public class TokenValidateFilter implements Filter {
Lists.newArrayList("/web/check/ok","/check/ok", Lists.newArrayList("/web/check/ok","/check/ok",
"/partner/pc/doc.html","/partner/pc/v2/api-docs","/**/test/**","/partner/pc/feiShuLogin","/partner/pc/oss/getUploadFileConfig", "/partner/pc/doc.html","/partner/pc/v2/api-docs","/**/test/**","/partner/pc/feiShuLogin","/partner/pc/oss/getUploadFileConfig",
"/**/swagger*/**", "/**/webjars/**"); "/**/swagger*/**", "/**/webjars/**",
//腾讯音视频回调,单独做验签
"/partner/pc/video/**");
/** /**

View File

@@ -0,0 +1,42 @@
package com.cool.store.controller;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.request.TRTCVideoCallBackReq;
import com.cool.store.response.ResponseResult;
import com.cool.store.service.TRTCVideoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/video")
@Api(tags = "腾讯音视频接口")
@Slf4j
public class VideoController {
@Autowired
private TRTCVideoService videoService;
@PostMapping("/callback")
@ApiOperation("音视频回调(腾讯云回调)")
public ResponseResult videoCallback(@RequestBody String requestBody) {
//由于腾讯云恶事做尽,它的回调参数不是驼峰法,首字母是大写,导致 SpringMVC 映射不上,只能手动映射了
TRTCVideoCallBackReq req = JSONObject.parseObject(requestBody, TRTCVideoCallBackReq.class);
//不是音视频上传的回调
if (!"311".equals(req.getEventType())) {
return null;
}
if (!"0".equals(req.getEventInfo().getPayLoad().getStatus())) {
log.error("腾讯音视频录制视频上传错误request\t{}", req);
return null;
}
videoService.handleVideoCallBack(req);
return new ResponseResult(0, "腾讯云回调音视频回调数据接收成功");
}
}