新增功能
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\controller\doctor;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\doctor\AppointmentLists;
|
||||
use app\adminapi\logic\doctor\AppointmentLogic;
|
||||
use app\adminapi\validate\doctor\AppointmentValidate;
|
||||
|
||||
/**
|
||||
* 医生预约控制器
|
||||
* Class AppointmentController
|
||||
* @package app\adminapi\controller\doctor
|
||||
*/
|
||||
class AppointmentController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function availableSlots()
|
||||
{
|
||||
$params = (new AppointmentValidate())->goCheck('availableSlots');
|
||||
$result = AppointmentLogic::getAvailableSlots($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 创建预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('create');
|
||||
$result = AppointmentLogic::create($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('预约成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('cancel');
|
||||
$result = AppointmentLogic::cancel($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('取消成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new AppointmentLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new AppointmentValidate())->goCheck('detail');
|
||||
$result = AppointmentLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生可用号源数
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function doctorAvailability()
|
||||
{
|
||||
$doctorId = $this->request->get('doctor_id');
|
||||
$date = $this->request->get('date');
|
||||
|
||||
if (!$doctorId || !$date) {
|
||||
return $this->fail('参数错误');
|
||||
}
|
||||
|
||||
$availableCount = AppointmentLogic::getDoctorAvailability($doctorId, $date);
|
||||
return $this->data(['available_count' => $availableCount]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function complete()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('complete');
|
||||
$result = AppointmentLogic::complete($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\controller\doctor;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\doctor\RosterLists;
|
||||
use app\adminapi\logic\doctor\RosterLogic;
|
||||
use app\adminapi\validate\doctor\RosterValidate;
|
||||
|
||||
/**
|
||||
* 医生排班控制器
|
||||
* Class RosterController
|
||||
* @package app\adminapi\controller\doctor
|
||||
*/
|
||||
class RosterController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 排班列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new RosterLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 保存排班(新增或更新)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('save');
|
||||
$result = RosterLogic::save($params);
|
||||
return $this->success('保存成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('delete');
|
||||
RosterLogic::delete($params);
|
||||
return $this->success('删除成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 排班详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new RosterValidate())->goCheck('detail');
|
||||
$result = RosterLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function batchSave()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('batchSave');
|
||||
$result = RosterLogic::batchSave($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(RosterLogic::getError());
|
||||
}
|
||||
return $this->success('批量保存成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('copy');
|
||||
$result = RosterLogic::copy($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(RosterLogic::getError());
|
||||
}
|
||||
return $this->success('复制成功', []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\controller\tcm;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\logic\tcm\BloodRecordLogic;
|
||||
|
||||
/**
|
||||
* 血糖血压记录控制器
|
||||
* Class BloodRecordController
|
||||
* @package app\adminapi\controller\tcm
|
||||
*/
|
||||
class BloodRecordController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 添加记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::add($params);
|
||||
if ($result) {
|
||||
return $this->success('添加成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::edit($params);
|
||||
if ($result) {
|
||||
return $this->success('编辑成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::delete($params);
|
||||
if ($result) {
|
||||
return $this->success('删除成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 记录详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
$result = BloodRecordLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者的记录列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getRecordsByPatient()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
$result = BloodRecordLogic::getRecordsByPatient($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?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\controller\tcm;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\tcm\DiagnosisLists;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\adminapi\validate\tcm\DiagnosisValidate;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单控制器
|
||||
* Class DiagnosisController
|
||||
* @package app\adminapi\controller\tcm
|
||||
*/
|
||||
class DiagnosisController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 测试接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
return $this->success('中医诊单控制器可以访问', [
|
||||
'controller' => 'TcmDiagnosisController',
|
||||
'namespace' => __NAMESPACE__,
|
||||
'class' => __CLASS__,
|
||||
'method' => __METHOD__
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊单列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new DiagnosisLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 添加诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('add');
|
||||
$result = DiagnosisLogic::add($params);
|
||||
if ($result) {
|
||||
return $this->success('添加成功', ['id' => $result], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('edit');
|
||||
$result = DiagnosisLogic::edit($params);
|
||||
if ($result) {
|
||||
return $this->success('编辑成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('id');
|
||||
$result = DiagnosisLogic::delete($params);
|
||||
if ($result) {
|
||||
return $this->success('删除成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊单详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->goCheck('id');
|
||||
$result = DiagnosisLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查手机号是否重复
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function checkPhone()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = DiagnosisLogic::checkPhone($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查身份证号是否重复
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function checkIdCard()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = DiagnosisLogic::checkIdCard($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 指派医助
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function assign()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
// 验证参数
|
||||
if (empty($params['id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
if (!isset($params['assistant_id'])) {
|
||||
return $this->fail('请选择医助');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::assign($params);
|
||||
if ($result) {
|
||||
return $this->success('指派成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话签名
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getCallSignature()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
if (empty($params['patient_id'])) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::getCallSignature($params);
|
||||
if ($result) {
|
||||
return $this->data($result);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发起通话
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function startCall()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::startCall($params);
|
||||
if ($result) {
|
||||
return $this->success('发起通话成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 结束通话
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function endCall()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::endCall($params);
|
||||
if ($result) {
|
||||
return $this->success('结束通话成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getCallRecords()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::getCallRecords($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者通话签名(用于测试)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getPatientSignature()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
|
||||
if (empty($params['patient_id'])) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::getPatientSignature((int)$params['patient_id']);
|
||||
if ($result) {
|
||||
return $this->data($result);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医助列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getAssistants()
|
||||
{
|
||||
$result = DiagnosisLogic::getAssistants();
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getDoctors()
|
||||
{
|
||||
$result = DiagnosisLogic::getDoctors();
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\lists\doctor;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 医生预约列表
|
||||
* Class AppointmentLists
|
||||
* @package app\adminapi\lists\doctor
|
||||
*/
|
||||
class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
$allowSearch = [
|
||||
'=' => ['a.patient_id', 'a.doctor_id', 'a.status', 'a.appointment_date'],
|
||||
];
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$this->searchWhere[] = ['a.appointment_date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
// 处理患者姓名搜索
|
||||
if (!empty($this->params['patient_name'])) {
|
||||
$this->searchWhere[] = ['u.patient_name', 'like', '%' . $this->params['patient_name'] . '%'];
|
||||
}
|
||||
|
||||
// 处理医生姓名搜索
|
||||
if (!empty($this->params['doctor_name'])) {
|
||||
$this->searchWhere[] = ['ad.name', 'like', '%' . $this->params['doctor_name'] . '%'];
|
||||
}
|
||||
|
||||
return $allowSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
|
||||
$lists = Appointment::alias('a')
|
||||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||||
->leftJoin('admin ad', 'a.doctor_id = ad.id')
|
||||
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, ad.name as doctor_name')
|
||||
->where($this->searchWhere)
|
||||
->order('a.id', 'desc')
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 添加状态和类型描述
|
||||
foreach ($lists as &$item) {
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
$item['status_desc'] = $statusMap[$item['status']] ?? '未知';
|
||||
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
$item['appointment_type_desc'] = $typeMap[$item['appointment_type']] ?? '未知';
|
||||
|
||||
// 格式化时间戳为日期时间
|
||||
if (isset($item['create_time']) && is_numeric($item['create_time'])) {
|
||||
$item['create_time'] = date('Y-m-d H:i:s', $item['create_time']);
|
||||
}
|
||||
if (isset($item['update_time']) && is_numeric($item['update_time'])) {
|
||||
$item['update_time'] = date('Y-m-d H:i:s', $item['update_time']);
|
||||
}
|
||||
}
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$query = Appointment::alias('a')
|
||||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||||
->leftJoin('admin ad', 'a.doctor_id = ad.id')
|
||||
->where($this->searchWhere);
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\lists\doctor;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\doctor\Roster;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 医生排班列表
|
||||
* Class RosterLists
|
||||
* @package app\adminapi\lists\doctor
|
||||
*/
|
||||
class RosterLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'=' => ['doctor_id', 'period', 'status'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
$lists = Roster::where($where)
|
||||
->field(['id', 'doctor_id', 'date', 'period', 'status', 'quota', 'max_patients', 'booked_count', 'remark', 'create_time', 'update_time'])
|
||||
->order(['date' => 'asc', 'period' => 'asc'])
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
return Roster::where($where)->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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\lists\tcm;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单列表
|
||||
* Class DiagnosisLists
|
||||
* @package app\adminapi\lists\tcm
|
||||
*/
|
||||
class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'%like%' => ['patient_name'],
|
||||
'=' => ['patient_id', 'gender', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status'],
|
||||
'between_time' => ['diagnosis_date'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
$lists = Diagnosis::where($this->searchWhere)
|
||||
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status', 'create_time', 'update_time'])
|
||||
->append(['gender_desc', 'status_desc', 'diagnosis_date_text'])
|
||||
->order(['id' => 'desc'])
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return Diagnosis::where($this->searchWhere)->count();
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,10 @@ use app\common\model\auth\AdminRole;
|
||||
use app\common\model\auth\AdminSession;
|
||||
use app\common\cache\AdminTokenCache;
|
||||
use app\common\service\FileService;
|
||||
use app\common\service\TencentImService;
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 管理员逻辑
|
||||
@@ -67,6 +69,9 @@ class AdminLogic extends BaseLogic
|
||||
// 岗位
|
||||
self::insertJobs($admin['id'], $params['jobs_id'] ?? []);
|
||||
|
||||
// 导入医生账号到腾讯云IM
|
||||
self::importDoctorAccountToIm($admin['id'], $params['name']);
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -134,6 +139,9 @@ class AdminLogic extends BaseLogic
|
||||
// 岗位
|
||||
self::insertJobs($params['id'], $params['jobs_id'] ?? []);
|
||||
|
||||
// 导入医生账号到腾讯云IM
|
||||
self::importDoctorAccountToIm($params['id'], $params['name']);
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -334,4 +342,33 @@ class AdminLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 导入医生账号到腾讯云IM
|
||||
* @param int $adminId 管理员ID
|
||||
* @param string $name 管理员名称
|
||||
* @return void
|
||||
* @author AI Assistant
|
||||
* @date 2026/03/02
|
||||
*/
|
||||
public static function importDoctorAccountToIm($adminId, $name)
|
||||
{
|
||||
try {
|
||||
$userId = 'doctor_' . $adminId;
|
||||
|
||||
Log::info('开始导入医生IM账号 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', name: ' . $name);
|
||||
|
||||
$imService = new TencentImService();
|
||||
$result = $imService->importAccount($userId, $name);
|
||||
|
||||
if ($result) {
|
||||
Log::info('医生IM账号导入成功 - admin_id: ' . $adminId . ', user_id: ' . $userId);
|
||||
} else {
|
||||
Log::warning('医生IM账号导入失败 - admin_id: ' . $adminId . ', user_id: ' . $userId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('导入医生IM账号异常 - admin_id: ' . $adminId . ', error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\logic\doctor;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\model\doctor\Roster;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 医生预约逻辑
|
||||
* Class AppointmentLogic
|
||||
* @package app\adminapi\logic\doctor
|
||||
*/
|
||||
class AppointmentLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableSlots(array $params)
|
||||
{
|
||||
try {
|
||||
$doctorId = $params['doctor_id'];
|
||||
$date = $params['appointment_date'];
|
||||
$period = $params['period'] ?? 'all';
|
||||
|
||||
// 记录请求参数
|
||||
\think\facade\Log::info('获取可用时段 - 请求参数', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'period' => $period
|
||||
]);
|
||||
|
||||
// 1. 检查医生在该日期是否有出诊排班(只查询status=1的记录)
|
||||
$rosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'status' => 1 // 只查询出诊状态
|
||||
])->select()->toArray();
|
||||
|
||||
// 记录查询到的排班
|
||||
\think\facade\Log::info('查询到的排班数据', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'count' => count($rosters),
|
||||
'rosters' => $rosters
|
||||
]);
|
||||
|
||||
// 额外记录:查询该日期所有排班(用于调试)
|
||||
$allRosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date
|
||||
])->select()->toArray();
|
||||
\think\facade\Log::info('该日期所有排班数据(包括非出诊)', [
|
||||
'count' => count($allRosters),
|
||||
'all_rosters' => $allRosters
|
||||
]);
|
||||
|
||||
// 如果没有出诊排班,返回空数组
|
||||
if (empty($rosters)) {
|
||||
\think\facade\Log::warning('没有找到出诊排班', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date
|
||||
]);
|
||||
return ['slots' => []];
|
||||
}
|
||||
|
||||
// 2. 根据排班时段生成时间段
|
||||
$slots = [];
|
||||
|
||||
foreach ($rosters as $roster) {
|
||||
// 再次确认状态为出诊(双重检查)
|
||||
if ($roster['status'] != 1) {
|
||||
\think\facade\Log::warning('跳过非出诊排班', [
|
||||
'roster_id' => $roster['id'],
|
||||
'status' => $roster['status']
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$periodValue = $roster['period'];
|
||||
|
||||
\think\facade\Log::info('处理排班时段', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue,
|
||||
'status' => $roster['status'],
|
||||
'period_type' => gettype($periodValue)
|
||||
]);
|
||||
|
||||
// 根据时段生成时间段(支持数字和字符串格式)
|
||||
$startHour = null;
|
||||
$endHour = null;
|
||||
|
||||
if ($periodValue == 1 || $periodValue === 'morning' || $periodValue === '上午') {
|
||||
// 上午:9:00-12:00
|
||||
$startHour = 9;
|
||||
$endHour = 12;
|
||||
\think\facade\Log::info('识别为上午时段', ['period' => $periodValue]);
|
||||
} elseif ($periodValue == 2 || $periodValue === 'afternoon' || $periodValue === '下午') {
|
||||
// 下午:14:00-18:00
|
||||
$startHour = 14;
|
||||
$endHour = 18;
|
||||
\think\facade\Log::info('识别为下午时段', ['period' => $periodValue]);
|
||||
} else {
|
||||
\think\facade\Log::warning('未知的时段值,跳过此记录', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue,
|
||||
'period_type' => gettype($periodValue)
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 确保startHour和endHour已设置
|
||||
if ($startHour === null || $endHour === null) {
|
||||
\think\facade\Log::error('时段参数未正确设置', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 生成15分钟间隔的时间段
|
||||
for ($hour = $startHour; $hour < $endHour; $hour++) {
|
||||
for ($minute = 0; $minute < 60; $minute += 15) {
|
||||
$time = sprintf('%02d:%02d', $hour, $minute);
|
||||
|
||||
$slots[] = [
|
||||
'time' => $time,
|
||||
'available' => true,
|
||||
'quota' => 1,
|
||||
'period' => $periodValue
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\think\facade\Log::info('生成的时间段数量(去重前)', [
|
||||
'count' => count($slots),
|
||||
'sample_slots' => array_slice($slots, 0, 3)
|
||||
]);
|
||||
|
||||
// 如果没有生成任何时间段,返回空数组
|
||||
if (empty($slots)) {
|
||||
\think\facade\Log::warning('没有生成任何时间段');
|
||||
return ['slots' => []];
|
||||
}
|
||||
|
||||
// 3. 去重:按时间去重(因为可能有重复的排班记录)
|
||||
$uniqueSlots = [];
|
||||
$timeMap = [];
|
||||
foreach ($slots as $slot) {
|
||||
$time = $slot['time'];
|
||||
if (!isset($timeMap[$time])) {
|
||||
$timeMap[$time] = true;
|
||||
$uniqueSlots[] = $slot;
|
||||
}
|
||||
}
|
||||
$slots = $uniqueSlots;
|
||||
|
||||
\think\facade\Log::info('生成的时间段数量(去重后)', [
|
||||
'count' => count($slots),
|
||||
'morning_slots' => count(array_filter($slots, function($s) {
|
||||
return $s['time'] < '12:00';
|
||||
})),
|
||||
'afternoon_slots' => count(array_filter($slots, function($s) {
|
||||
return $s['time'] >= '14:00';
|
||||
})),
|
||||
'first_3_slots' => array_slice($slots, 0, 3),
|
||||
'last_3_slots' => array_slice($slots, -3)
|
||||
]);
|
||||
|
||||
// 4. 查询已预约的时间段
|
||||
$appointmentTimes = Appointment::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'appointment_date' => $date,
|
||||
'status' => 1 // 只查询有效预约
|
||||
])->column('appointment_time');
|
||||
|
||||
// 将时间格式统一为 HH:MM(去掉秒)
|
||||
$appointments = array_map(function($time) {
|
||||
// 如果是 HH:MM:SS 格式,截取前5位
|
||||
return substr($time, 0, 5);
|
||||
}, $appointmentTimes);
|
||||
|
||||
// 4. 标记已占用的时间段
|
||||
foreach ($slots as &$slot) {
|
||||
if (in_array($slot['time'], $appointments)) {
|
||||
$slot['available'] = false;
|
||||
$slot['quota'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 按时间排序
|
||||
usort($slots, function($a, $b) {
|
||||
return strcmp($a['time'], $b['time']);
|
||||
});
|
||||
|
||||
return ['slots' => $slots];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return ['slots' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 创建预约
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function create(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
// 1. 检查时间段是否已被预约(每个时间段只能预约1次)
|
||||
// 统一时间格式为 HH:MM:SS
|
||||
$appointmentTime = strlen($params['appointment_time']) == 5
|
||||
? $params['appointment_time'] . ':00'
|
||||
: $params['appointment_time'];
|
||||
|
||||
$exists = Appointment::where([
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'status' => 1
|
||||
])->where('appointment_time', $appointmentTime)
|
||||
->find();
|
||||
|
||||
if ($exists) {
|
||||
self::setError('该时间段已被预约');
|
||||
Db::rollback();
|
||||
return false;
|
||||
}
|
||||
$data = [
|
||||
'patient_id' => $params['patient_id'],
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'roster_id' => 0, // 不再关联排班表
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'period' => $params['period'] ?? 'all',
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $params['appointment_type'] ?? 'video',
|
||||
'remark' => $params['remark'] ?? '',
|
||||
'status' => 1,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
|
||||
$appointment = Appointment::create($data);
|
||||
|
||||
Db::commit();
|
||||
return ['id' => $appointment->id];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function cancel(array $params)
|
||||
{
|
||||
try {
|
||||
$appointment = Appointment::findOrEmpty($params['id']);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment->status = 2; // 已取消
|
||||
$appointment->update_time = time();
|
||||
$appointment->save();
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail(array $params)
|
||||
{
|
||||
$appointment = Appointment::alias('a')
|
||||
->leftJoin('la_user u', 'a.patient_id = u.id')
|
||||
->leftJoin('la_admin ad', 'a.doctor_id = ad.id')
|
||||
->field('a.*, u.nickname as patient_name, u.mobile as patient_phone, ad.name as doctor_name')
|
||||
->where('a.id', $params['id'])
|
||||
->findOrEmpty()
|
||||
->toArray();
|
||||
|
||||
if (empty($appointment)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 添加状态和类型描述
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
$appointment['status_desc'] = $statusMap[$appointment['status']] ?? '未知';
|
||||
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
$appointment['appointment_type_desc'] = $typeMap[$appointment['appointment_type']] ?? '未知';
|
||||
|
||||
// 格式化时间戳为日期时间
|
||||
if (isset($appointment['create_time']) && is_numeric($appointment['create_time'])) {
|
||||
$appointment['create_time'] = date('Y-m-d H:i:s', $appointment['create_time']);
|
||||
}
|
||||
if (isset($appointment['update_time']) && is_numeric($appointment['update_time'])) {
|
||||
$appointment['update_time'] = date('Y-m-d H:i:s', $appointment['update_time']);
|
||||
}
|
||||
|
||||
return $appointment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生某天的可用号源总数
|
||||
* @param int $doctorId
|
||||
* @param string $date
|
||||
* @return int
|
||||
*/
|
||||
public static function getDoctorAvailability($doctorId, $date)
|
||||
{
|
||||
try {
|
||||
$totalAvailable = 0;
|
||||
|
||||
// 查询上午和下午的排班
|
||||
$rosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'status' => 1 // 出诊
|
||||
])->select();
|
||||
|
||||
foreach ($rosters as $roster) {
|
||||
// 查询该时段已预约数量
|
||||
$appointmentCount = Appointment::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'appointment_date' => $date,
|
||||
'period' => $roster->period,
|
||||
'status' => 1
|
||||
])->count();
|
||||
|
||||
// 计算剩余号源
|
||||
$remaining = $roster->quota - $appointmentCount;
|
||||
if ($remaining > 0) {
|
||||
$totalAvailable += $remaining;
|
||||
}
|
||||
}
|
||||
|
||||
return $totalAvailable;
|
||||
} catch (\Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function complete(array $params)
|
||||
{
|
||||
try {
|
||||
$appointment = Appointment::findOrEmpty($params['id']);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($appointment->status != 1) {
|
||||
self::setError('该预约已处理,无法完成');
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment->status = 3; // 已完成
|
||||
$appointment->update_time = time();
|
||||
$appointment->save();
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\logic\doctor;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\doctor\Roster;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 医生排班逻辑
|
||||
* Class RosterLogic
|
||||
* @package app\adminapi\logic\doctor
|
||||
*/
|
||||
class RosterLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 保存排班
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function save(array $params)
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'date' => $params['date'],
|
||||
'period' => $params['period'],
|
||||
'status' => $params['status'],
|
||||
'quota' => $params['quota'] ?? 0,
|
||||
'max_patients' => $params['max_patients'] ?? 0,
|
||||
'remark' => $params['remark'] ?? '',
|
||||
];
|
||||
|
||||
if (isset($params['id']) && $params['id']) {
|
||||
// 更新
|
||||
$data['update_time'] = time();
|
||||
Roster::where('id', $params['id'])->update($data);
|
||||
return ['id' => $params['id']];
|
||||
} else {
|
||||
// 新增
|
||||
$data['create_time'] = time();
|
||||
$data['update_time'] = time();
|
||||
$roster = Roster::create($data);
|
||||
return ['id' => $roster->id];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除排班
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete(array $params)
|
||||
{
|
||||
try {
|
||||
Roster::destroy($params['id']);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 排班详情
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail(array $params)
|
||||
{
|
||||
return Roster::findOrEmpty($params['id'])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存排班
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function batchSave(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
$successCount = 0;
|
||||
$updateCount = 0;
|
||||
$createCount = 0;
|
||||
|
||||
foreach ($params['rosters'] as $roster) {
|
||||
$data = [
|
||||
'doctor_id' => $roster['doctor_id'],
|
||||
'date' => $roster['date'],
|
||||
'period' => $roster['period'],
|
||||
'status' => $roster['status'],
|
||||
'quota' => $roster['quota'] ?? 0,
|
||||
'max_patients' => $roster['max_patients'] ?? 0,
|
||||
'remark' => $roster['remark'] ?? '',
|
||||
];
|
||||
|
||||
// 检查是否已存在
|
||||
$exists = Roster::where([
|
||||
['doctor_id', '=', $data['doctor_id']],
|
||||
['date', '=', $data['date']],
|
||||
['period', '=', $data['period']],
|
||||
])->find();
|
||||
|
||||
if ($exists) {
|
||||
// 更新
|
||||
$data['update_time'] = time();
|
||||
$exists->save($data);
|
||||
$updateCount++;
|
||||
} else {
|
||||
// 新增
|
||||
$data['create_time'] = time();
|
||||
$data['update_time'] = time();
|
||||
Roster::create($data);
|
||||
$createCount++;
|
||||
}
|
||||
$successCount++;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return [
|
||||
'success_count' => $successCount,
|
||||
'create_count' => $createCount,
|
||||
'update_count' => $updateCount,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制排班
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function copy(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
// 获取源排班数据
|
||||
$where = [
|
||||
['date', 'between', [$params['source_start_date'], $params['source_end_date']]]
|
||||
];
|
||||
|
||||
if (isset($params['doctor_id']) && $params['doctor_id']) {
|
||||
$where[] = ['doctor_id', '=', $params['doctor_id']];
|
||||
}
|
||||
|
||||
$sourceRosters = Roster::where($where)->select();
|
||||
|
||||
// 计算日期差
|
||||
$sourceDays = (strtotime($params['source_end_date']) - strtotime($params['source_start_date'])) / 86400;
|
||||
$targetDays = (strtotime($params['target_start_date']) - strtotime($params['source_start_date'])) / 86400;
|
||||
|
||||
foreach ($sourceRosters as $roster) {
|
||||
$newDate = date('Y-m-d', strtotime($roster->date) + ($targetDays * 86400));
|
||||
|
||||
$data = [
|
||||
'doctor_id' => $roster->doctor_id,
|
||||
'date' => $newDate,
|
||||
'period' => $roster->period,
|
||||
'status' => $roster->status,
|
||||
'quota' => $roster->quota,
|
||||
'max_patients' => $roster->max_patients,
|
||||
'remark' => $roster->remark,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
|
||||
// 检查目标日期是否已存在排班
|
||||
$exists = Roster::where([
|
||||
['doctor_id', '=', $data['doctor_id']],
|
||||
['date', '=', $data['date']],
|
||||
['period', '=', $data['period']],
|
||||
])->find();
|
||||
|
||||
if (!$exists) {
|
||||
Roster::create($data);
|
||||
}
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\logic\tcm;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\tcm\BloodRecord;
|
||||
|
||||
/**
|
||||
* 血糖血压记录逻辑
|
||||
* Class BloodRecordLogic
|
||||
* @package app\adminapi\logic\tcm
|
||||
*/
|
||||
class BloodRecordLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 添加记录
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function add(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 处理记录日期
|
||||
if (isset($params['record_date'])) {
|
||||
$params['record_date'] = strtotime($params['record_date']);
|
||||
}
|
||||
|
||||
BloodRecord::create($params);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑记录
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function edit(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 处理记录日期
|
||||
if (isset($params['record_date'])) {
|
||||
$params['record_date'] = strtotime($params['record_date']);
|
||||
}
|
||||
|
||||
BloodRecord::update($params);
|
||||
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 {
|
||||
BloodRecord::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
|
||||
{
|
||||
$record = BloodRecord::findOrEmpty($params['id'])->toArray();
|
||||
|
||||
// 处理记录日期格式
|
||||
if (!empty($record['record_date'])) {
|
||||
$record['record_date'] = date('Y-m-d', $record['record_date']);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者的血糖血压记录列表
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getRecordsByPatient(array $params): array
|
||||
{
|
||||
$where = [];
|
||||
|
||||
if (isset($params['diagnosis_id'])) {
|
||||
$where[] = ['diagnosis_id', '=', $params['diagnosis_id']];
|
||||
}
|
||||
|
||||
if (isset($params['patient_id'])) {
|
||||
$where[] = ['patient_id', '=', $params['patient_id']];
|
||||
}
|
||||
|
||||
$records = BloodRecord::where($where)
|
||||
->where('delete_time', null)
|
||||
->order('record_date', 'desc')
|
||||
->order('record_time', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 格式化日期
|
||||
foreach ($records as &$record) {
|
||||
if (!empty($record['record_date'])) {
|
||||
$record['record_date'] = date('Y-m-d', $record['record_date']);
|
||||
}
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
}
|
||||
@@ -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 [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\validate\doctor;
|
||||
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 医生预约验证器
|
||||
* Class AppointmentValidate
|
||||
* @package app\adminapi\validate\doctor
|
||||
*/
|
||||
class AppointmentValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 设置校验规则
|
||||
* @var string[]
|
||||
*/
|
||||
protected $rule = [
|
||||
'id' => 'require',
|
||||
'patient_id' => 'require|integer',
|
||||
'doctor_id' => 'require|integer',
|
||||
'appointment_date' => 'require|date',
|
||||
'period' => 'in:morning,afternoon,all',
|
||||
'appointment_time' => 'require',
|
||||
'appointment_type' => 'in:video,text,phone',
|
||||
];
|
||||
|
||||
/**
|
||||
* 参数描述
|
||||
* @var string[]
|
||||
*/
|
||||
protected $field = [
|
||||
'id' => '预约ID',
|
||||
'patient_id' => '患者ID',
|
||||
'doctor_id' => '医生ID',
|
||||
'appointment_date' => '预约日期',
|
||||
'period' => '时段',
|
||||
'appointment_time' => '预约时间',
|
||||
'appointment_type' => '预约类型',
|
||||
];
|
||||
|
||||
/**
|
||||
* @notes 创建预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneCreate()
|
||||
{
|
||||
return $this->only(['patient_id', 'doctor_id', 'appointment_date', 'period', 'appointment_time', 'appointment_type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneCancel()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneDetail()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 可用时间段场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneAvailableSlots()
|
||||
{
|
||||
return $this->only(['doctor_id', 'appointment_date', 'period']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneComplete()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\validate\doctor;
|
||||
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 医生排班验证器
|
||||
* Class RosterValidate
|
||||
* @package app\adminapi\validate\doctor
|
||||
*/
|
||||
class RosterValidate extends BaseValidate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require',
|
||||
'doctor_id' => 'require',
|
||||
'date' => 'require|date',
|
||||
'period' => 'require|in:morning,afternoon',
|
||||
'status' => 'require|in:1,2,3,4',
|
||||
'quota' => 'number',
|
||||
'max_patients' => 'number',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'id.require' => '请选择排班',
|
||||
'doctor_id.require' => '请选择医生',
|
||||
'date.require' => '请选择日期',
|
||||
'date.date' => '日期格式不正确',
|
||||
'period.require' => '请选择时段',
|
||||
'period.in' => '时段参数错误',
|
||||
'status.require' => '请选择状态',
|
||||
'status.in' => '状态参数错误',
|
||||
'quota.number' => '号源数必须是数字',
|
||||
'max_patients.number' => '最大接诊数必须是数字',
|
||||
];
|
||||
|
||||
/**
|
||||
* @notes 保存场景
|
||||
*/
|
||||
public function sceneSave()
|
||||
{
|
||||
return $this->only(['doctor_id', 'date', 'period', 'status', 'quota', 'max_patients', 'remark']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除场景
|
||||
*/
|
||||
public function sceneDelete()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 详情场景
|
||||
*/
|
||||
public function sceneDetail()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存场景
|
||||
*/
|
||||
public function sceneBatchSave()
|
||||
{
|
||||
return $this->only(['rosters'])
|
||||
->append('rosters', 'require|array');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制场景
|
||||
*/
|
||||
public function sceneCopy()
|
||||
{
|
||||
return $this->only(['source_start_date', 'source_end_date', 'target_start_date'])
|
||||
->append('source_start_date', 'require|date')
|
||||
->append('source_end_date', 'require|date')
|
||||
->append('target_start_date', 'require|date');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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\validate\tcm;
|
||||
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单验证
|
||||
* Class DiagnosisValidate
|
||||
* @package app\adminapi\validate\tcm
|
||||
*/
|
||||
class DiagnosisValidate extends BaseValidate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require|checkDiagnosis',
|
||||
'patient_name' => 'require|length:1,50',
|
||||
'id_card' => 'length:15,18',
|
||||
'phone' => 'require|mobile',
|
||||
'gender' => 'require|in:0,1',
|
||||
'age' => 'require|number|between:0,150',
|
||||
'diagnosis_date' => 'require',
|
||||
'diagnosis_type' => 'require',
|
||||
'syndrome_type' => 'require',
|
||||
'status' => 'in:0,1',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'id.require' => '参数缺失',
|
||||
'patient_name.require' => '请输入患者姓名',
|
||||
'patient_name.length' => '患者姓名长度须在1-50位字符',
|
||||
'id_card.length' => '身份证号长度不正确',
|
||||
'phone.require' => '请输入手机号',
|
||||
'phone.mobile' => '手机号格式不正确',
|
||||
'gender.require' => '请选择性别',
|
||||
'gender.in' => '性别参数错误',
|
||||
'age.require' => '请输入年龄',
|
||||
'age.number' => '年龄必须为数字',
|
||||
'age.between' => '年龄范围0-150',
|
||||
'diagnosis_date.require' => '请选择诊断日期',
|
||||
'diagnosis_type.require' => '请选择诊断类型',
|
||||
'syndrome_type.require' => '请选择证型',
|
||||
'status.in' => '状态参数错误',
|
||||
];
|
||||
|
||||
public function sceneAdd()
|
||||
{
|
||||
return $this->remove('id', true);
|
||||
}
|
||||
|
||||
public function sceneEdit()
|
||||
{
|
||||
return $this->only(['id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'past_history', 'symptoms', 'tongue_coating', 'pulse', 'treatment_principle', 'prescription', 'doctor_advice', 'remark', 'status']);
|
||||
}
|
||||
|
||||
public function sceneId()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
protected function checkDiagnosis($value)
|
||||
{
|
||||
$diagnosis = Diagnosis::findOrEmpty($value);
|
||||
if ($diagnosis->isEmpty()) {
|
||||
return '诊单不存在';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
<?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\api\controller;
|
||||
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
|
||||
/**
|
||||
* 中医诊断控制器(供小程序调用)
|
||||
* Class TcmController
|
||||
* @package app\api\controller
|
||||
*/
|
||||
class TcmController extends BaseApiController
|
||||
{
|
||||
/**
|
||||
* @notes 不需要登录的方法
|
||||
* @var array
|
||||
*/
|
||||
public array $notNeedLogin = ['getPatientSignature'];
|
||||
|
||||
/**
|
||||
* @notes 获取患者签名(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getPatientSignature()
|
||||
{
|
||||
$patientId = $this->request->get('patient_id');
|
||||
|
||||
if (!$patientId) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
// 调用逻辑层
|
||||
$result = DiagnosisLogic::getPatientSignature($patientId);
|
||||
|
||||
if ($result === false) {
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\doctor;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 医生预约模型
|
||||
* Class Appointment
|
||||
* @package app\common\model\doctor
|
||||
*/
|
||||
class Appointment extends BaseModel
|
||||
{
|
||||
protected $name = 'doctor_appointment';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 时段描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getPeriodDescAttr($value, $data)
|
||||
{
|
||||
return $data['period'] == 'morning' ? '上午' : '下午';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约类型描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getAppointmentTypeDescAttr($value, $data)
|
||||
{
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
return $typeMap[$data['appointment_type']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getAppointmentDateTextAttr($value, $data)
|
||||
{
|
||||
return $data['appointment_date'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 关联患者
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function patient()
|
||||
{
|
||||
return $this->hasOne('app\common\model\user\User', 'id', 'patient_id')
|
||||
->bind(['patient_name' => 'nickname', 'patient_phone' => 'mobile']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 关联医生
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function doctor()
|
||||
{
|
||||
return $this->hasOne('app\adminapi\logic\auth\AdminLogic', 'id', 'doctor_id')
|
||||
->bind(['doctor_name' => 'name']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\doctor;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 医生排班模型
|
||||
* Class Roster
|
||||
* @package app\common\model\doctor
|
||||
*/
|
||||
class Roster extends BaseModel
|
||||
{
|
||||
protected $name = 'doctor_roster';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
1 => '出诊',
|
||||
2 => '停诊',
|
||||
3 => '休息',
|
||||
4 => '请假',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 时段描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getPeriodDescAttr($value, $data)
|
||||
{
|
||||
return $data['period'] == 'morning' ? '上午' : '下午';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTextAttr($value, $data)
|
||||
{
|
||||
return $data['date'] ?? '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 血糖血压记录模型
|
||||
* Class BloodRecord
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class BloodRecord extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_blood_record';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 记录日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getRecordDateTextAttr($value, $data)
|
||||
{
|
||||
return !empty($data['record_date']) ? date('Y-m-d', $data['record_date']) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 血压文本
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getBloodPressureTextAttr($value, $data)
|
||||
{
|
||||
if (!empty($data['systolic_pressure']) && !empty($data['diastolic_pressure'])) {
|
||||
return $data['systolic_pressure'] . '/' . $data['diastolic_pressure'] . ' mmHg';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 通话记录模型
|
||||
* Class CallRecord
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class CallRecord extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_call_record';
|
||||
|
||||
/**
|
||||
* @notes 关联诊单
|
||||
*/
|
||||
public function diagnosis()
|
||||
{
|
||||
return $this->belongsTo('app\common\model\tcm\Diagnosis', 'diagnosis_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话类型文本
|
||||
*/
|
||||
public function getCallTypeTextAttr($value, $data)
|
||||
{
|
||||
$types = [
|
||||
1 => '语音通话',
|
||||
2 => '视频通话'
|
||||
];
|
||||
return $types[$data['call_type']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取状态文本
|
||||
*/
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$status = [
|
||||
1 => '进行中',
|
||||
2 => '已结束',
|
||||
3 => '未接听',
|
||||
4 => '已取消'
|
||||
];
|
||||
return $status[$data['status']] ?? '未知';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?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\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单模型
|
||||
* Class Diagnosis
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class Diagnosis extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_diagnosis';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 性别描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getGenderDescAttr($value, $data)
|
||||
{
|
||||
return $data['gender'] == 1 ? '男' : '女';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
return $data['status'] == 1 ? '启用' : '禁用';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊断日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getDiagnosisDateTextAttr($value, $data)
|
||||
{
|
||||
return !empty($data['diagnosis_date']) ? date('Y-m-d', $data['diagnosis_date']) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 既往史数组
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function getPastHistoryArrAttr($value, $data)
|
||||
{
|
||||
return !empty($data['past_history']) ? explode(',', $data['past_history']) : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 患者TRTC账号模型
|
||||
* Class PatientTrtc
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class PatientTrtc extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_patient_trtc';
|
||||
|
||||
/**
|
||||
* @notes 关联诊单
|
||||
*/
|
||||
public function diagnosis()
|
||||
{
|
||||
return $this->hasMany('app\common\model\tcm\Diagnosis', 'patient_id', 'patient_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查是否过期
|
||||
*/
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return $this->expire_time > 0 && $this->expire_time < time();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 更新最后登录时间
|
||||
*/
|
||||
public function updateLastLogin(): bool
|
||||
{
|
||||
$this->last_login_time = time();
|
||||
return $this->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
/**
|
||||
* 腾讯云IM UserSig生成类(官方算法)
|
||||
* 参考:https://github.com/tencentyun/tls-sig-api-v2-php
|
||||
*/
|
||||
class TLSSigAPIv2
|
||||
{
|
||||
private $sdkappid;
|
||||
private $key;
|
||||
|
||||
public function __construct($sdkappid, $key)
|
||||
{
|
||||
$this->sdkappid = $sdkappid;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $expire 过期时间(秒)
|
||||
* @return string UserSig
|
||||
*/
|
||||
public function genUserSig($identifier, $expire = 86400)
|
||||
{
|
||||
return $this->__genSig($identifier, $expire, '', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带权限的UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $expire 过期时间(秒)
|
||||
* @param string $userbuf 用户权限buffer
|
||||
* @return string UserSig
|
||||
*/
|
||||
public function genUserSigWithUserBuf($identifier, $expire, $userbuf)
|
||||
{
|
||||
return $this->__genSig($identifier, $expire, $userbuf, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部方法:生成签名
|
||||
*/
|
||||
private function __genSig($identifier, $expire, $userbuf, $userbuf_enabled)
|
||||
{
|
||||
$current = time();
|
||||
$sigDoc = [
|
||||
'TLS.ver' => '2.0',
|
||||
'TLS.identifier' => strval($identifier),
|
||||
'TLS.sdkappid' => intval($this->sdkappid),
|
||||
'TLS.expire' => intval($expire),
|
||||
'TLS.time' => intval($current)
|
||||
];
|
||||
|
||||
$base64_userbuf = '';
|
||||
if ($userbuf_enabled) {
|
||||
$base64_userbuf = base64_encode($userbuf);
|
||||
$sigDoc['TLS.userbuf'] = $base64_userbuf;
|
||||
}
|
||||
|
||||
$sig = $this->hmacsha256($identifier, $current, $expire, $base64_userbuf, $userbuf_enabled);
|
||||
$sigDoc['TLS.sig'] = base64_encode($sig);
|
||||
|
||||
$json_text = json_encode($sigDoc);
|
||||
$compressed = gzcompress($json_text);
|
||||
|
||||
return $this->base64_url_encode($compressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成HMAC-SHA256签名
|
||||
*/
|
||||
private function hmacsha256($identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled)
|
||||
{
|
||||
$content_to_be_signed = "TLS.identifier:" . $identifier . "\n"
|
||||
. "TLS.sdkappid:" . $this->sdkappid . "\n"
|
||||
. "TLS.time:" . $curr_time . "\n"
|
||||
. "TLS.expire:" . $expire . "\n";
|
||||
|
||||
if ($userbuf_enabled) {
|
||||
$content_to_be_signed .= "TLS.userbuf:" . $base64_userbuf . "\n";
|
||||
}
|
||||
|
||||
return hash_hmac('sha256', $content_to_be_signed, $this->key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 URL安全编码
|
||||
*/
|
||||
private function base64_url_encode($input)
|
||||
{
|
||||
return str_replace(['+', '/', '='], ['*', '-', '_'], base64_encode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 URL安全解码
|
||||
*/
|
||||
private function base64_url_decode($base64_url_string)
|
||||
{
|
||||
return base64_decode(str_replace(['*', '-', '_'], ['+', '/', '='], $base64_url_string));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证UserSig
|
||||
* @param string $sig UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $init_time 初始化时间
|
||||
* @param int $expire_time 过期时间
|
||||
* @param string $userbuf 用户权限buffer
|
||||
* @param string $error_msg 错误信息
|
||||
* @return bool 是否有效
|
||||
*/
|
||||
public function verifySig($sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg)
|
||||
{
|
||||
try {
|
||||
$error_msg = '';
|
||||
$compressed_sig = $this->base64_url_decode($sig);
|
||||
$pre_level = error_reporting(E_ERROR);
|
||||
$uncompressed_sig = gzuncompress($compressed_sig);
|
||||
error_reporting($pre_level);
|
||||
|
||||
if ($uncompressed_sig === false) {
|
||||
throw new \Exception('gzuncompress error');
|
||||
}
|
||||
|
||||
$sig_doc = json_decode($uncompressed_sig, true);
|
||||
if ($sig_doc === false) {
|
||||
throw new \Exception('json_decode error');
|
||||
}
|
||||
|
||||
if ($sig_doc['TLS.identifier'] !== $identifier) {
|
||||
throw new \Exception('identifier dosen\'t match');
|
||||
}
|
||||
|
||||
if ($sig_doc['TLS.sdkappid'] != $this->sdkappid) {
|
||||
throw new \Exception('sdkappid dosen\'t match');
|
||||
}
|
||||
|
||||
$sig = base64_decode($sig_doc['TLS.sig']);
|
||||
if ($sig === false) {
|
||||
throw new \Exception('sig base64_decode error');
|
||||
}
|
||||
|
||||
$init_time = $sig_doc['TLS.time'];
|
||||
$expire_time = $sig_doc['TLS.expire'];
|
||||
|
||||
$curr_time = time();
|
||||
if ($curr_time > $init_time + $expire_time) {
|
||||
throw new \Exception('sig expired');
|
||||
}
|
||||
|
||||
$userbuf_enabled = false;
|
||||
$base64_userbuf = '';
|
||||
if (isset($sig_doc['TLS.userbuf'])) {
|
||||
$base64_userbuf = $sig_doc['TLS.userbuf'];
|
||||
$userbuf = base64_decode($base64_userbuf);
|
||||
$userbuf_enabled = true;
|
||||
}
|
||||
|
||||
$sigCalculated = $this->hmacsha256($identifier, $init_time, $expire_time, $base64_userbuf, $userbuf_enabled);
|
||||
|
||||
if ($sig != $sigCalculated) {
|
||||
throw new \Exception('verify failed');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $ex) {
|
||||
$error_msg = $ex->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
/**
|
||||
* 腾讯云IM服务类
|
||||
* Class TencentImService
|
||||
* @package app\common\service
|
||||
*/
|
||||
class TencentImService
|
||||
{
|
||||
private $sdkAppId;
|
||||
private $secretKey;
|
||||
private $adminIdentifier = 'administrator'; // 管理员账号
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = config('project.trtc');
|
||||
$this->sdkAppId = $config['sdkAppId'];
|
||||
$this->secretKey = $config['secretKey'];
|
||||
|
||||
\think\facade\Log::info('TencentImService初始化 - sdkAppId: ' . $this->sdkAppId . ', secretKey长度: ' . strlen($this->secretKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成UserSig(使用腾讯云官方算法)
|
||||
* @param string $userId
|
||||
* @param int $expire
|
||||
* @return string|false
|
||||
*/
|
||||
private function generateUserSig(string $userId, int $expire = 86400)
|
||||
{
|
||||
try {
|
||||
$api = new TLSSigAPIv2($this->sdkAppId, $this->secretKey);
|
||||
$userSig = $api->genUserSig($userId, $expire);
|
||||
|
||||
\think\facade\Log::info('UserSig生成成功 - userId: ' . $userId . ', sig长度: ' . strlen($userSig));
|
||||
|
||||
return $userSig;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('生成UserSig失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 导入单个账号到IM
|
||||
* @param string $userId 用户ID
|
||||
* @param string $nick 昵称(可选)
|
||||
* @param string $faceUrl 头像URL(可选)
|
||||
* @return array|false
|
||||
*/
|
||||
public function importAccount(string $userId, string $nick = '', string $faceUrl = '')
|
||||
{
|
||||
try {
|
||||
\think\facade\Log::info('开始导入IM账号 - userId: ' . $userId . ', nick: ' . $nick . ', sdkAppId: ' . $this->sdkAppId);
|
||||
|
||||
// 生成管理员UserSig
|
||||
$adminUserSig = $this->generateUserSig($this->adminIdentifier);
|
||||
|
||||
if (!$adminUserSig) {
|
||||
throw new \Exception('生成管理员UserSig失败');
|
||||
}
|
||||
|
||||
\think\facade\Log::info('管理员UserSig生成成功');
|
||||
|
||||
// 构建请求URL
|
||||
$random = rand(0, 4294967295);
|
||||
$url = sprintf(
|
||||
'https://console.tim.qq.com/v4/im_open_login_svc/account_import?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json',
|
||||
$this->sdkAppId,
|
||||
$this->adminIdentifier,
|
||||
urlencode($adminUserSig),
|
||||
$random
|
||||
);
|
||||
|
||||
\think\facade\Log::info('请求URL构建完成');
|
||||
|
||||
// 构建请求体
|
||||
$data = [
|
||||
'UserID' => $userId
|
||||
];
|
||||
|
||||
if ($nick) {
|
||||
$data['Nick'] = $nick;
|
||||
}
|
||||
|
||||
if ($faceUrl) {
|
||||
$data['FaceUrl'] = $faceUrl;
|
||||
}
|
||||
|
||||
\think\facade\Log::info('请求数据: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 发送请求
|
||||
$result = $this->httpPost($url, json_encode($data));
|
||||
|
||||
\think\facade\Log::info('HTTP响应: ' . substr($result, 0, 500));
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('请求失败:无响应');
|
||||
}
|
||||
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if (!$response) {
|
||||
throw new \Exception('响应解析失败:' . substr($result, 0, 200));
|
||||
}
|
||||
|
||||
\think\facade\Log::info('响应解析成功: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 检查响应状态
|
||||
if ($response['ActionStatus'] !== 'OK') {
|
||||
$errorMsg = sprintf(
|
||||
'导入账号失败 - ErrorCode: %s, ErrorInfo: %s',
|
||||
$response['ErrorCode'] ?? 'unknown',
|
||||
$response['ErrorInfo'] ?? '未知错误'
|
||||
);
|
||||
throw new \Exception($errorMsg);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'userId' => $userId,
|
||||
'message' => '账号导入成功'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$errorMsg = sprintf(
|
||||
'导入IM账号失败 - userId: %s, error: %s',
|
||||
$userId,
|
||||
$e->getMessage()
|
||||
);
|
||||
\think\facade\Log::error($errorMsg);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'userId' => $userId,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量导入账号
|
||||
* @param array $accounts 账号列表 [['userId' => 'xxx', 'nick' => 'xxx', 'faceUrl' => 'xxx'], ...]
|
||||
* @return array
|
||||
*/
|
||||
public function batchImportAccounts(array $accounts): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$userId = $account['userId'] ?? '';
|
||||
$nick = $account['nick'] ?? '';
|
||||
$faceUrl = $account['faceUrl'] ?? '';
|
||||
|
||||
if (!$userId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->importAccount($userId, $nick, $faceUrl);
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除账号
|
||||
* @param array $userIds 用户ID列表
|
||||
* @return array|false
|
||||
*/
|
||||
public function deleteAccounts(array $userIds)
|
||||
{
|
||||
try {
|
||||
// 生成管理员UserSig
|
||||
$adminUserSig = $this->generateUserSig($this->adminIdentifier);
|
||||
|
||||
if (!$adminUserSig) {
|
||||
throw new \Exception('生成管理员UserSig失败');
|
||||
}
|
||||
|
||||
// 构建请求URL
|
||||
$random = rand(0, 4294967295);
|
||||
$url = sprintf(
|
||||
'https://console.tim.qq.com/v4/im_open_login_svc/account_delete?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json',
|
||||
$this->sdkAppId,
|
||||
$this->adminIdentifier,
|
||||
urlencode($adminUserSig),
|
||||
$random
|
||||
);
|
||||
|
||||
// 构建请求体
|
||||
$data = [
|
||||
'DeleteItem' => array_map(function($userId) {
|
||||
return ['UserID' => $userId];
|
||||
}, $userIds)
|
||||
];
|
||||
|
||||
// 发送请求
|
||||
$result = $this->httpPost($url, json_encode($data));
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('请求失败');
|
||||
}
|
||||
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if (!$response) {
|
||||
throw new \Exception('响应解析失败');
|
||||
}
|
||||
|
||||
// 检查响应状态
|
||||
if ($response['ActionStatus'] !== 'OK') {
|
||||
throw new \Exception($response['ErrorInfo'] ?? '删除账号失败');
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => '账号删除成功'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('删除IM账号失败', [
|
||||
'userIds' => $userIds,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发送HTTP POST请求
|
||||
* @param string $url
|
||||
* @param string $data
|
||||
* @return string|false
|
||||
*/
|
||||
private function httpPost(string $url, string $data)
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
]);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$error = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
\think\facade\Log::error('HTTP请求失败', [
|
||||
'url' => $url,
|
||||
'error' => $error
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class IndexController extends BaseController
|
||||
*/
|
||||
public function index($name = '你好,likeadmin')
|
||||
{
|
||||
$template = app()->getRootPath() . 'public/pc/index.html';
|
||||
$template = app()->getRootPath() . 'public/admin/index.html';
|
||||
if (Request::isMobile()) {
|
||||
$template = app()->getRootPath() . 'public/mobile/index.html';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user