Files
zyt/server/app/adminapi/logic/tcm/DiagnosisLogic.php
T
2026-03-04 15:32:30 +08:00

804 lines
27 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | likeadmin快速开发前后端分离管理后台(PHP版)
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | 开源版本可自由商用,可去除界面版权logo
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
// | github下载:https://github.com/likeshop-github/likeadmin
// | 访问官网:https://www.likeadmin.cn
// | likeadmin团队 版权所有 拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeadminTeam
// +----------------------------------------------------------------------
namespace app\adminapi\logic\tcm;
use app\common\logic\BaseLogic;
use app\common\model\tcm\Diagnosis;
/**
* 中医辨房病因诊单逻辑
* Class DiagnosisLogic
* @package app\adminapi\logic\tcm
*/
class DiagnosisLogic extends BaseLogic
{
/**
* @notes 添加诊单
* @param array $params
* @return int|bool 成功返回ID,失败返回false
*/
public static function add(array $params)
{
try {
// 检查手机号是否重复
if (!empty($params['phone'])) {
$exists = Diagnosis::where('phone', $params['phone'])
->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 [];
}
}
}