diff --git a/app/Controller/CallBackController.php b/app/Controller/CallBackController.php index 61f1248..81af605 100644 --- a/app/Controller/CallBackController.php +++ b/app/Controller/CallBackController.php @@ -712,8 +712,6 @@ class CallBackController extends AbstractController /** * im回调 * @return ResponseInterface - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function imCallBack(): ResponseInterface { @@ -1706,10 +1704,10 @@ class CallBackController extends AbstractController } /** - * 音视频回调 + * 音视频回调-房间与媒体 * @return ResponseInterface */ - public function trtcCallBack(): ResponseInterface + public function videoTrtcCallBack(): ResponseInterface { $request_params = $this->request->getBody()->getContents(); $SdkAppId = $this->request->header("SdkAppId"); @@ -1717,7 +1715,7 @@ class CallBackController extends AbstractController $Sign = stripslashes($Sign); try { - Log::getInstance("CallBackController-trtcCallBack")->info(json_encode($request_params, JSON_UNESCAPED_UNICODE)); + Log::getInstance("CallBackController-videoTrtcCallBack")->info(json_encode($request_params, JSON_UNESCAPED_UNICODE)); if (empty($SdkAppId)) { return $this->TrtcErrorReturn("缺少SdkAppId字段"); @@ -1953,7 +1951,7 @@ class CallBackController extends AbstractController return $this->TrtcErrorReturn($e->getMessage()); } - Log::getInstance("CallBackController-trtcCallBack")->info("音视频回调-房间数据处理成功"); + Log::getInstance("CallBackController-videoTrtcCallBack")->info("音视频回调-房间与媒体处理成功"); return $this->TrtcSuccessReturn(); } @@ -1964,7 +1962,7 @@ class CallBackController extends AbstractController */ protected function TrtcErrorReturn(string $message): ResponseInterface { - Log::getInstance("CallBackController-trtcCallBack")->error($message); + Log::getInstance("CallBackController-videoTrtcCallBack")->error($message); return $this->response ->withStatus(200) ->withBody( @@ -1999,4 +1997,298 @@ class CallBackController extends AbstractController ); } + /** + * 音视频回调-云端录制 + * @return ResponseInterface + */ + public function videoRecordingCallBack(): ResponseInterface + { + $request_params = $this->request->getBody()->getContents(); + $SdkAppId = $this->request->header("SdkAppId"); + $Sign = $this->request->header("Sign"); + $Sign = stripslashes($Sign); + + try { + Log::getInstance("CallBackController-videoRecordingCallBack")->info(json_encode($request_params, JSON_UNESCAPED_UNICODE)); + + if (empty($SdkAppId)) { + return $this->recordingErrorReturn("缺少SdkAppId字段"); + } + + if (empty($Sign)) { + return $this->recordingErrorReturn("缺少签名字段"); + } + + // 鉴定回调签名 + $VideoSafe = new VideoSafe(); + $result = $VideoSafe->validateSign($request_params,$Sign); + if (!$result) { + return $this->recordingErrorReturn("回调签名不匹配"); + } + + $request_params = json_decode($request_params,true); + + // 验证参数 + if (empty($request_params['EventGroupId'])){ + return $this->recordingErrorReturn("回调事件组为空"); + } + + if (empty($request_params['EventType'])){ + return $this->recordingErrorReturn("回调事件类型为空"); + } + + if (empty($request_params['EventInfo'])){ + return $this->recordingErrorReturn("回调事件信息为空"); + } + + if (empty($request_params['EventInfo']['RoomId'])){ + return $this->recordingErrorReturn("缺少房间id"); + } + + if (empty($request_params['EventInfo']['EventMsTs'])){ + return $this->recordingErrorReturn("缺少时间戳"); + } + + // 排除数据 + if ($request_params['EventGroupId'] != 3){ + return $this->recordingErrorReturn("回调地址推送错误"); + } + + if (!in_array($request_params['EventType'],[102,103,201,203])){ + return $this->recordingSuccessReturn(); + } + } catch (\Throwable $e) { + // 验证失败 + return $this->recordingErrorReturn($e->getMessage()); + } + + // 处理业务逻辑 + try { + // 获取视频预约记录 + $params = array(); + $params['room_id'] = $request_params['EventInfo']['RoomId']; + $video_reservation = VideoReservation::getLastOne($params); + if (empty($video_reservation)){ + return $this->recordingErrorReturn("未查询到该房间号,此条数据不接收"); + } + + // 获取视频记录 + $params = array(); + $params['room_id'] = $request_params['EventInfo']['RoomId']; + $params['order_inquiry_id'] = $video_reservation['order_inquiry_id']; + $video_record = VideoRecord::getLastOne($params); + if (empty($video_record)){ + return $this->recordingErrorReturn("缺少视频开启记录"); + } + + // 检测缓存-只允许同时处理同一房间的一个请求,防止数据库冲突 + $redis = $this->container->get(Redis::class); + $redis_key = "trtcCallBack_" . $request_params['EventInfo']['RoomId']; + $redis_value = $redis->get($redis_key); + + // 加入缓存 + $redis->set($redis_key, $request_params['EventType'], 20); + if (!empty($redis_value)){ + if ($redis_value == $request_params['EventType']){ + // 存在正在执行的相同事件,此次事件不处理 + return $this->recordingSuccessReturn(); + } + + // 解散房间 + if ($redis_value == 102){ + // 正在处理结束通话事件,此次事件不处理 + return $this->recordingSuccessReturn(); + } + + // 视频推流 + if ($redis_value == 201){ + if ($request_params['EventType'] == 103){ + // 正在处理视频推流事件,进入房间事件不处理 + return $this->recordingSuccessReturn(); + } + + if ($request_params['EventType'] == 203){ + // 正在处理视频推流事件,音频推流事件不处理 + return $this->recordingSuccessReturn(); + } + } + + // 音频推流 + if ($redis_value == 203){ + if ($request_params['EventType'] == 103){ + // 正在处理视频推流事件,进入房间事件不处理 + return $this->recordingSuccessReturn(); + } + + if ($request_params['EventType'] == 201){ + // 正在处理视频推流事件,视频推流事件不处理 + return $this->recordingSuccessReturn(); + } + } + } + + // 处理业务逻辑 + switch ($request_params['EventType']) { + case '103': // 进入房间 + if (empty($request_params['EventInfo']['UserId'])){ + return $this->recordingErrorReturn("缺少用户id"); + } + + // 获取用户数据 + $params = array(); + $params['user_id'] = $request_params['EventInfo']['UserId']; + $user = User::getOne($params); + if (empty($user)){ + return $this->recordingErrorReturn("非法用户"); + } + + if ($user['user_type'] != 1){ + return $this->recordingSuccessReturn(); + } + + // 检测视频状态 + if ($video_record['video_status'] == 3 || $video_record['video_status'] == 4){ + // 已开始/结束通话,进入房间动作不做处理 + return $this->recordingSuccessReturn(); + } + + // 修改视频状态 + $params = array(); + $params['records_id'] = $video_record['records_id']; + + $data = array(); + $data['video_status'] = 2; + $res = VideoRecord::edit($params,$data); + if (!$res){ + return $this->recordingErrorReturn("修改视频状态失败"); + } + + break; + case '102': // 解散房间 + // 检测视频状态 + if ($video_record['video_status'] == 4){ + // 已结束通话,解散房间动作不做处理 + return $this->recordingSuccessReturn(); + } + + // 修改视频状态 + $params = array(); + $params['records_id'] = $video_record['records_id']; + + $data = array(); + $data['video_status'] = 4; + $data['stop_video_time'] = date('Y-m-d H:i:s',$request_params['EventInfo']['EventMsTs']); + $res = VideoRecord::edit($params,$data); + if (!$res){ + return $this->recordingErrorReturn("修改视频状态失败"); + } + + break; + case '201': // 视频推流 + // 检测视频状态 + if ($video_record['video_status'] == 4){ + // 已结束通话,视频推流动作不做处理 + return $this->recordingSuccessReturn(); + } + + if ($video_record['video_status'] == 3){ + // 已开始通话,视频推流动作不做处理 + return $this->recordingSuccessReturn(); + } + + // 修改视频状态 + $params = array(); + $params['records_id'] = $video_record['records_id']; + + $data = array(); + $data['video_status'] = 3; + $data['stop_video_time'] = date('Y-m-d H:i:s',$request_params['EventInfo']['EventMsTs']); + $res = VideoRecord::edit($params,$data); + if (!$res){ + return $this->recordingErrorReturn("修改视频状态失败"); + } + + break; + case '203': // 音频推流 + // 检测视频状态 + if ($video_record['video_status'] == 4){ + // 已结束通话,音频推流动作不做处理 + return $this->recordingSuccessReturn(); + } + + if ($video_record['video_status'] == 3){ + // 已开始通话,音频推流动作不做处理 + return $this->TrtcSuccessReturn(); + } + + // 修改视频状态 + $params = array(); + $params['records_id'] = $video_record['records_id']; + + $data = array(); + $data['video_status'] = 3; + $data['stop_video_time'] = date('Y-m-d H:i:s',$request_params['EventInfo']['EventMsTs']); + $res = VideoRecord::edit($params,$data); + if (!$res){ + return $this->recordingErrorReturn("修改视频状态失败"); + } + + break; + + default: + return $this->recordingErrorReturn("未知事件"); + } + + // 删除缓存 + $redis->del($redis_key); + }catch (\Throwable $e){ + return $this->recordingErrorReturn($e->getMessage()); + } + + Log::getInstance("CallBackController-videoTrtcCallBack")->info("音视频回调-房间数据处理成功"); + return $this->TrtcSuccessReturn(); + } + + /** + * 音视频回调-云端录制-返回错误响应 + * @param string $message + * @return ResponseInterface + */ + protected function recordingErrorReturn(string $message): ResponseInterface + { + Log::getInstance("CallBackController-videoTrtcCallBack")->error($message); + return $this->response + ->withStatus(200) + ->withBody( + new SwooleStream( + strval( + json_encode([ + 'code' => 0, + 'ErrorInfo' => $message, + ], JSON_UNESCAPED_UNICODE) + ) + ) + ); + } + + /** + * 音视频回调-云端录制-返回正确响应 + * @return ResponseInterface + */ + protected function recordingSuccessReturn(): ResponseInterface + { + return $this->response + ->withStatus(200) + ->withBody( + new SwooleStream( + strval( + json_encode([ + 'code' => 0, + 'ErrorInfo' => "", + ], JSON_UNESCAPED_UNICODE) + ) + ) + ); + } + } \ No newline at end of file diff --git a/app/Model/OrderInquiryVideoRecord.php b/app/Model/OrderInquiryVideoRecord.php new file mode 100644 index 0000000..f8e9f15 --- /dev/null +++ b/app/Model/OrderInquiryVideoRecord.php @@ -0,0 +1,86 @@ +first($fields); + } + + /** + * 获取信息-多条 + * @param array $params + * @param array $fields + * @return object|null + */ + public static function getList(array $params, array $fields = ['*']): object|null + { + return self::where($params)->get($fields); + } + + /** + * 新增-批量 + * @param array $data 新增数据 + * @return \Hyperf\Database\Model\Model|VideoRecord + */ + public static function addVideoRecord(array $data): \Hyperf\Database\Model\Model|VideoRecord + { + return self::create($data); + } + + public static function edit(array $params = [], array $data = []): int + { + return self::where($params)->update($data); + } + + /** + * 获取信息-最后一条 + * @param array $params + * @param array $fields + * @return object|null + */ + public static function getLastOne(array $params, array $fields = ['*']): object|null + { + return self::where($params)->latest()->first($fields); + } + +} diff --git a/app/Model/OrderInquiryVideoReservation.php b/app/Model/OrderInquiryVideoReservation.php new file mode 100644 index 0000000..a230b59 --- /dev/null +++ b/app/Model/OrderInquiryVideoReservation.php @@ -0,0 +1,89 @@ +first($fields); + } + + /** + * 获取信息-多条 + * @param array $params + * @param array $fields + * @return object|null + */ + public static function getList(array $params, array $fields = ['*']): object|null + { + return self::where($params)->get($fields); + } + + /** + * 新增-批量 + * @param array $data 新增数据 + * @return \Hyperf\Database\Model\Model|VideoReservation + */ + public static function addVideoReservation(array $data): \Hyperf\Database\Model\Model|VideoReservation + { + return self::create($data); + } + + public static function edit(array $params = [], array $data = []): int + { + return self::where($params)->update($data); + } + + /** + * 获取信息-最后一条 + * @param array $params + * @param array $fields + * @return object|null + */ + public static function getLastOne(array $params, array $fields = ['*']): object|null + { + return self::where($params)->latest()->first($fields); + } +} diff --git a/config/routes.php b/config/routes.php index a682fbd..eb1878a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -731,7 +731,14 @@ Router::addGroup('/callback', function () { Router::post('/im', [CallBackController::class, 'imCallBack']); // 音视频回调 - Router::post('/trtc', [CallBackController::class, 'trtcCallBack']); + Router::addGroup('/video', function () { + // 房间与媒体 + Router::post('/trtc', [CallBackController::class, 'videoTrtcCallBack']); + + // 云端录制 + Router::post('/recording', [CallBackController::class, 'videoRecordingCallBack']); + }); + // 处方平台物流回调 Router::post('/platform/logistics', [CallBackController::class, 'platformLogisticsCallBack']); diff --git a/extend/TencentVideo/Safe.php b/extend/TencentVideo/Safe.php index e055dc7..f969aa3 100644 --- a/extend/TencentVideo/Safe.php +++ b/extend/TencentVideo/Safe.php @@ -17,7 +17,7 @@ class Safe extends Base } /** - * 验证回调签名 + * 验证视频回调签名 * @param string $body 请求内容提 * @param string $sign 签名 * @return bool