where('delete_time', null) ->find(); if ($exists) { self::setError('该手机号已存在'); return false; } } // 检查身份证号是否重复 if (!empty($params['id_card'])) { $exists = Diagnosis::where('id_card', $params['id_card']) ->where('delete_time', null) ->find(); if ($exists) { self::setError('该身份证号已存在'); return false; } } // 生成患者ID $params['patient_id'] = self::generatePatientId(); // 处理既往史数组 if (isset($params['past_history']) && is_array($params['past_history'])) { $params['past_history'] = implode(',', $params['past_history']); } // 处理现病史多选字段 $multiSelectFields = [ 'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition', 'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition', 'stool_condition', 'kidney_condition' ]; foreach ($multiSelectFields as $field) { if (isset($params[$field]) && is_array($params[$field])) { $params[$field] = implode(',', $params[$field]); } } // 处理舌苔照片(JSON数组) if (isset($params['tongue_images']) && is_array($params['tongue_images'])) { $params['tongue_images'] = json_encode($params['tongue_images'], JSON_UNESCAPED_UNICODE); } // 处理检查报告(JSON数组) if (isset($params['report_files']) && is_array($params['report_files'])) { $params['report_files'] = json_encode($params['report_files'], JSON_UNESCAPED_UNICODE); } // 处理诊断日期 if (isset($params['diagnosis_date'])) { $params['diagnosis_date'] = strtotime($params['diagnosis_date']); } $model = Diagnosis::create($params); // 自动为患者创建 TRTC 账号 self::createPatientTrtcAccount($model->patient_id); return $model->id; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 生成患者ID * @return int */ private static function generatePatientId(): int { // 获取当前最大的患者ID $maxPatientId = Diagnosis::max('patient_id') ?? 100000; // 返回下一个患者ID return $maxPatientId + 1; } /** * @notes 编辑诊单 * @param array $params * @return bool */ public static function edit(array $params): bool { try { // 检查手机号是否重复(排除当前记录) if (!empty($params['phone'])) { $exists = Diagnosis::where('phone', $params['phone']) ->where('id', '<>', $params['id']) ->where('delete_time', null) ->find(); if ($exists) { self::setError('该手机号已存在'); return false; } } // 检查身份证号是否重复(排除当前记录) if (!empty($params['id_card'])) { $exists = Diagnosis::where('id_card', $params['id_card']) ->where('id', '<>', $params['id']) ->where('delete_time', null) ->find(); if ($exists) { self::setError('该身份证号已存在'); return false; } } // 处理既往史数组 if (isset($params['past_history']) && is_array($params['past_history'])) { $params['past_history'] = implode(',', $params['past_history']); } // 处理现病史多选字段 $multiSelectFields = [ 'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition', 'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition', 'stool_condition', 'kidney_condition' ]; foreach ($multiSelectFields as $field) { if (isset($params[$field]) && is_array($params[$field])) { $params[$field] = implode(',', $params[$field]); } } // 处理舌苔照片(JSON数组) if (isset($params['tongue_images']) && is_array($params['tongue_images'])) { $params['tongue_images'] = json_encode($params['tongue_images'], JSON_UNESCAPED_UNICODE); } // 处理检查报告(JSON数组) if (isset($params['report_files']) && is_array($params['report_files'])) { $params['report_files'] = json_encode($params['report_files'], JSON_UNESCAPED_UNICODE); } // 处理诊断日期 if (isset($params['diagnosis_date'])) { $params['diagnosis_date'] = strtotime($params['diagnosis_date']); } Diagnosis::update($params); // 如果是编辑,也确保患者有 TRTC 账号 $diagnosis = Diagnosis::find($params['id']); if ($diagnosis) { self::createPatientTrtcAccount($diagnosis->patient_id); } return true; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 删除诊单 * @param array $params * @return bool */ public static function delete(array $params): bool { try { Diagnosis::destroy($params['id']); return true; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 诊单详情 * @param $params * @return array */ public static function detail($params): array { $diagnosis = Diagnosis::findOrEmpty($params['id'])->toArray(); // 处理既往史为数组 if (!empty($diagnosis['past_history'])) { $diagnosis['past_history'] = explode(',', $diagnosis['past_history']); } else { $diagnosis['past_history'] = []; } // 处理现病史多选字段为数组 $multiSelectFields = [ 'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition', 'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition', 'stool_condition', 'kidney_condition' ]; foreach ($multiSelectFields as $field) { if (!empty($diagnosis[$field])) { $diagnosis[$field] = explode(',', $diagnosis[$field]); } else { $diagnosis[$field] = []; } } // 处理舌苔照片(JSON转数组) if (!empty($diagnosis['tongue_images'])) { $diagnosis['tongue_images'] = json_decode($diagnosis['tongue_images'], true) ?: []; } else { $diagnosis['tongue_images'] = []; } // 处理检查报告(JSON转数组) if (!empty($diagnosis['report_files'])) { $diagnosis['report_files'] = json_decode($diagnosis['report_files'], true) ?: []; } else { $diagnosis['report_files'] = []; } // 处理诊断日期格式 if (!empty($diagnosis['diagnosis_date'])) { $diagnosis['diagnosis_date'] = date('Y-m-d', $diagnosis['diagnosis_date']); } return $diagnosis; } /** * @notes 检查手机号是否重复 * @param array $params * @return array */ public static function checkPhone(array $params): array { $query = Diagnosis::where('phone', $params['phone']) ->where('delete_time', null); // 编辑时排除当前记录 if (isset($params['id']) && $params['id']) { $query->where('id', '<>', $params['id']); } $exists = $query->find(); return [ 'exists' => !empty($exists), 'message' => $exists ? '该手机号已存在' : '' ]; } /** * @notes 检查身份证号是否重复 * @param array $params * @return array */ public static function checkIdCard(array $params): array { $query = Diagnosis::where('id_card', $params['id_card']) ->where('delete_time', null); // 编辑时排除当前记录 if (isset($params['id']) && $params['id']) { $query->where('id', '<>', $params['id']); } $exists = $query->find(); return [ 'exists' => !empty($exists), 'message' => $exists ? '该身份证号已存在' : '' ]; } /** * @notes 指派医助 * @param array $params * @return bool */ public static function assign(array $params): bool { try { Diagnosis::where('id', $params['id'])->update([ 'assistant_id' => $params['assistant_id'] ]); return true; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 获取通话签名 * @param array $params * @return array|bool */ public static function getCallSignature(array $params) { try { // 获取配置 $config = self::getTrtcConfig(); if (!$config) { self::setError('请先配置腾讯云TRTC参数'); return false; } // 获取当前管理员ID(从参数中获取) $adminId = $params['admin_id'] ?? 0; $patientId = $params['patient_id'] ?? 0; if (!$adminId) { self::setError('获取管理员信息失败'); return false; } if (!$patientId) { self::setError('获取患者信息失败'); return false; } // 医生userId $doctorUserId = 'doctor_' . $adminId; // 患者userId(必须与小程序端一致) $patientUserId = 'patient_' . $patientId; // 生成医生的 UserSig $userSig = self::generateUserSig($config['sdkAppId'], $config['secretKey'], $doctorUserId); if (!$userSig) { self::setError('生成签名失败'); return false; } // 导入医生账号到IM self::importDoctorAccountToIm($adminId, $doctorUserId); // 确保患者账号也已导入IM(用于跨平台通话) self::ensurePatientImAccount($patientId, $patientUserId); return [ 'sdkAppId' => (int)$config['sdkAppId'], // 确保返回整数 'userId' => $doctorUserId, // 医生的userId 'userSig' => $userSig, 'patientUserId' => $patientUserId, // 患者的userId(用于发起通话) 'expireTime' => 86400 // 24小时 ]; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 确保患者IM账号存在 * @param int $patientId * @param string $userId * @return bool */ private static function ensurePatientImAccount(int $patientId, string $userId): bool { try { // 获取患者信息 $diagnosis = Diagnosis::where('patient_id', $patientId)->find(); $nick = $diagnosis ? $diagnosis->patient_name : '患者' . $patientId; // 调用IM服务导入账号 $imService = new \app\common\service\TencentImService(); $result = $imService->importAccount($userId, $nick); if ($result['success']) { \think\facade\Log::info('确保患者IM账号存在 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', nick: ' . $nick); return true; } else { \think\facade\Log::warning('导入患者IM账号失败 - patient_id: ' . $patientId . ', error: ' . ($result['message'] ?? '未知错误')); return false; } } catch (\Exception $e) { \think\facade\Log::error('确保患者IM账号失败: ' . $e->getMessage()); return false; } } /** * @notes 导入医生账号到腾讯云IM * @param int $adminId * @param string $userId * @return bool */ private static function importDoctorAccountToIm(int $adminId, string $userId): bool { try { // 获取医生信息 $admin = \app\common\model\auth\Admin::find($adminId); $nick = $admin ? $admin->name : '医生' . $adminId; // 调用IM服务导入账号 $imService = new \app\common\service\TencentImService(); $result = $imService->importAccount($userId, $nick); if ($result['success']) { \think\facade\Log::info('导入医生IM账号成功 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', nick: ' . $nick); return true; } else { \think\facade\Log::warning('导入医生IM账号失败 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', error: ' . $result['message']); return false; } } catch (\Exception $e) { \think\facade\Log::error('导入医生IM账号异常 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', error: ' . $e->getMessage()); return false; } } /** * @notes 发起通话 * @param array $params * @return bool */ public static function startCall(array $params): bool { try { // 获取当前管理员ID(从参数中获取) $adminId = $params['admin_id'] ?? 0; if (!$adminId) { self::setError('获取管理员信息失败'); return false; } // 创建通话记录 \app\common\model\tcm\CallRecord::create([ 'diagnosis_id' => $params['diagnosis_id'], 'caller_id' => $adminId, 'caller_type' => 'doctor', 'callee_id' => $params['patient_id'] ?? 0, 'callee_type' => 'patient', 'call_type' => $params['call_type'] ?? 2, // 1-语音 2-视频 'status' => 1, // 1-进行中 'start_time' => time() ]); return true; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 结束通话 * @param array $params * @return bool */ public static function endCall(array $params): bool { try { // 获取当前管理员ID(从参数中获取) $adminId = $params['admin_id'] ?? 0; if (!$adminId) { self::setError('获取管理员信息失败'); return false; } // 查找最近的通话记录 $record = \app\common\model\tcm\CallRecord::where('diagnosis_id', $params['diagnosis_id']) ->where('caller_id', $adminId) ->where('status', 1) ->order('id', 'desc') ->find(); if ($record) { $endTime = time(); $duration = $endTime - $record->start_time; $record->save([ 'status' => 2, // 2-已结束 'end_time' => $endTime, 'duration' => $duration ]); } return true; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 获取通话记录 * @param array $params * @return array */ public static function getCallRecords(array $params): array { try { $records = \app\common\model\tcm\CallRecord::where('diagnosis_id', $params['diagnosis_id']) ->order('id', 'desc') ->select() ->toArray(); foreach ($records as &$record) { $record['start_time_text'] = date('Y-m-d H:i:s', $record['start_time']); $record['end_time_text'] = $record['end_time'] ? date('Y-m-d H:i:s', $record['end_time']) : ''; $record['duration_text'] = self::formatDuration($record['duration']); } return $records; } catch (\Exception $e) { self::setError($e->getMessage()); return []; } } /** * @notes 获取TRTC配置 * @return array|null */ private static function getTrtcConfig(): ?array { // 从配置文件或数据库读取 // 这里使用配置文件方式 $config = config('project.trtc'); if (empty($config['sdkAppId']) || empty($config['secretKey'])) { return null; } return $config; } /** * @notes 生成UserSig * @param int $sdkAppId * @param string $secretKey * @param string $userId * @param int $expire * @return string|false */ private static function generateUserSig(int $sdkAppId, string $secretKey, string $userId, int $expire = 86400) { try { // 使用官方的TLSSigAPIv2类生成UserSig $api = new \app\common\service\TLSSigAPIv2($sdkAppId, $secretKey); $userSig = $api->genUserSig($userId, $expire); return $userSig; } catch (\Exception $e) { \think\facade\Log::error('生成UserSig失败: ' . $e->getMessage()); return false; } } /** * @notes 格式化通话时长 * @param int $seconds * @return string */ private static function formatDuration(int $seconds): string { if ($seconds < 60) { return $seconds . '秒'; } elseif ($seconds < 3600) { $minutes = floor($seconds / 60); $secs = $seconds % 60; return $minutes . '分' . $secs . '秒'; } else { $hours = floor($seconds / 3600); $minutes = floor(($seconds % 3600) / 60); $secs = $seconds % 60; return $hours . '小时' . $minutes . '分' . $secs . '秒'; } } /** * @notes 为患者创建 TRTC 账号(自动调用) * @param int $patientId * @return bool */ private static function createPatientTrtcAccount(int $patientId): bool { try { $config = self::getTrtcConfig(); if (!$config || !$config['enable']) { // 如果 TRTC 未启用,不创建账号 return false; } $userId = 'patient_' . $patientId; // 检查是否已存在 $existingAccount = \app\common\model\tcm\PatientTrtc::where('patient_id', $patientId) ->where('delete_time', null) ->find(); // 生成 UserSig(长期有效,180天) $expireTime = 86400 * 180; $userSig = self::generateUserSig($config['sdkAppId'], $config['secretKey'], $userId, $expireTime); if (!$userSig) { return false; } $data = [ 'patient_id' => $patientId, 'user_id' => $userId, 'user_sig' => $userSig, 'sdk_app_id' => $config['sdkAppId'], 'expire_time' => time() + $expireTime, 'is_active' => 1 ]; if ($existingAccount) { // 更新现有账号 $existingAccount->save($data); \think\facade\Log::info('更新患者 TRTC 账号 - patient_id: ' . $patientId . ', user_id: ' . $userId); } else { // 创建新账号 \app\common\model\tcm\PatientTrtc::create($data); \think\facade\Log::info('创建患者 TRTC 账号 - patient_id: ' . $patientId . ', user_id: ' . $userId); } // 导入账号到腾讯云IM(用于TUICallKit) self::importAccountToTencentIm($patientId, $userId); return true; } catch (\Exception $e) { \think\facade\Log::error('创建患者 TRTC 账号失败 - patient_id: ' . $patientId . ', error: ' . $e->getMessage()); return false; } } /** * @notes 导入账号到腾讯云IM * @param int $patientId * @param string $userId * @return bool */ private static function importAccountToTencentIm(int $patientId, string $userId): bool { try { // 获取患者信息 $diagnosis = Diagnosis::where('patient_id', $patientId) ->order('id', 'desc') ->find(); $nick = $diagnosis ? $diagnosis->patient_name : '患者' . $patientId; // 调用IM服务导入账号 $imService = new \app\common\service\TencentImService(); $result = $imService->importAccount($userId, $nick); if ($result['success']) { \think\facade\Log::info('导入患者IM账号成功 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', nick: ' . $nick); return true; } else { \think\facade\Log::warning('导入患者IM账号失败 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', error: ' . $result['message']); return false; } } catch (\Exception $e) { \think\facade\Log::error('导入患者IM账号异常 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', error: ' . $e->getMessage()); return false; } } /** * @notes 获取患者签名(供小程序调用) * @param int $patientId * @return array|bool */ public static function getPatientSignature(int $patientId) { try { // 获取配置 $config = self::getTrtcConfig(); if (!$config) { self::setError('请先配置腾讯云TRTC参数'); return false; } // 患者userId $patientUserId = 'patient_' . $patientId; // 生成患者的 UserSig $userSig = self::generateUserSig( $config['sdkAppId'], $config['secretKey'], $patientUserId ); if (!$userSig) { self::setError('生成签名失败'); return false; } // 确保患者账号已导入IM self::ensurePatientImAccount($patientId, $patientUserId); \think\facade\Log::info('小程序获取患者签名成功 - patient_id: ' . $patientId . ', user_id: ' . $patientUserId); return [ 'sdkAppId' => (int)$config['sdkAppId'], 'userId' => $patientUserId, 'userSig' => $userSig, 'expireTime' => 86400 ]; } catch (\Exception $e) { \think\facade\Log::error('获取患者签名失败 - patient_id: ' . $patientId . ', error: ' . $e->getMessage()); self::setError($e->getMessage()); return false; } } /** * @notes 获取医助列表 * @return array */ public static function getAssistants() { try { // 通过中间表查询角色ID为2的管理员(医助) $assistants = \app\common\model\auth\Admin::alias('a') ->join('admin_role ar', 'a.id = ar.admin_id') ->where('ar.role_id', 2) ->where('a.disable', 0) ->field(['a.id', 'a.name', 'a.account']) ->order('a.id', 'asc') ->select() ->toArray(); return $assistants; } catch (\Exception $e) { \think\facade\Log::error('获取医助列表失败: ' . $e->getMessage()); return []; } } /** * @notes 获取医生列表 * @return array */ public static function getDoctors() { try { // 通过中间表查询角色ID为1的管理员(医生) $doctors = \app\common\model\auth\Admin::alias('a') ->join('admin_role ar', 'a.id = ar.admin_id') ->where('ar.role_id', 1) ->where('a.disable', 0) ->field(['a.id', 'a.name', 'a.account']) ->order('a.id', 'asc') ->select() ->toArray(); return $doctors; } catch (\Exception $e) { \think\facade\Log::error('获取医生列表失败: ' . $e->getMessage()); return []; } } }