预约优化

main
鹿和sa0ChunLuyu 1 week ago
parent 9eb4323eeb
commit e15d1c9926

@ -434,12 +434,44 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint
$endDate = new DateTime(); $endDate = new DateTime();
$endDate->modify('+' . $dateRange . ' day'); $endDate->modify('+' . $dateRange . ' day');
$debug = [
'input_params' => [
'type' => $type,
'type_type' => gettype($type),
'type_length' => is_string($type) ? strlen($type) : null,
'type_hex' => is_string($type) ? bin2hex($type) : null,
'regnum' => $regnum,
'entrustid_count' => count($entrustids),
'entrustid' => $entrustids,
'episodeid' => $episodeid,
'appointment_type' => $appointment_type,
'items_count' => count($items),
'item_ids' => array_column($items, 'id'),
'item_entrust_ids' => array_column($items, 'entrust_id'),
'occupied_plan_ids' => $occupied_plan_ids,
'startDate' => $startDate->format('Y-m-d'),
'endDate' => $endDate->format('Y-m-d'),
'scope' => $scope,
],
'dispatch' => null,
'trace' => [],
];
if ($type === 'full_day') { if ($type === 'full_day') {
return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); $debug['dispatch'] = 'doFullDayAssign';
$response = $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids);
$response['debug'] = $debug;
return $response;
} elseif ($type === 'same_day_slot') { } elseif ($type === 'same_day_slot') {
return $this->doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); $debug['dispatch'] = 'doSameDaySlotAssign';
$response = $this->doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids, $debug);
$response['debug'] = $debug;
return $response;
} else { } else {
return $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); $debug['dispatch'] = 'doOptimalTimeAssign (兜底)';
$response = $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids);
$response['debug'] = $debug;
return $response;
} }
} }
@ -630,18 +662,31 @@ private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $ep
} }
// 同一天同一时间段分配:所有 item 必须在同一天且同一时间段完成 // 同一天同一时间段分配:所有 item 必须在同一天且同一时间段完成
private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = []) private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = [], &$debug = [])
{ {
// 按科室分组 // 按科室分组
$deptGroups = $this->groupItemsByDept($items); $deptGroups = $this->groupItemsByDept($items);
if (empty($deptGroups)) { if (empty($deptGroups)) {
$debug['error'] = 'groupItemsByDept 返回空分组';
return \Yz::Return(true, '获取成功', []); return \Yz::Return(true, '获取成功', []);
} }
$deptGroupSummary = [];
foreach ($deptGroups as $deptCode => $group) {
$deptGroupSummary[$deptCode] = [
'entrustid_count' => count($group['entrustids']),
'entrustids' => $group['entrustids'],
'item_count' => count($group['items']),
'item_ids' => array_column($group['items'], 'id'),
];
}
$debug['deptGroups'] = $deptGroupSummary;
$currentDate = clone $startDate; $currentDate = clone $startDate;
while ($currentDate <= $endDate) { while ($currentDate <= $endDate) {
$nowdate = $currentDate->format('Y-m-d'); $nowdate = $currentDate->format('Y-m-d');
$dayTrace = ['date' => $nowdate, 'steps' => []];
// 收集所有科室在该日期的可用号源 // 收集所有科室在该日期的可用号源
$allDeptPlans = []; $allDeptPlans = [];
@ -650,15 +695,26 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
foreach ($deptGroups as $deptCode => $group) { foreach ($deptGroups as $deptCode => $group) {
$availablePlans = $this->getAvailablePlansForDate($regnum, $group['entrustids'], $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); $availablePlans = $this->getAvailablePlansForDate($regnum, $group['entrustids'], $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
$planInfo = [
'deptCode' => $deptCode,
'raw_plan_count' => count($availablePlans),
'raw_plan_ids' => array_map(function($p) { return $p->id; }, $availablePlans),
'raw_plan_times' => array_map(function($p) { return $p->begin_time . '-' . $p->end_time; }, $availablePlans),
];
// 过滤已占用的号源 // 过滤已占用的号源
if (!empty($occupied_plan_ids)) { if (!empty($occupied_plan_ids)) {
$availablePlans = array_filter($availablePlans, function($plan) use ($occupied_plan_ids) { $availablePlans = array_filter($availablePlans, function($plan) use ($occupied_plan_ids) {
return !in_array($plan->id, $occupied_plan_ids); return !in_array($plan->id, $occupied_plan_ids);
}); });
$availablePlans = array_values($availablePlans); $availablePlans = array_values($availablePlans);
$planInfo['after_occupy_filter_count'] = count($availablePlans);
$planInfo['after_occupy_filter_ids'] = array_map(function($p) { return $p->id; }, $availablePlans);
} }
if (empty($availablePlans)) { if (empty($availablePlans)) {
$planInfo['no_plans'] = true;
$dayTrace['steps'][] = $planInfo;
$allAssigned = false; $allAssigned = false;
break; break;
} }
@ -667,6 +723,8 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
} }
if (!$allAssigned) { if (!$allAssigned) {
$dayTrace['result'] = 'skip: 有科室无可用号源';
$debug['trace'][] = $dayTrace;
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
continue; continue;
} }
@ -686,13 +744,45 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
} }
} }
$slotGroupSummary = [];
foreach ($slotGroups as $slotKey => $deptPlans) {
$entry = [];
foreach ($deptPlans as $deptCode => $plans) {
$entry[$deptCode] = [
'count' => count($plans),
'plan_ids' => array_map(function($p) { return $p->id; }, $plans),
'capacities' => array_map(function($p) { return ($p->count ?? 0) - ($p->used_count ?? 0); }, $plans),
];
}
$missingDepts = [];
foreach ($deptGroups as $deptCode => $group) {
if (!isset($deptPlans[$deptCode])) {
$missingDepts[] = $deptCode;
}
}
$entry['missing_depts'] = $missingDepts;
$slotGroupSummary[$slotKey] = $entry;
}
$dayTrace['steps'][] = [
'action' => 'slot_grouping',
'slot_count' => count($slotGroups),
'slot_keys' => array_keys($slotGroups),
'slot_details' => $slotGroupSummary,
];
// 对每个时间段,尝试分配所有项目 // 对每个时间段,尝试分配所有项目
foreach ($slotGroups as $slotKey => $deptPlans) { foreach ($slotGroups as $slotKey => $deptPlans) {
$result = []; $result = [];
$allInSlot = true; $allInSlot = true;
$slotTrace = ['slot' => $slotKey, 'dept_assignments' => []];
foreach ($deptGroups as $deptCode => $group) { foreach ($deptGroups as $deptCode => $group) {
if (!isset($deptPlans[$deptCode])) { if (!isset($deptPlans[$deptCode])) {
$slotTrace['dept_assignments'][] = [
'deptCode' => $deptCode,
'result' => 'missing_in_slot',
];
$allInSlot = false; $allInSlot = false;
break; break;
} }
@ -700,6 +790,7 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
$availablePlans = $deptPlans[$deptCode]; $availablePlans = $deptPlans[$deptCode];
$usedCapacity = []; $usedCapacity = [];
$assignedPlans = []; $assignedPlans = [];
$deptAssignTrace = ['deptCode' => $deptCode, 'items' => []];
foreach ($group['items'] as $item) { foreach ($group['items'] as $item) {
$assigned = false; $assigned = false;
@ -708,7 +799,17 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0); $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$used = $usedCapacity[$planId] ?? 0; $used = $usedCapacity[$planId] ?? 0;
if (($remaining - $used) <= 0) continue; if (($remaining - $used) <= 0) {
$deptAssignTrace['items'][] = [
'item_id' => $item['id'],
'entrust_id' => $item['entrust_id'],
'result' => 'capacity_exhausted',
'plan_id' => $planId,
'remaining' => $remaining,
'used_in_session' => $used,
];
continue;
}
$result[] = [ $result[] = [
'id' => $item['id'], 'id' => $item['id'],
@ -724,24 +825,48 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids,
'begin_time' => $plan->begin_time, 'begin_time' => $plan->begin_time,
'end_time' => $plan->end_time 'end_time' => $plan->end_time
]; ];
$deptAssignTrace['items'][] = [
'item_id' => $item['id'],
'entrust_id' => $item['entrust_id'],
'result' => 'assigned',
'plan_id' => $planId,
];
$assigned = true; $assigned = true;
break; break;
} }
if (!$assigned) { if (!$assigned) {
$deptAssignTrace['items'][] = [
'item_id' => $item['id'],
'entrust_id' => $item['entrust_id'],
'result' => 'no_available_plan',
];
$allInSlot = false; $allInSlot = false;
break 2; break 2;
} }
} }
$slotTrace['dept_assignments'][] = $deptAssignTrace;
} }
if ($allInSlot) { if ($allInSlot) {
$slotTrace['result'] = 'success';
$dayTrace['steps'][] = $slotTrace;
$dayTrace['result'] = 'success';
$debug['trace'][] = $dayTrace;
return \Yz::Return(true, '获取成功', $result); return \Yz::Return(true, '获取成功', $result);
} }
$slotTrace['result'] = 'failed';
$slotTrace['partial_result_count'] = count($result);
$slotTrace['partial_result'] = $result;
$dayTrace['steps'][] = $slotTrace;
} }
$dayTrace['result'] = 'all_slots_failed';
$debug['trace'][] = $dayTrace;
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
} }
$debug['result'] = 'no_solution_in_range';
return \Yz::Return(true, '获取成功', []); return \Yz::Return(true, '获取成功', []);
} }

Loading…
Cancel
Save