预约时占位,取消时根据占位取消,判断关联号源池

main
yanzai 5 months ago
parent d0f1e205e9
commit 7e8c8b407a

@ -8,6 +8,7 @@ use DateInterval;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use DateTime;
use Illuminate\Support\Facades\Log;
use Tools;
class PlanListService
@ -174,36 +175,7 @@ WHERE
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('当前预约时间名额不足');
$zhanweiCount=0;//各个检查项目需要占用检查名额的总和
$huchiList=[];//互斥item_id对应关系
$oldMainInfos = [];//临时存储原来的主表信息,用于改约
//遍历多个s_list表id,前端多选,一次预约多个检查项目
@ -239,9 +211,9 @@ WHERE
//判断互斥暂时根据reg_num判断身份
//查询想要预约的项目 其自身code
$item = DB::table('s_check_item')->where(['item_name' => $mainInfo->entrust, 'status' => 1, 'is_del' => 0])->first();
$item = DB::table('s_check_item')->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])->first();
if (!$item) return \Yz::echoError1('此检查项目不可用');
$zhanweiCount+=$item->use_seats;
//医嘱开具后,预约时间需在设定的等待期之后,单位分钟
$entrust_time = $mainInfo->entrust_date . ' ' . $mainInfo->entrust_time; //医嘱时间
$date = new DateTime($entrust_time);
@ -303,6 +275,41 @@ WHERE
}
}
//查询号源渠道是否有合并
$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]);
sort($appointment_types);
}
$roster_detail_counts = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $planid])
->whereIn('appointment_type_id', $appointment_types)
->orderBy('appointment_type_id','asc')
->get();
$planZongCount=0;//合并后的各个渠道总名额
$planZongUsedCount=0;//合并后的各个渠道已预约的名额
$plan_qudao_tempCount=[];//拆分各个渠道需要占用的名额
$weifenpeiCount=$zhanweiCount;//未分配的名额
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 +$zhanweiCount)) return \Yz::echoError1('当前预约时间名额不足');
//判断某人这些待预约项目里,是否存在互斥
$userGroup=[];
foreach ($oldMainInfos as $key1 => $value1) {
@ -372,16 +379,17 @@ WHERE
];
$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);
$currentBatchIds = array_slice($mainlistids, $offset, $length);//这块逻辑得优化之前是1个项目占位1个现在有可能1个项目占位多个
foreach ($currentBatchIds as $id) {
// 为当前记录生成唯一的排队号
$xuhao = $this->generateQueueNumber($planInfo->id);
// 更新当前记录的数据(包括 xuhao
$u_data['xuhao'] = $xuhao;
$u_mainList = DB::table('s_list')
@ -503,48 +511,175 @@ WHERE
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 {
// 1. 查询并锁定主预约记录(防并发)
$mainInfo = DB::table('s_list')
->where(['id' => $MainListId, 'reg_num' => $reg_num])
->lockForUpdate()
->first();
if (!$mainInfo) {
return \Yz::echoError1('医嘱不存在');
}
// 2. 检查当前状态是否允许取消仅状态为1可取消
if ($mainInfo->list_status != 1) {
return \Yz::echoError1('该记录无法取消,当前状态:' . $mainInfo->list_status);
}
// 3. 获取检查项目信息(判断是否可用)
$item = DB::table('s_check_item')
->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])
->first();
if (!$item) {
return \Yz::echoError1('此检查项目不可用');
}
$zhanweiCount = (int)$item->use_seats; // 该预约占用的号源数量
$rosterId = $mainInfo->roster_id; // 排班详情ID
if ($zhanweiCount <= 0) {
$zhanweiCount = 1; // 防止配置错误默认至少占1个号源
}
// 4. 查询排班详情并加锁(确保号源操作安全)
$planInfo = DB::table('s_source_roster_detail')
->where(['id' => $rosterId])
->lockForUpdate()
->first();
if (!$planInfo) {
return \Yz::echoError1('号源排班信息不存在');
}
// 5. 获取当前预约类型的合并渠道配置
$appointment_type_link = DB::table('s_appointment_type_ratio')
->where([
'department_id' => $planInfo->department_id,
'appointment_type_id' => $mainInfo->appointment_type_id
])
->first();
// 构建可用的预约类型列表(包含自身 + 合并渠道)
$appointment_types = [$mainInfo->appointment_type_id];
if (!empty($appointment_type_link->link)) {
$linkTypes = json_decode($appointment_type_link->link, true);
if (is_array($linkTypes)) {
$appointment_types = array_merge($appointment_types, $linkTypes);
}
$appointment_types = array_unique($appointment_types); // 去重
sort($appointment_types);
}
// 6. 获取当前渠道已使用的号源数量
$currentCountRecord = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->lockForUpdate() // 加锁
->first();
if (!$currentCountRecord) {
return \Yz::echoError1('当前渠道号源计数记录不存在');
}
$usedCurrent = (int)$currentCountRecord->used_count;
// 7. 开始释放号源:优先从当前渠道释放,不足则从合并渠道扣除
$remainingRelease = $zhanweiCount; // 还需释放的数量
// 先从当前预约类型渠道释放
if ($usedCurrent >= $remainingRelease) {
// 当前渠道足够释放
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->decrement('used_count', $remainingRelease);
$remainingRelease = 0;
} else {
// 当前渠道不够,全部释放当前渠道
$releasedFromCurrent = $usedCurrent;
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->update(['used_count' => 0]);
$remainingRelease -= $releasedFromCurrent;
}
// 如果还有剩余要释放的号源,从合并渠道中扣除
if ($remainingRelease > 0) {
foreach ($appointment_types as $typeId) {
// 跳过当前已处理的渠道
if ($typeId == $mainInfo->appointment_type_id) {
continue;
}
$mergeCountRecord = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId])
->lockForUpdate()
->first();
if (!$mergeCountRecord || $mergeCountRecord->used_count <= 0) {
continue;
}
$deduct = min($mergeCountRecord->used_count, $remainingRelease);
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId])
->decrement('used_count', $deduct);
$remainingRelease -= $deduct;
if ($remainingRelease <= 0) {
break;
}
}
// 如果最终仍无法完全释放所需号源数量,说明数据异常(理论上不应发生)
if ($remainingRelease > 0) {
DB::rollBack();
return \Yz::echoError1('号源释放失败:合并渠道号源不足,无法完成释放');
}
}
// 8. 更新主表状态为“已取消”0
$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,
'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);
// 9. 写入操作日志
$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)
'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, JSON_UNESCAPED_UNICODE),
'created_at' => $nowdatetime,
]);
$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('取消失败');
}
// 10. 提交事务
DB::commit();
return \Yz::Return(true, '取消成功', []);
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('取消异常');
// 记录错误日志(建议使用 Laravel 日志系统)
Log::error("取消预约失败 - ID: {$MainListId}, reg_num: {$reg_num}, 错误: " . $e->getMessage());
return \Yz::echoError1('取消失败:系统异常,请稍后重试');
}
}
//短信提醒
public function SendMsg($infos,$dotype=1)

Loading…
Cancel
Save