新增功能

This commit is contained in:
Your Name
2026-03-04 15:32:30 +08:00
parent a07e844c47
commit ab77f5488d
2266 changed files with 177942 additions and 3444 deletions
@@ -0,0 +1,803 @@
<?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 [];
}
}
}