You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

587 lines
27 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Services\Admin\YeWu;
use App\Services\SendMessgeService;
use DateInterval;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use DateTime;
use Tools;
class PlanListService
{
public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $appointment_date)
{
date_default_timezone_set('PRC');
$allDevice = [];//所有医嘱检查项目绑定的设备id
$commPatientType = [];//所有医嘱共同的病人类型
foreach ($entrustids as $key => $entrustid) {
// $info = DB::table('s_list')->where(['reg_num' => $regnum, 'entrust_id' => $entrustid, 'episodeid' => $episodeid, 'is_nullify' => 0])->first();
$info = DB::table('s_list')->where(['entrust_id' => $entrustid, 'is_nullify' => 0])->first();
if (!$info) return \Yz::echoError1('没有找到对应医嘱信息:'.$entrustid);
$itemInfo = DB::table('s_check_item')->where(['item_name' => $info->entrust, 'status' => 1, "is_del" => 0])->get();
if (count($itemInfo) == 0) return \Yz::echoError1('没有找到可用的检查项目信息');
$itemInfo = $itemInfo[0];
$qudaos = explode(',', $itemInfo->reservation_method);
//只判断主渠道,如果主渠道支持,则子渠道默认也支持,即使项目没绑定子渠道
if (!in_array($appointment_type, $qudaos)) return \Yz::echoError1('此检查项目不支持在当前渠道预约');
//获取检查项目绑定的服务组(设备),判断状态正常的
$devices = DB::table('s_check_item_device')
->leftJoin("s_devices", "s_check_item_device.device_id", "=", "s_devices.id")
->where(['s_check_item_device.item_id' => $itemInfo->id])
->where(['s_devices.status' => 1, 'is_del' => 0])->pluck('s_devices.id')->toArray();
$allDevice[] = $devices;
if (!in_array($info->patient_type, $commPatientType)) {
// 如果不在数组中,则添加它
array_push($commPatientType, $info->patient_type);
}
}
$commonDevice = []; //多个检查项目共同的设备id
foreach ($allDevice as $set) {
if (count($commonDevice) == 0) {
// 如果$intersection为空直接将第一个子数组的元素放入
$commonDevice = $set;
} else {
// 使用array_intersect()函数求当前子数组与已有交集的交集
$commonDevice = array_intersect($commonDevice, $set);
}
}
// dd($commonDevice);
//获取主表检查项目绑定的科室id
$department_id = DB::table('s_department')->where(['department_number' => $info->RISRAcceptDeptCode])->first();
if (!$department_id) return \Yz::echoError1('获取医嘱检查项目科室信息失败');
//查询号源渠道是否有合并
$appointment_types=[$appointment_type];
$appointment_type_link = DB::table('s_appointment_type_ratio')->where(['department_id'=>$department_id->id,'appointment_type_id' => $appointment_type])->first();
if (!empty($appointment_type_link->link)) {
$appointment_types=json_decode($appointment_type_link->link, true);
$appointment_types=array_merge($appointment_types, [$appointment_type]);
}
if(count($commonDevice)==0) return \Yz::echoError1("无可用号源");
$placeholders = implode(',', array_fill(0, count($commonDevice), '?'));
$appointment_types_placeholders = implode(',', array_fill(0, count($appointment_types), '?'));
$canshu = array_merge($commonDevice, [$department_id->id, $appointment_date], $appointment_types);
$plan = DB::select("SELECT
a.*,
dd.devices,
b.department_resources_name,
c.roster_detail_id,
c.count,
c.used_count
FROM
s_source_roster_detail AS a
LEFT JOIN s_department_resources AS b ON a.resources_id = b.id
LEFT JOIN s_source_roster_detail_count AS c ON a.id = c.roster_detail_id
JOIN (
SELECT
roster_detail_id,
GROUP_CONCAT( e.device_name SEPARATOR ', ' ) AS devices
FROM
s_source_roster_detail_device AS d
LEFT JOIN s_devices AS e ON d.device_id = e.id
WHERE
d.device_id IN ($placeholders)
GROUP BY
d.roster_detail_id
) AS dd ON a.id = dd.roster_detail_id
WHERE
a.department_id = ?
AND a.date = ?
AND a.STATUS = 1
AND a.is_del = 0
AND b.is_del = 0
AND c.appointment_type_id IN ($appointment_types_placeholders)", $canshu);
$mergedPlan = [];
foreach ($plan as $key => $p) {
// 如果 roster_detail_id 已经存在,则合并 count 数量
if (isset($mergedPlan[$p->roster_detail_id])) {
// 累加 count 数量到已存在的记录中
$mergedPlan[$p->roster_detail_id]->count += $p->count;
$mergedPlan[$p->roster_detail_id]->used_count += $p->used_count; // 如果需要合并 used_count也可以加上
} else {
// 初始化新的合并数据
$mergedPlan[$p->roster_detail_id] = $p;
}
}
// 将合并后的数据重新组织为数组
$plan = array_values($mergedPlan);
//遍历列表 把超过当前时间的放在后面
$pl1 = [];
$pl2 = [];
$pp = [];
$nowtime = date('Y-m-d H:i:s');
foreach ($plan as $key => $p) {
// //病人类型不符合的过滤掉
$planPatientType = explode(",", $p->patient_type);
if (!empty(array_diff($commPatientType, $planPatientType))) {
continue;
}
//过期的排在后面
$time = $p->date . ' ' . $p->end_time;
if ($time > $nowtime) {
$pl1[] = $p;
} else {
$pl2[] = $p;
}
}
$pp = array_merge($pl1, $pl2);
return \Yz::Return(true, '查询完成', ['today_date' => date("Y-m-d"), 'appointment_date' => $appointment_date, 'weekname' => Tools::GetWeekName($appointment_date), 'mainInfo' => $info, 'plan_list' => $pp]);
}
//开始预约占用名额
public function YuYue($planid, $appointment_type, $mainlistids, $do_type)
{
date_default_timezone_set('PRC');
$nowdatetime = date("Y-m-d H:i:s");
// $planid = request('planid');
// $appointment_type = request('appointment_type');//渠道id
// $mainlistid = request('mainlistid');//主表id
// $do_type = request('dotype');//操作类型,1预约2改约
// if (!isset($do_type)) return \Yz::echoError1('参数:操作类型 不能为空');
$planInfo = DB::table('s_source_roster_detail')->where(['id' => $planid, 'status' => 1, 'is_del' => 0])->first();
if (!$planInfo) return \Yz::echoError1('当前时段不可用');
if ($nowdatetime > $planInfo->date . ' ' . $planInfo->end_reservation_time) return \Yz::echoError1('已经超过预约截止时间');
//查询号源渠道是否有合并
$appointment_types=[$appointment_type];
$appointment_type_link = DB::table('s_appointment_type_ratio')->where(['department_id'=>$planInfo->department_id,'appointment_type_id' => $appointment_type])->first();
if (!empty($appointment_type_link->link)) {
$appointment_types=json_decode($appointment_type_link->link, true);
$appointment_types=array_merge($appointment_types, [$appointment_type]);
}
$roster_detail_counts = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $planid])
->whereIn('appointment_type_id', $appointment_types)
->get();
$planZongCount=0;//合并后的各个渠道总名额
$planZongUsedCount=0;//合并后的各个渠道已预约的名额
$plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额
$weifenpeiCount=count($mainlistids);//未分配的名额
foreach ($roster_detail_counts as $roster_detail_count) {
$planZongCount+=$roster_detail_count->count;
$planZongUsedCount+=$roster_detail_count->used_count;
$keyongCount=$roster_detail_count->count-$roster_detail_count->used_count;
if($weifenpeiCount-$keyongCount>=0){
$plan_qudao_tempCount[]=$keyongCount;
$weifenpeiCount-=$keyongCount;
}else{
$plan_qudao_tempCount[]=$weifenpeiCount;
$weifenpeiCount=0;
}
}
if ($planZongCount < ($planZongUsedCount + count($mainlistids))) return \Yz::echoError1('当前预约时间名额不足');
$huchiList=[];//互斥item_id对应关系
$oldMainInfos = [];//临时存储原来的主表信息,用于改约
//遍历多个s_list表id,前端多选,一次预约多个检查项目
foreach ($mainlistids as $key_m => $mainlistid) {
$mainInfo = DB::table('s_list as a')
->select('a.*', 'b.period_begin_time', 'b.period_end_time')
->leftJoin('s_period as b', 'a.reservation_time', '=', 'b.id')
->where(['a.id' => $mainlistid])->first();
$oldMainInfos[] = $mainInfo;
if (!$mainInfo) return \Yz::echoError1('医嘱不存在');
//判断状态
$msg_t = "";
if ($mainInfo->list_status == 0) {
$msg_t = "当前状态为待预约";
}
if ($mainInfo->list_status == 1) {
$msg_t = "已经在" . $mainInfo->reservation_date . '的' . $mainInfo->period_begin_time . '-' . $mainInfo->period_end_time . '预约成功,不能再次预约';
}
if ($mainInfo->list_status == 2) {
$msg_t = "当前状态为已登记";
}
if ($mainInfo->list_status == 3) {
$msg_t = "当前状态为已完成";
}
if ($do_type == 1 && $mainInfo->list_status <> 0) return \Yz::echoError1($mainInfo->entrust . ' ' . $msg_t . ',禁止预约');
if ($do_type == 2 && $mainInfo->list_status <> 1) return \Yz::echoError1($mainInfo->entrust . ' ' . $msg_t . ',不允许改约操作');
//判断病人类型
$plan_patient_type=explode(",", $planInfo->patient_type);
if(!in_array($mainInfo->patient_type, $plan_patient_type)) return \Yz::echoError1('当前计划不支持此病人类型');
//判断互斥暂时根据reg_num判断身份
//查询想要预约的项目 其自身code
$item = DB::table('s_check_item')->where(['item_name' => $mainInfo->entrust, 'status' => 1, 'is_del' => 0])->first();
if (!$item) return \Yz::echoError1('此检查项目不可用');
//医嘱开具后,预约时间需在设定的等待期之后,单位分钟
$entrust_time = $mainInfo->entrust_date . ' ' . $mainInfo->entrust_time; //医嘱时间
$date = new DateTime($entrust_time);
$date->modify("+" . $item->check_begin_time . " minutes");
$enableCheckTime = $date;//到此时间后可进行预约
$plan_dateTime = $planInfo->date . ' ' . $planInfo->end_time;
$plan_dateTime = new DateTime($plan_dateTime);
if ($plan_dateTime < $enableCheckTime and $item->check_begin_time>0) return \Yz::echoError1($item->item_name . " 已设置只能预约医嘱开具后" . $item->check_begin_time . "分钟后的时间,请预约" . $enableCheckTime->format("Y-m-d H:i:s") . "之后的时间");
//检测是否空腹
$configs = DB::table('configs')->where('label', '开启空腹')->first();
if ($item->limosis == 1 and $planInfo->end_reservation_time > '12:00:00' and $configs->value == 1) return \Yz::echoError1($item->item_name . ' 项目必须空腹,只能预约上午,请重新选择时间');
//查询当前检查项目是否存在互斥
$cha_hc = DB::table('s_huchi')->where('is_del', 0)
->where(function ($q) use ($item) {
$q->where(['code1' => $item->item_code])->orWhere('code2', $item->item_code);
})->get();
foreach ($cha_hc as $hc) {
$huchiList[]=[$hc->code1,$hc->code2];
}
if (count($cha_hc) > 0) {
//查询用户预约中的医嘱
$status_1 = DB::table('s_list')
->select('s_check_item.item_code', 's_list.entrust', 's_list.reservation_date', 's_list.reservation_time')
->leftJoin('s_check_item', 's_list.entrust', '=', 's_check_item.item_name')
->where(['s_list.reg_num' => $mainInfo->reg_num, 's_list.list_status' => 1, 's_list.is_del' => 0, 's_list.is_nullify' => 0])
//排除自身,自己不能互斥自己
->whereNotIn('item_name', [$mainInfo->entrust])
->get();
if (count($status_1) > 0) {
foreach ($status_1 as $key => $value) {
foreach ($cha_hc as $k => $v) {
if ($v->code1 == $value->item_code or $v->code2 == $value->item_code) {
if ($v->time == 0) {
//如果是永久互斥,直接拒绝
return \Yz::Return(false, '当前预约项目与' . $value->entrust . '互斥,暂不可预约', ['name' => $value->entrust]);
}
if ($v->time > 0) {
//如果设置互斥时间,则判断预约时间是否超过 正在预约的最后时间段+互斥时间
$period = DB::table('s_period')->where(['id' => $value->reservation_time])->first();
$endTime = $period->period_end_time;
$periodEndDateTime = $value->reservation_date . ' ' . $endTime;
$date = new DateTime($periodEndDateTime);
// 添加互斥时间/小时
$date->add(new \DateInterval('PT' . $v->time . 'H')); // PTXH 表示X小时的时间间隔
$HuChi_EndDateTime = $date->format('Y-m-d H:i:s');//已经预约的项目结束互斥时间
$YuYueDateTime = substr($planInfo->date, 0, 10) . ' ' . $planInfo->begin_time;
if ($HuChi_EndDateTime > $YuYueDateTime) {
return \Yz::Return(false, '当前预约项目与' . $value->entrust . '互斥,暂不可预约,请预约' . $HuChi_EndDateTime . '后的日期', ['name' => $value->entrust]);
}
}
}
}
}
}
}
}
//判断某人这些待预约项目里,是否存在互斥
$userGroup=[];
foreach ($oldMainInfos as $key1 => $value1) {
$reg_num = $value1->reg_num;
if (!isset($userGroup[$reg_num])) {
$userGroup[$reg_num] = [];
}
$userGroup[$reg_num][]=[
'reg_num' => $value1->reg_num,
'user_name' => $value1->user_name,
'entrust_code' => $value1->entrust_code,
'entrust' => $value1->entrust,
];
}
// $huchiList
foreach ($userGroup as $personEntry) {
$ids = array_column($personEntry, 'entrust_code');
if ($this->isMutuallyExclusive($ids, $huchiList)) {
return \Yz::echoError1($personEntry[0]['user_name'].'勾选的项目存在互斥,不可同时预约');
}
}
DB::beginTransaction();
try {
//更新计划明细表使用数量
$up_plan_count_all_success =true;
foreach ($roster_detail_counts as $key => $planCount) {
if($plan_qudao_tempCount[$key]==0) continue;
$u = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id])->whereRaw('count >= (used_count + ?)', [$plan_qudao_tempCount[$key]])
->increment('used_count',$plan_qudao_tempCount[$key]);
if(!$u){
$up_plan_count_all_success=false;
break;
}
}
if ($up_plan_count_all_success) {
foreach ($roster_detail_counts as $key => $planCount) {
$cha = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id])->first();
if ($cha->count < $cha->used_count) {
DB::rollBack();
return \Yz::echoError1('操作失败1');
}
}
}else{
DB::rollBack();
return \Yz::echoError1('操作失败');
}
//排队号
$xvhao=0;
$xvhao= $this->generateQueueNumber($planInfo->id);
//更新主表信息
$u_data = [
'list_status' => 1,
'reservation_date' => $planInfo->date,
'reservation_time' => $planInfo->period_id,
'reservation_sources' => $planInfo->resources_id,
'services_group' => $planInfo->device_id,
'roster_id' => $planInfo->id,
'department_id' => $planInfo->department_id,
'xuhao' => $xvhao,
'appointment_type_id' => $appointment_type,
'qudao_appointment_type_id' => $appointment_type,
];
$list_all_success =true;
$offset = 0;
foreach($plan_qudao_tempCount as $key=>$length){
if($length==0) continue;
$u_data['appointment_type_id']= $appointment_types[$key];
// 获取当前批次的记录 ID
$currentBatchIds = array_slice($mainlistids, $offset, $length);
foreach ($currentBatchIds as $id) {
// 为当前记录生成唯一的排队号
$xuhao = $this->generateQueueNumber($planInfo->id);
// 更新当前记录的数据(包括 xuhao
$u_data['xuhao'] = $xuhao;
$u_mainList = DB::table('s_list')
->where('id', $id)
->update($u_data);
if (!$u_mainList) {
$list_all_success = false;
break 2; // 如果更新失败,跳出外层循环
}
}
$offset += $length;
}
if (!$list_all_success) {
DB::rollBack();
return \Yz::echoError1('预约失败');
}
$note = "预约";
foreach ($oldMainInfos as $key => $oldMainInfo) {
if ($do_type == 2) {
// if(count($mainlistids)>1) return \Yz::echoError1('请选择1条医嘱改约暂不支持批量');
$note = "改约";
//如果是改约,则恢复原来的数量
$u_old = DB::table('s_source_roster_detail_count')->where(['roster_detail_id' => $oldMainInfo->roster_id, 'appointment_type_id' => $oldMainInfo->appointment_type_id, ['used_count', '>', 0]])->decrement('used_count');
}
$i_log = DB::table('s_list_log')->insert([
'list_id' => $oldMainInfo->id,
'reg_num' => $oldMainInfo->reg_num,
'old_status' => $oldMainInfo->list_status,
'new_status' => 1,
'create_user' => null,
'note' => $note,
'data' => json_encode($u_data)
]);
}
if ($u_mainList) {
DB::commit();
if(config('app.globals.预约完成短信通知')==1){
$this->SendMsg($oldMainInfos,$do_type);
}
return \Yz::Return(true, '预约成功', ['planid' => $planid, 'mainlistids' => $mainlistids]);
} else {
DB::rollBack();
return \Yz::echoError1('预约失败');
}
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('预约异常' . $e->getMessage());
}
}
public function AutoYuYue($regnum,$entrustids,$episodeid,$appointment_type)
{
$dateRange=config('app.globals.可用号源查询范围');
// $regnum = request('regnum');
// $entrustids = request('entrustid');
// $episodeid = request('episodeid');
// $appointment_type = request('appointment_type'); //预约类型
$TodayDateTime = date("Y-m-d H:i:s");
$startDate = new DateTime();
// 设定结束日期为当前日期加7天
$endDate = new DateTime();
$endDate->modify('+' . $dateRange . ' day');
// 循环遍历每一天
$currentDate = $startDate;
$enable_plan=false;
while ($currentDate <= $endDate) {
$nowdate = $currentDate->format('Y-m-d');
$s = $this->GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $nowdate);
if ($s['status']) {
$list = $s['data']['plan_list'];
if (count($list) > 0) {
foreach ($list as $k => $v) {
if($v->count-$v->used_count>0 and $TodayDateTime< $v->date.' '.$v->end_reservation_time){
$enable_plan=$v;
break;
}
}
if(!!$enable_plan){
break;
}
}
}
// 每次循环增加一天
$currentDate->modify('+1 day');
}
if(!!$enable_plan){
$mainlistids=DB::table('s_list')->whereIn('entrust_id',$entrustids)->pluck('id')->toArray();
$yuyue=$this->YuYue($enable_plan->id, $appointment_type, $mainlistids, 1);
if($yuyue['status']==false){
return \Yz::echoError1($yuyue['msg']);
}
if($yuyue['status']===true){
return \Yz::Return(true,"预约成功",['entrust_id'=>$entrustids,'date'=>$enable_plan->date,'begin_time'=>$enable_plan->begin_time,'end_time'=>$enable_plan->end_time]);
}
}else{
return \Yz::echoError1('最近'.$dateRange.'日无可用号源');
}
}
public function CancelYuYue($MainListId, $reg_num)
{
date_default_timezone_set('PRC');
$nowdatetime = date("Y-m-d H:i:s");
$mainInfo = DB::table('s_list')->where(['id' => $MainListId, 'reg_num' => $reg_num])->first();
if (!$mainInfo) return \Yz::echoError1('医嘱不存在');
//判断状态
if ($mainInfo->list_status <> 1) return \Yz::echoError1('该记录无法取消,当前状态:' . $mainInfo->list_status);
DB::beginTransaction();
try {
$u_data = [
'list_status' => 0,
'reservation_date' => null,
'reservation_time' => null,
'reservation_sources' => null,
'services_group' => null,
'roster_id' => null,
'xuhao' => null,
'department_id' => null,
'appointment_type_id' => null,
'canel_time' => $nowdatetime,
];
$u_mainList = DB::table('s_list')->where(['id' => $MainListId])->update($u_data);
$i_log = DB::table('s_list_log')->insert([
'list_id' => $mainInfo->id,
'reg_num' => $mainInfo->reg_num,
'old_status' => $mainInfo->list_status,
'new_status' => 0,
'create_user' => null,
'note' => '取消预约',
'data' => json_encode($u_data)
]);
$u_count = DB::table('s_source_roster_detail_count')->where(['roster_detail_id' => $mainInfo->roster_id, 'appointment_type_id' => $mainInfo->appointment_type_id])->decrement('used_count');
if ($u_mainList && $u_count) {
DB::commit();
return \Yz::Return(true, '取消成功', []);
} else {
DB::rollBack();
return \Yz::echoError1('取消失败');
}
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('取消异常');
}
}
//短信提醒
public function SendMsg($infos,$dotype=1)
{
$s=new SendMessgeService();
foreach ($infos as $key => $info) {
$mainInfo = DB::table('s_list as a')
->select('a.*', 'b.period_begin_time', 'b.period_end_time')
->leftJoin('s_period as b', 'a.reservation_time', '=', 'b.id')
->where(['a.id' => $info->id])->first();
$s->sendMessage($info->user_phone,'测试短信,项目:'.$mainInfo->entrust.',时间:'.$mainInfo->reservation_date.' '.substr($mainInfo->period_begin_time,0,5).'-'.substr($mainInfo->period_end_time,0,5));
}
}
//判断是否在互斥组内
function isMutuallyExclusive($ids, $huchi) {
foreach ($huchi as $pair) {
if (in_array($pair[0], $ids) && in_array($pair[1], $ids)) {
return true;
}
}
return false;
}
//获取排队号思路获取主表内使用这个plan_id的医嘱获取已生成排队号最大的一个数加1。如果中间有断号则选择断号中最小的一个
function generateQueueNumber($planId)
{
DB::transaction(function () use ($planId, &$queueNumbers) {
// 使用悲观锁锁定相关行,确保同一时间只有一个进程操作
$queueNumbers = DB::table('s_list')
->where(['roster_id' => $planId, 'is_del' => 0, 'is_nullify' => 0])
->lockForUpdate() // 关键修改:添加行级锁
->pluck('xuhao')
->filter()
->map(fn($num) => (int)$num)
->sort();
});
if ($queueNumbers->isEmpty()) {
return 1;
}
$maxQueueNumber = $queueNumbers->max();
$minAvailableNumber = null;
for ($i = 1; $i <= $maxQueueNumber; $i++) {
if (!$queueNumbers->contains($i)) {
$minAvailableNumber = $i;
break;
}
}
return $minAvailableNumber ?? $maxQueueNumber + 1;
}
}