|
|
|
@ -196,6 +196,17 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
|
|
|
|
AND a.is_del = 0
|
|
|
|
AND a.is_del = 0
|
|
|
|
AND b.is_del = 0
|
|
|
|
AND b.is_del = 0
|
|
|
|
AND b.appointment_enabled = 1
|
|
|
|
AND b.appointment_enabled = 1
|
|
|
|
|
|
|
|
AND (
|
|
|
|
|
|
|
|
b.execute_department_id IS NULL
|
|
|
|
|
|
|
|
OR b.execute_department_id = 0
|
|
|
|
|
|
|
|
OR EXISTS (
|
|
|
|
|
|
|
|
SELECT 1 FROM s_department
|
|
|
|
|
|
|
|
WHERE id = b.execute_department_id
|
|
|
|
|
|
|
|
AND is_del = 0
|
|
|
|
|
|
|
|
AND appointment_enabled = 1
|
|
|
|
|
|
|
|
AND department_status = 1
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
)
|
|
|
|
AND c.appointment_type_id = ?", $canshu);
|
|
|
|
AND c.appointment_type_id = ?", $canshu);
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($plan as $p) {
|
|
|
|
foreach ($plan as $p) {
|
|
|
|
@ -244,6 +255,17 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
|
|
|
|
AND a.is_del = 0
|
|
|
|
AND a.is_del = 0
|
|
|
|
AND b.is_del = 0
|
|
|
|
AND b.is_del = 0
|
|
|
|
AND b.appointment_enabled = 1
|
|
|
|
AND b.appointment_enabled = 1
|
|
|
|
|
|
|
|
AND (
|
|
|
|
|
|
|
|
b.execute_department_id IS NULL
|
|
|
|
|
|
|
|
OR b.execute_department_id = 0
|
|
|
|
|
|
|
|
OR EXISTS (
|
|
|
|
|
|
|
|
SELECT 1 FROM s_department
|
|
|
|
|
|
|
|
WHERE id = b.execute_department_id
|
|
|
|
|
|
|
|
AND is_del = 0
|
|
|
|
|
|
|
|
AND appointment_enabled = 1
|
|
|
|
|
|
|
|
AND department_status = 1
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
)
|
|
|
|
AND c.appointment_type_id IN ($appointment_types_placeholders)", $canshu);
|
|
|
|
AND c.appointment_type_id IN ($appointment_types_placeholders)", $canshu);
|
|
|
|
|
|
|
|
|
|
|
|
$mergedPlan = [];
|
|
|
|
$mergedPlan = [];
|
|
|
|
@ -548,36 +570,27 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 最优时间分配:按科室分组,逐个 item 找最早可用号源,可跨天
|
|
|
|
// 最优时间分配:逐项目独立找最早可用号源,可跨天
|
|
|
|
private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = [], $existingSharedAtPlan = [])
|
|
|
|
private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = [], $existingSharedAtPlan = [])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 按科室分组
|
|
|
|
|
|
|
|
$deptGroups = $this->groupItemsByDept($items);
|
|
|
|
|
|
|
|
if (empty($deptGroups)) {
|
|
|
|
|
|
|
|
return \Yz::Return(true, '获取成功', []);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$result = [];
|
|
|
|
$result = [];
|
|
|
|
$currentDate = clone $startDate;
|
|
|
|
$usedCapacity = []; // 记录本次推荐中已分配占用的容量
|
|
|
|
// 每组待分配的 items
|
|
|
|
$assignedPlans = []; // 记录已分配的时间段,用于冲突检查
|
|
|
|
$remainingGroups = [];
|
|
|
|
|
|
|
|
foreach ($deptGroups as $deptCode => $group) {
|
|
|
|
|
|
|
|
$remainingGroups[$deptCode] = [
|
|
|
|
|
|
|
|
'entrustids' => $group['entrustids'],
|
|
|
|
|
|
|
|
'items' => $group['items']
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while ($currentDate <= $endDate && !empty($remainingGroups)) {
|
|
|
|
// 逐项目独立分配
|
|
|
|
$nowdate = $currentDate->format('Y-m-d');
|
|
|
|
foreach ($items as $item) {
|
|
|
|
$newRemainingGroups = [];
|
|
|
|
$entrustId = $item['entrust_id'] ?? null;
|
|
|
|
$globalAssignedPlans = []; // 跨科室组的全局已分配时间段
|
|
|
|
if (!$entrustId) {
|
|
|
|
|
|
|
|
$result[] = ['id' => $item['id'], 'plan_id' => 0, 'department_resources_name' => '', 'date' => '', 'begin_time' => '', 'end_time' => ''];
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($remainingGroups as $deptCode => $group) {
|
|
|
|
$assigned = false;
|
|
|
|
$groupEntrustids = $group['entrustids'];
|
|
|
|
$currentDate = clone $startDate;
|
|
|
|
$groupItems = $group['items'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$availablePlans = $this->getAvailablePlansForDate($regnum, $groupEntrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
|
|
|
|
while ($currentDate <= $endDate) {
|
|
|
|
|
|
|
|
$nowdate = $currentDate->format('Y-m-d');
|
|
|
|
|
|
|
|
$availablePlans = $this->getAvailablePlansForDate($regnum, [$entrustId], $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤已占用的号源
|
|
|
|
// 过滤已占用的号源
|
|
|
|
if (!empty($occupied_plan_ids)) {
|
|
|
|
if (!empty($occupied_plan_ids)) {
|
|
|
|
@ -588,72 +601,52 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($availablePlans)) {
|
|
|
|
if (empty($availablePlans)) {
|
|
|
|
// 当天该科室无号源,整组留到下一轮
|
|
|
|
$currentDate->modify('+1 day');
|
|
|
|
$newRemainingGroups[$deptCode] = $group;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$usedCapacity = [];
|
|
|
|
// 找到第一个能容纳的号源
|
|
|
|
$assignedPlans = [];
|
|
|
|
foreach ($availablePlans as $plan) {
|
|
|
|
$remainingItems = [];
|
|
|
|
$planId = $plan->id;
|
|
|
|
|
|
|
|
$useSeats = $item['use_seats'] ?? 1;
|
|
|
|
foreach ($groupItems as $item) {
|
|
|
|
if (!empty($item['is_share_slot']) && $useSeats > 0) {
|
|
|
|
$assigned = false;
|
|
|
|
$existingMax = $existingSharedAtPlan[$planId] ?? 0;
|
|
|
|
foreach ($availablePlans as $plan) {
|
|
|
|
$useSeats = max(0, $useSeats - $existingMax);
|
|
|
|
$planId = $plan->id;
|
|
|
|
|
|
|
|
$useSeats = $item['use_seats'] ?? 1;
|
|
|
|
|
|
|
|
// 共享项目号源持有者:减去已有共享项目的 max
|
|
|
|
|
|
|
|
if (!empty($item['is_share_slot']) && $useSeats > 0) {
|
|
|
|
|
|
|
|
$existingMax = $existingSharedAtPlan[$planId] ?? 0;
|
|
|
|
|
|
|
|
$useSeats = max(0, $useSeats - $existingMax);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
|
|
|
|
|
|
|
|
$used = $usedCapacity[$planId] ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (($remaining - $used) < $useSeats) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 冲突检查:同一科室组内,不同排班时间不能重叠
|
|
|
|
|
|
|
|
if ($this->isTimeConflict($assignedPlans, $plan)) continue;
|
|
|
|
|
|
|
|
// 冲突检查:跨科室组,时间段不能重叠
|
|
|
|
|
|
|
|
if ($this->isTimeConflict($globalAssignedPlans, $plan)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$result[] = [
|
|
|
|
|
|
|
|
'id' => $item['id'],
|
|
|
|
|
|
|
|
'plan_id' => $planId,
|
|
|
|
|
|
|
|
'department_resources_name' => $plan->department_resources_name,
|
|
|
|
|
|
|
|
'date' => $plan->date,
|
|
|
|
|
|
|
|
'begin_time' => $plan->begin_time,
|
|
|
|
|
|
|
|
'end_time' => $plan->end_time
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
$usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + $useSeats;
|
|
|
|
|
|
|
|
$assignedPlans[] = [
|
|
|
|
|
|
|
|
'resource_name' => $plan->department_resources_name,
|
|
|
|
|
|
|
|
'begin_time' => $plan->begin_time,
|
|
|
|
|
|
|
|
'end_time' => $plan->end_time
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
$globalAssignedPlans[] = [
|
|
|
|
|
|
|
|
'resource_name' => $plan->department_resources_name,
|
|
|
|
|
|
|
|
'begin_time' => $plan->begin_time,
|
|
|
|
|
|
|
|
'end_time' => $plan->end_time
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
$assigned = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$assigned) {
|
|
|
|
|
|
|
|
$remainingItems[] = $item;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($remainingItems)) {
|
|
|
|
// 容量检查:减去数据库已用 + 本次已分配
|
|
|
|
$newRemainingGroups[$deptCode] = [
|
|
|
|
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
|
|
|
|
'entrustids' => $groupEntrustids,
|
|
|
|
$used = $usedCapacity[$planId] ?? 0;
|
|
|
|
'items' => $remainingItems
|
|
|
|
if (($remaining - $used) < $useSeats) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 冲突检查:同一患者的不同项目时间段不能重叠
|
|
|
|
|
|
|
|
if ($this->isTimeConflict($assignedPlans, $plan)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$result[] = [
|
|
|
|
|
|
|
|
'id' => $item['id'],
|
|
|
|
|
|
|
|
'plan_id' => $planId,
|
|
|
|
|
|
|
|
'department_resources_name' => $plan->department_resources_name,
|
|
|
|
|
|
|
|
'date' => $plan->date,
|
|
|
|
|
|
|
|
'begin_time' => $plan->begin_time,
|
|
|
|
|
|
|
|
'end_time' => $plan->end_time
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
$usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + $useSeats;
|
|
|
|
|
|
|
|
$assignedPlans[] = [
|
|
|
|
|
|
|
|
'resource_name' => $plan->department_resources_name,
|
|
|
|
|
|
|
|
'begin_time' => $plan->begin_time,
|
|
|
|
|
|
|
|
'end_time' => $plan->end_time
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
$assigned = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($assigned) break;
|
|
|
|
|
|
|
|
$currentDate->modify('+1 day');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$remainingGroups = $newRemainingGroups;
|
|
|
|
if (!$assigned) {
|
|
|
|
$currentDate->modify('+1 day');
|
|
|
|
$result[] = ['id' => $item['id'], 'plan_id' => 0, 'department_resources_name' => '', 'date' => '', 'begin_time' => '', 'end_time' => ''];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return \Yz::Return(true, '获取成功', $result);
|
|
|
|
return \Yz::Return(true, '获取成功', $result);
|
|
|
|
@ -1706,6 +1699,79 @@ public function CancelYuYue($MainListId, $reg_num,$do_user=null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//批量取消预约:统一事务,避免并发号源校正冲突
|
|
|
|
|
|
|
|
public function BatchCancelYuYue($ids, $reg_num, $do_user = null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
date_default_timezone_set('PRC');
|
|
|
|
|
|
|
|
$nowdatetime = date('Y-m-d H:i:s');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DB::beginTransaction();
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
$rosterIds = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($ids as $MainListId) {
|
|
|
|
|
|
|
|
$mainInfo = DB::table('s_list')
|
|
|
|
|
|
|
|
->where(['id' => $MainListId, 'reg_num' => $reg_num])
|
|
|
|
|
|
|
|
->lockForUpdate()
|
|
|
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!$mainInfo) {
|
|
|
|
|
|
|
|
throw new \Exception("医嘱不存在: {$MainListId}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($mainInfo->list_status != 1) {
|
|
|
|
|
|
|
|
throw new \Exception("{$mainInfo->entrust} 无法取消,当前状态:" . $mainInfo->list_status);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($mainInfo->roster_id) {
|
|
|
|
|
|
|
|
$rosterIds[] = $mainInfo->roster_id;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$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,
|
|
|
|
|
|
|
|
'appointment_use_plan_detail' => null,
|
|
|
|
|
|
|
|
'canel_time' => $nowdatetime,
|
|
|
|
|
|
|
|
'is_emergency' => 0,
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DB::table('s_list')->where(['id' => $MainListId])->update($u_data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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' => $do_user,
|
|
|
|
|
|
|
|
'note' => '取消预约',
|
|
|
|
|
|
|
|
'data' => json_encode($u_data, JSON_UNESCAPED_UNICODE),
|
|
|
|
|
|
|
|
'created_at' => $nowdatetime,
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 每个 roster 只校正一次
|
|
|
|
|
|
|
|
$rosterIds = array_unique($rosterIds);
|
|
|
|
|
|
|
|
foreach ($rosterIds as $rid) {
|
|
|
|
|
|
|
|
$this->correctUsedCount($rid);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DB::commit();
|
|
|
|
|
|
|
|
return \Yz::Return(true, '取消成功', []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
|
|
DB::rollBack();
|
|
|
|
|
|
|
|
Log::error('批量取消预约失败', ['ids' => $ids, 'error' => $e->getMessage()]);
|
|
|
|
|
|
|
|
return \Yz::echoError1($e->getMessage());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function releaseSourceFromDetail($mainListId, $mainInfo = null)
|
|
|
|
public function releaseSourceFromDetail($mainListId, $mainInfo = null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 由 correctUsedCount 校正替代,不再在此处释放号源
|
|
|
|
// 由 correctUsedCount 校正替代,不再在此处释放号源
|
|
|
|
@ -1737,7 +1803,7 @@ private function releaseSourceFallback($mainInfo)
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 校正排班号源:根据项目配置(is_share_slot / use_seats)重新计算 used_count
|
|
|
|
* 校正排班号源:根据项目配置(is_share_slot / use_seats)重新计算 used_count
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private function correctUsedCount($rosterId)
|
|
|
|
public function correctUsedCount($rosterId)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!$rosterId) return;
|
|
|
|
if (!$rosterId) return;
|
|
|
|
|
|
|
|
|
|
|
|
@ -1748,9 +1814,10 @@ private function correctUsedCount($rosterId)
|
|
|
|
|
|
|
|
|
|
|
|
// 获取该排班上所有活跃项目及其配置
|
|
|
|
// 获取该排班上所有活跃项目及其配置
|
|
|
|
$activeItems = DB::table('s_list')
|
|
|
|
$activeItems = DB::table('s_list')
|
|
|
|
->join('s_check_item', 's_list.entrust', '=', 's_check_item.item_name')
|
|
|
|
->join('s_check_item', 's_list.entrust_code', '=', 's_check_item.item_code')
|
|
|
|
->where('s_list.roster_id', $rosterId)
|
|
|
|
->where('s_list.roster_id', $rosterId)
|
|
|
|
->where('s_list.list_status', 1)
|
|
|
|
->whereIn('s_list.list_status', [1, 2, 3])
|
|
|
|
|
|
|
|
->where('s_list.is_nullify', 0)
|
|
|
|
->where('s_check_item.status', 1)
|
|
|
|
->where('s_check_item.status', 1)
|
|
|
|
->where('s_check_item.is_del', 0)
|
|
|
|
->where('s_check_item.is_del', 0)
|
|
|
|
->select('s_list.*', 's_check_item.use_seats', 's_check_item.is_share_slot')
|
|
|
|
->select('s_list.*', 's_check_item.use_seats', 's_check_item.is_share_slot')
|
|
|
|
|