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); } } } }