新增功能

This commit is contained in:
Your Name
2026-04-01 09:46:25 +08:00
parent 099bc1dd22
commit a780356908
332 changed files with 7845 additions and 866 deletions
+27 -2
View File
@@ -28,7 +28,7 @@ class TcmController extends BaseApiController
* @notes 不需要登录的方法
* @var array
*/
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo'];
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo'];
/**
* @notes 获取患者签名(供小程序调用)
@@ -51,7 +51,32 @@ class TcmController extends BaseApiController
return $this->data($result);
}
/**
* @notes 患者挂断视频:后端 DeleteCloudRecording + 结束通话记录(与诊单 patient_id 校验)
* @return \think\response\Json
*/
public function patientHangupVideo()
{
$params = [
'diagnosis_id' => (int)$this->request->post('diagnosis_id', 0),
'patient_id' => (int)$this->request->post('patient_id', 0),
'room_id' => trim((string)$this->request->post('room_id', '')),
];
if ($params['patient_id'] <= 0) {
return $this->fail('患者ID不能为空');
}
if ($params['diagnosis_id'] <= 0 && $params['room_id'] === '') {
return $this->fail('诊单ID或房间号须至少传一项');
}
$result = DiagnosisLogic::patientHangupVideoCall($params);
if ($result === false) {
return $this->fail(DiagnosisLogic::getError());
}
return $this->success('已结束通话');
}
/**
* @notes 获取诊单详情(供小程序调用)
* @return \think\response\Json
+67 -19
View File
@@ -17,7 +17,7 @@ class TrtcController extends BaseApiController
public array $notNeedLogin = ['recordingNotify'];
/**
* POST /api/trtc/recording-notify
* POST /api/trtc/recording-notify 或 /api/trtc/recordingNotify(与控制台回调 URL 一致即可)
* 可选安全:环境变量 TRTC_RECORDING_CALLBACK_TOKEN 非空时,Query 需带 ?token=xxx
*/
public function recordingNotify()
@@ -37,28 +37,41 @@ class TrtcController extends BaseApiController
return json(['code' => 0, 'msg' => 'ok']);
}
$eventType = (int)($json['EventType'] ?? 0);
$roomId = $this->extractRoomId($json);
$taskId = trim((string)data_get($json, 'EventInfo.TaskId', ''));
$urls = $this->extractRecordingUrls($json);
if ($roomId === '' || $urls === []) {
Log::info('TRTC recording callback: skip (no room or no urls)', [
'roomId' => $roomId,
'eventType' => $json['EventType'] ?? null,
]);
if ($urls === []) {
// 301–308 等为进度事件,无播放地址;311=VOD 上传完成带 VideoUrl(见文档 81113
$mayHaveUrl = in_array($eventType, [309, 310, 311], true);
$payload = data_get($json, 'EventInfo.Payload');
if ($urls === [] && $eventType === 311) {
Log::warning('TRTC recording callback: 311 但未解析到 VideoUrl(可能 Status!=0 或字段名变更)', [
'PayloadStatus' => is_array($payload) ? ($payload['Status'] ?? null) : null,
'Errmsg' => is_array($payload) ? ($payload['Errmsg'] ?? $payload['ErrMsg'] ?? null) : null,
'TaskId' => $taskId !== '' ? $taskId : null,
'roomId' => $roomId !== '' ? $roomId : null,
]);
}
Log::info(
'TRTC recording callback: skip (no playback url in payload) '
. 'EventType=' . $eventType
. ' EventGroupId=' . (string)($json['EventGroupId'] ?? '')
. ' TaskId=' . ($taskId !== '' ? $taskId : '-')
. ' roomId=' . ($roomId !== '' ? $roomId : '-')
. ' ' . ($mayHaveUrl ? 'expect_url' : 'progress_ok')
);
return json(['code' => 0, 'msg' => 'ok']);
}
$record = CallRecord::where('room_id', $roomId)
->order('id', 'desc')
->find();
$record = $this->resolveCallRecordForNotify($roomId, $taskId);
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]);
Log::warning('TRTC recording: no call_record (try room_id + cloud_recording_task_id)', [
'roomId' => $roomId,
'TaskId' => $taskId,
'EventType' => $eventType,
]);
return json(['code' => 0, 'msg' => 'ok']);
}
@@ -88,18 +101,52 @@ class TrtcController extends BaseApiController
$candidates = [
data_get($data, 'EventInfo.RoomId'),
data_get($data, 'EventInfo.RoomIdStr'),
data_get($data, 'EventInfo.StrRoomId'),
data_get($data, 'EventInfo.Payload.RoomId'),
data_get($data, 'EventInfo.Payload.RoomIdStr'),
data_get($data, 'RoomId'),
data_get($data, 'room_id'),
];
foreach ($candidates as $v) {
if ($v !== null && $v !== '') {
return trim((string)$v);
$s = trim((string)$v);
if ($s !== '' && $s !== '0') {
return $s;
}
}
}
return '';
}
/**
* 先按 room_id,再按 CreateCloudRecording 返回的 TaskId(须库中仍保留 cloud_recording_task_id
*/
private function resolveCallRecordForNotify(string $roomId, string $taskId): ?CallRecord
{
if ($roomId !== '') {
$record = CallRecord::where('room_id', $roomId)->order('id', 'desc')->find();
if ($record) {
return $record;
}
if (ctype_digit($roomId)) {
$norm = (string)(int)$roomId;
$record = CallRecord::where('room_id', $norm)->order('id', 'desc')->find();
if ($record) {
return $record;
}
}
}
if ($taskId !== '') {
$record = CallRecord::where('cloud_recording_task_id', $taskId)->order('id', 'desc')->find();
if ($record) {
return $record;
}
}
return null;
}
/**
* 从回调 JSON 中提取录制文件地址(混流/单路字段名在不同版本可能不同)
*/
@@ -117,9 +164,10 @@ class TrtcController extends BaseApiController
return;
}
foreach ($node as $key => $val) {
if (is_string($key) && in_array($key, ['VideoUrl', 'FileUrl', 'MediaUrl', 'Url', 'url'], true) && is_string($val)) {
$keyLower = is_string($key) ? strtolower($key) : '';
if (in_array($keyLower, ['videourl', 'fileurl', 'mediaurl', 'url', 'streamurl', 'playurl'], true) && is_string($val)) {
$v = trim($val);
if ($v !== '' && str_starts_with($v, 'http')) {
if ($v !== '' && preg_match('#^https?://#i', $v) === 1) {
$urls[] = $v;
}
}