更新
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 腾讯云 TRTC 云端录制 HTTP 回调(需在控制台填写本接口完整 URL)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\common\model\tcm\CallRecord;
|
||||
use think\facade\Log;
|
||||
|
||||
class TrtcController extends BaseApiController
|
||||
{
|
||||
/** 免登录:腾讯云服务器回调 */
|
||||
public array $notNeedLogin = ['recordingNotify'];
|
||||
|
||||
/**
|
||||
* POST /api/trtc/recording-notify
|
||||
* 可选安全:环境变量 TRTC_RECORDING_CALLBACK_TOKEN 非空时,Query 需带 ?token=xxx
|
||||
*/
|
||||
public function recordingNotify()
|
||||
{
|
||||
$token = (string)config('trtc.recording_callback_token', '');
|
||||
if ($token !== '') {
|
||||
$q = (string)($this->request->param('token', ''));
|
||||
if (!hash_equals($token, $q)) {
|
||||
return json(['code' => -1, 'msg' => 'forbidden']);
|
||||
}
|
||||
}
|
||||
|
||||
$raw = $this->request->getContent();
|
||||
$json = json_decode($raw, true);
|
||||
if (!is_array($json)) {
|
||||
Log::warning('TRTC recording callback: invalid json', ['raw' => substr($raw, 0, 500)]);
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
$roomId = $this->extractRoomId($json);
|
||||
$urls = $this->extractRecordingUrls($json);
|
||||
|
||||
if ($roomId === '' || $urls === []) {
|
||||
Log::info('TRTC recording callback: skip (no room or no urls)', [
|
||||
'roomId' => $roomId,
|
||||
'eventType' => $json['EventType'] ?? null,
|
||||
]);
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
$record = CallRecord::where('room_id', $roomId)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
if (!$record) {
|
||||
// 兼容数字房间号与字符串
|
||||
$record = CallRecord::where('room_id', (string)(int)$roomId)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
}
|
||||
if (!$record) {
|
||||
Log::warning('TRTC recording: no call_record for room', ['roomId' => $roomId]);
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
$prev = [];
|
||||
if (!empty($record->recording_urls)) {
|
||||
$prev = json_decode((string)$record->recording_urls, true);
|
||||
if (!is_array($prev)) {
|
||||
$prev = [];
|
||||
}
|
||||
}
|
||||
$merged = array_values(array_unique(array_merge($prev, $urls)));
|
||||
|
||||
$record->save([
|
||||
'recording_urls' => json_encode($merged, JSON_UNESCAPED_UNICODE),
|
||||
'recording_status' => 2,
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
Log::info('TRTC recording saved', ['id' => $record->id, 'urls' => count($merged)]);
|
||||
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
private function extractRoomId(array $data): string
|
||||
{
|
||||
$candidates = [
|
||||
data_get($data, 'EventInfo.RoomId'),
|
||||
data_get($data, 'EventInfo.RoomIdStr'),
|
||||
data_get($data, 'RoomId'),
|
||||
data_get($data, 'room_id'),
|
||||
];
|
||||
foreach ($candidates as $v) {
|
||||
if ($v !== null && $v !== '') {
|
||||
return trim((string)$v);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从回调 JSON 中提取录制文件地址(混流/单路字段名在不同版本可能不同)
|
||||
*/
|
||||
private function extractRecordingUrls(array $data): array
|
||||
{
|
||||
$urls = [];
|
||||
$this->walkForUrls($data, $urls);
|
||||
|
||||
return array_values(array_unique(array_filter($urls)));
|
||||
}
|
||||
|
||||
private function walkForUrls($node, array &$urls): void
|
||||
{
|
||||
if (!is_array($node)) {
|
||||
return;
|
||||
}
|
||||
foreach ($node as $key => $val) {
|
||||
if (is_string($key) && in_array($key, ['VideoUrl', 'FileUrl', 'MediaUrl', 'Url', 'url'], true) && is_string($val)) {
|
||||
$v = trim($val);
|
||||
if ($v !== '' && str_starts_with($v, 'http')) {
|
||||
$urls[] = $v;
|
||||
}
|
||||
}
|
||||
if (is_array($val)) {
|
||||
$this->walkForUrls($val, $urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user