@ -156,46 +156,49 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$appointment_dates_placeholders = implode(',', array_fill(0, count($appointment_date_arr), '?'));
$appointment_dates_placeholders = implode(',', array_fill(0, count($appointment_date_arr), '?'));
if ($isDeviceMode) {
if ($isDeviceMode) {
//设备模式:不限制科室,只按单渠道查号源,渠道合路后置处理
//设备模式: 以排班资源为主表, LEFT JOIN 排班明细
$patientType = $commPatientType[0] ?? '0';
if ($useScopeDept & & !empty($deptIds)) {
if ($useScopeDept & & !empty($deptIds)) {
$deptPlaceholders = implode(',', array_fill(0, count($deptIds), '?'));
$deptPlaceholders = implode(',', array_fill(0, count($deptIds), '?'));
$canshu = array_merge($commonDevice, $appointment_date_arr, $deptIds , [$appointment_type]);
$canshu = array_merge($commonDevice, [$patientType], $appointment_date_arr, [$appointment_type], $commonDevice, $deptIds );
$deptCondition = "AND a .department_id IN ($deptPlaceholders)";
$deptCondition = "AND b .department_id IN ($deptPlaceholders)";
} else {
} else {
$canshu = array_merge($commonDevice, $appointment_date_arr, [$appointment_type]);
$canshu = array_merge($commonDevice, [$patientType], $appointment_date_arr, [$appointment_type], $commonDevice );
$deptCondition = '';
$deptCondition = '';
}
}
$plan = DB::select("SELECT
$plan = DB::select("SELECT
a.*,
b.id AS resource_id,
dd.devices,
b.department_resources_name,
b.department_resources_name,
b.department_id AS resource_department_id,
dept.department_number,
a.*,
c.roster_detail_id,
c.roster_detail_id,
c.count,
c.count,
c.used_count,
c.used_count,
c.locked_count
c.locked_count,
dd.devices
FROM
FROM
s_source_roster_detail AS a
s_department_resources AS b
LEFT JOIN s_department_resources AS b ON a.resources_id = b.id
JOIN s_department_resources_device AS rd ON b.id = rd.rsourece_id AND rd.device_id IN ($placeholders)
LEFT JOIN s_source_roster_detail_count AS c ON a.id = c.roster_detail_id
LEFT JOIN s_department AS dept ON b.department_id = dept.id
JOIN (
LEFT JOIN s_source_roster_detail AS a ON a.resources_id = b.id AND FIND_IN_SET(?, a.patient_type) AND a.date IN ($appointment_dates_placeholders) AND a.is_del = 0
LEFT JOIN s_source_roster_detail_count AS c ON c.roster_detail_id = a.id AND c.appointment_type_id = ?
LEFT JOIN (
SELECT
SELECT
roster_detail_id,
rd2.rsourece _id,
GROUP_CONCAT( e.device_name SEPARATOR ', ' ) AS devices
GROUP_CONCAT( e.device_name SEPARATOR ', ' ) AS devices
FROM
FROM
s_source_roster_detail_device AS d
s_department_resources_device AS rd2
LEFT JOIN s_devices AS e ON d.device_id = e.id
LEFT JOIN s_devices AS e ON r d2 .device_id = e.id
WHERE
WHERE
d.device_id IN ($placeholders)
r d2 .device_id IN ($placeholders)
GROUP BY
GROUP BY
d.roster_detail _id
rd2.rsourece _id
) AS dd ON a.id = dd.roster_detail _id
) AS dd ON b.id = dd.rsourece _id
WHERE
WHERE
a.date IN ($appointment_dates_placeholders)
b.is_del = 0
$deptCondition
AND a.STATUS = 1
AND a.is_del = 0
AND b.is_del = 0
AND b.appointment_enabled = 1
AND b.appointment_enabled = 1
$deptCondition
AND (
AND (
b.execute_department_id IS NULL
b.execute_department_id IS NULL
OR b.execute_department_id = 0
OR b.execute_department_id = 0
@ -206,55 +209,59 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
AND appointment_enabled = 1
AND appointment_enabled = 1
AND department_status = 1
AND department_status = 1
)
)
)
)", $canshu);
AND c.appointment_type_id = ?", $canshu);
foreach ($plan as $p) {
foreach ($plan as $p) {
if (!empty($p->id)) {
$p->used_count = $p->used_count + ($p->locked_count ?? 0);
$p->used_count = $p->used_count + ($p->locked_count ?? 0);
}
}
}
$plan = $this->mergeChannelsPerDepartment($plan, $appointment_type);
$plan = $this->mergeChannelsPerDepartment($plan, $appointment_type);
} else {
} else {
//科室模式:原有逻辑
//科室模式: 以排班资源为主表, LEFT JOIN 排班明细
$patientType = $commPatientType[0] ?? '0';
if ($useScopeDept) {
if ($useScopeDept) {
$deptPlaceholders = implode(',', array_fill(0, count($deptIds), '?'));
$deptPlaceholders = implode(',', array_fill(0, count($deptIds), '?'));
$canshu = array_merge($commonDevice, $deptIds, $appointment_date_arr, $appointment_type s);
$canshu = array_merge($commonDevice, [$patientType], $appointment_date_arr, $appointment_types, $commonDevice, $deptId s);
$deptCondition = "a .department_id IN ($deptPlaceholders)";
$deptCondition = "b .department_id IN ($deptPlaceholders)";
} else {
} else {
$canshu = array_merge($commonDevice, [$department_id->id], $appointment_date_arr, $appointment_types );
$canshu = array_merge($commonDevice, [$patientType], $appointment_date_arr, $appointment_types, $commonDevice, [$department_id->id] );
$deptCondition = "a .department_id = ?";
$deptCondition = "b .department_id = ?";
}
}
$plan = DB::select("SELECT
$plan = DB::select("SELECT
a.*,
b.id AS resource_id,
dd.devices,
b.department_resources_name,
b.department_resources_name,
b.department_id AS resource_department_id,
dept.department_number,
a.*,
c.roster_detail_id,
c.roster_detail_id,
c.count,
c.count,
c.used_count,
c.used_count,
c.locked_count
c.locked_count,
dd.devices
FROM
FROM
s_source_roster_detail AS a
s_department_resources AS b
LEFT JOIN s_department_resources AS b ON a.resources_id = b.id
JOIN s_department_resources_device AS rd ON b.id = rd.rsourece_id AND rd.device_id IN ($placeholders)
LEFT JOIN s_source_roster_detail_count AS c ON a.id = c.roster_detail_id
LEFT JOIN s_department AS dept ON b.department_id = dept.id
JOIN (
LEFT JOIN s_source_roster_detail AS a ON a.resources_id = b.id AND FIND_IN_SET(?, a.patient_type) AND a.date IN ($appointment_dates_placeholders) AND a.is_del = 0
LEFT JOIN s_source_roster_detail_count AS c ON c.roster_detail_id = a.id AND c.appointment_type_id IN ($appointment_types_placeholders)
LEFT JOIN (
SELECT
SELECT
roster_detail_id,
rd2.rsourece _id,
GROUP_CONCAT( e.device_name SEPARATOR ', ' ) AS devices
GROUP_CONCAT( e.device_name SEPARATOR ', ' ) AS devices
FROM
FROM
s_source_roster_detail_device AS d
s_department_resources_device AS rd2
LEFT JOIN s_devices AS e ON d.device_id = e.id
LEFT JOIN s_devices AS e ON r d2 .device_id = e.id
WHERE
WHERE
d.device_id IN ($placeholders)
r d2 .device_id IN ($placeholders)
GROUP BY
GROUP BY
d.roster_detail _id
rd2.rsourece _id
) AS dd ON a.id = dd.roster_detail _id
) AS dd ON b.id = dd.rsourece _id
WHERE
WHERE
$deptCondition
b.is_del = 0
AND a.date IN ($appointment_dates_placeholders)
AND a.STATUS = 1
AND a.is_del = 0
AND b.is_del = 0
AND b.appointment_enabled = 1
AND b.appointment_enabled = 1
AND $deptCondition
AND (
AND (
b.execute_department_id IS NULL
b.execute_department_id IS NULL
OR b.execute_department_id = 0
OR b.execute_department_id = 0
@ -265,11 +272,11 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
AND appointment_enabled = 1
AND appointment_enabled = 1
AND department_status = 1
AND department_status = 1
)
)
)
)", $canshu);
AND c.appointment_type_id IN ($appointment_types_placeholders)", $canshu);
$mergedPlan = [];
$mergedPlan = [];
foreach ($plan as $key => $p) {
foreach ($plan as $key => $p) {
if (!empty($p->id)) {
$p->used_count = $p->used_count + ($p->locked_count ?? 0);
$p->used_count = $p->used_count + ($p->locked_count ?? 0);
if (isset($mergedPlan[$p->roster_detail_id])) {
if (isset($mergedPlan[$p->roster_detail_id])) {
$mergedPlan[$p->roster_detail_id]->count += $p->count;
$mergedPlan[$p->roster_detail_id]->count += $p->count;
@ -277,6 +284,10 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
} else {
} else {
$mergedPlan[$p->roster_detail_id] = $p;
$mergedPlan[$p->roster_detail_id] = $p;
}
}
} else {
// 排班明细为 NULL( 暂无号源) , 不参与渠道合并, 直接保留
$mergedPlan[] = $p;
}
}
}
$plan = array_values($mergedPlan);
$plan = array_values($mergedPlan);
@ -293,6 +304,8 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$debugPassed = []; // 记录通过的
$debugPassed = []; // 记录通过的
$pl1 = [];
$pl1 = [];
$pl2 = [];
$pl2 = [];
$disabledPlans = []; // status=0 的排班明细
$enabledResourceIds = []; // 有 status=1 明细的排班资源ID集合
$pp = [];
$pp = [];
$nowtime = date('Y-m-d H:i:s');
$nowtime = date('Y-m-d H:i:s');
@ -320,14 +333,25 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
]);
]);
foreach ($plan as $key => $p) {
foreach ($plan as $key => $p) {
// //病人类型不符合的过滤掉
// 排班明细为 NULL( 无排班明细或患者类型不匹配) : 直接丢弃
$planPatientType = explode(",", $p->patient_type);
if (empty($p->id)) {
$diff = array_diff($commPatientType, $planPatientType);
continue;
if (!empty($diff)) {
}
$debugSkipped[] = ['rid' => $p->resources_id, 'reason' => 'patient_type', 'planTypes' => $p->patient_type, 'commTypes' => $commPatientType, 'diff' => array_values($diff)];
$p->no_plan = false;
// status=0 的排班明细:强制 0/0, 标记不可用, 暂存等待分组判定
if ($p->status == 0) {
$p->count = 0;
$p->used_count = 0;
$p->locked_count = 0;
$p->plan_enable = false;
$disabledPlans[] = $p;
continue;
continue;
}
}
// 记录该排班资源存在 status=1 的明细
$enabledResourceIds[$p->resource_id] = true;
//检查项目时段规则过滤
//检查项目时段规则过滤
if (!empty($allItemRules)) {
if (!empty($allItemRules)) {
$rulePass = $this->checkPlanAgainstRules($p, $allItemRules);
$rulePass = $this->checkPlanAgainstRules($p, $allItemRules);
@ -353,6 +377,18 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
}
}
}
}
// status=0 的排班明细分流:同资源存在 status=1 的归入 $pl2, 全部 status=0 的归入第二段
$allDisabledPlans = []; // 第二段:排班资源全部明细 status=0
foreach ($disabledPlans as $dp) {
if (isset($enabledResourceIds[$dp->resource_id])) {
// 该资源还有 status=1 的明细 → 部分禁用,参与正常排序
$pl2[] = $dp;
} else {
// 该资源全部明细 status=0 → 全部禁用,进入第二段
$allDisabledPlans[] = $dp;
}
}
usort($pl1, function($a, $b) {
usort($pl1, function($a, $b) {
$timeA = $a->date . ' ' . ($a->start_time ?? $a->end_time); // 兼容性处理
$timeA = $a->date . ' ' . ($a->start_time ?? $a->end_time); // 兼容性处理
$timeB = $b->date . ' ' . ($b->start_time ?? $b->end_time);
$timeB = $b->date . ' ' . ($b->start_time ?? $b->end_time);
@ -371,12 +407,20 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$pp = array_merge($pl1, $pl2);
$pp = array_merge($pl1, $pl2);
// 诊室排序:按最早可用时间升序 → 最早时段剩余号源降序 → 随机
// 诊室排序:按最早可用时间升序 → 最早时段剩余号源降序 → 随机
// 统一构建诊室分组:第一段 + 第二段 + 第三段
$grouped = [];
$grouped = [];
$nowTimestamp = time();
$nowTimestamp = time();
foreach ($pp as $plan) {
// 辅助函数:把一批记录加入 $grouped
$addToGrouped = function($records, $priority) use (& $grouped, $nowTimestamp) {
foreach ($records as $plan) {
$name = $plan->department_resources_name;
$name = $plan->department_resources_name;
if (!isset($grouped[$name])) {
if (!isset($grouped[$name])) {
$grouped[$name] = ['plans' => [], 'remaining' => 0, 'earliest_remaining' => 0];
$grouped[$name] = ['plans' => [], 'remaining' => 0, 'earliest_remaining' => 0, 'priority' => $priority];
}
// priority 取最小值(如果有同名的记录分属不同段,以更靠前的段为准)
if ($priority < $grouped[$name]['priority']) {
$grouped[$name]['priority'] = $priority;
}
}
$grouped[$name]['plans'][] = $plan;
$grouped[$name]['plans'][] = $plan;
@ -384,7 +428,8 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$grouped[$name]['remaining'] += max($remaining, 0);
$grouped[$name]['remaining'] += max($remaining, 0);
// 判断是否未过期,收集最早开始时间及该时段剩余
// 收集 earliest: 仅 status=1 且 remaining>0 且未过期
if (($plan->status ?? 1) == 1 & & $remaining > 0) {
$endDateTime = $plan->date . ' ' . ($plan->end_time ?? '23:59:59');
$endDateTime = $plan->date . ' ' . ($plan->end_time ?? '23:59:59');
if (strtotime($endDateTime) > $nowTimestamp) {
if (strtotime($endDateTime) > $nowTimestamp) {
$startDateTime = $plan->date . ' ' . ($plan->begin_time ?? '00:00:00');
$startDateTime = $plan->date . ' ' . ($plan->begin_time ?? '00:00:00');
@ -398,6 +443,11 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
}
}
}
}
}
}
}
};
$addToGrouped($pp, 0); // 第一段:可用 + 部分禁用
$addToGrouped($allDisabledPlans, 1); // 第二段:全部 status=0
// 诊室排序:先转为索引数组,再手动排序
// 诊室排序:先转为索引数组,再手动排序
$grouped = array_values($grouped);
$grouped = array_values($grouped);
@ -408,27 +458,34 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$hasEarliest[$idx] = isset($g['earliest']);
$hasEarliest[$idx] = isset($g['earliest']);
}
}
// 简单冒泡排序(只有 2 个诊室时足够)
// 简单冒泡排序
for ($i = 0; $i < count ( $ grouped ) - 1 ; $ i + + ) {
for ($i = 0; $i < count ( $ grouped ) - 1 ; $ i + + ) {
for ($j = $i + 1; $j < count ( $ grouped ) ; $ j + + ) {
for ($j = $i + 1; $j < count ( $ grouped ) ; $ j + + ) {
$a = $grouped[$i];
$a = $grouped[$i];
$b = $grouped[$j];
$b = $grouped[$j];
$aHas = isset($a['earliest']);
$bHas = isset($b['earliest']);
$swap = false;
$swap = false;
// 1. priority 不同 → 小的排前面
if ($a['priority'] != $b['priority']) {
$swap = ($a['priority'] > $b['priority']);
} else {
$aHas = isset($a['earliest']);
$bHas = isset($b['earliest']);
// 2. 同 priority, 有 earliest 排前面
if ($aHas != $bHas) {
if ($aHas != $bHas) {
$swap = $bHas; // 有可用号源的排前面
$swap = $bHas;
} elseif ($aHas) {
} elseif ($aHas) {
// 3. 都有 earliest → 时间升序 → remaining 降序 → 随机
if ($a['earliest'] != $b['earliest']) {
if ($a['earliest'] != $b['earliest']) {
$swap = ($a['earliest'] > $b['earliest']); // 最早时间升序
$swap = ($a['earliest'] > $b['earliest']);
} elseif ($a['earliest_remaining'] != $b['earliest_remaining']) {
} elseif ($a['earliest_remaining'] != $b['earliest_remaining']) {
$swap = ($a['earliest_remaining'] < $b['earliest_remaining']); // 最早时段剩余号源降序
$swap = ($a['earliest_remaining'] < $b['earliest_remaining']);
} else {
} else {
$swap = (rand(0, 1) == 1); // 随机
$swap = (rand(0, 1) == 1);
}
}
} else {
} else {
$swap = (rand(0, 1) == 1); // 都无可用号源,随机
$swap = (rand(0, 1) == 1);
}
}
}
if ($swap) {
if ($swap) {
@ -2351,7 +2408,13 @@ private function mergeChannelsPerDepartment($plan, $appointment_type)
}
}
$mergedPlan = [];
$mergedPlan = [];
$noPlanRecords = []; // 排班明细为 NULL 的记录,不参与渠道合并
foreach ($plan as $p) {
foreach ($plan as $p) {
// 排班明细为 NULL( 暂无号源) , 不参与渠道合并, 直接保留
if (empty($p->roster_detail_id)) {
$noPlanRecords[] = $p;
continue;
}
$rid = $p->roster_detail_id;
$rid = $p->roster_detail_id;
$deptChannels = $mergeConfigs[$p->department_id] ?? [];
$deptChannels = $mergeConfigs[$p->department_id] ?? [];
@ -2378,7 +2441,7 @@ private function mergeChannelsPerDepartment($plan, $appointment_type)
}
}
}
}
return array_values($mergedPlan);
return array_merge(array_ values($mergedPlan), $noPlanRecords );
}
}
//判断是否在互斥组内
//判断是否在互斥组内