自动匹配

main
鹿和sa0ChunLuyu 2 weeks ago
parent 3d22681335
commit 6eb4f6a0f0

@ -427,74 +427,109 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint
{ {
date_default_timezone_set('PRC'); date_default_timezone_set('PRC');
$dateRange = config('app.globals.可用号源查询范围'); $dateRange = config('app.globals.可用号源查询范围');
$result = [];
$usedCapacity = []; // 记录已分配的号源容量key: roster_detail_id, value: 已分配数
// 遍历日期范围
$startDate = new DateTime(); $startDate = new DateTime();
$endDate = new DateTime(); $endDate = new DateTime();
$endDate->modify('+' . $dateRange . ' day'); $endDate->modify('+' . $dateRange . ' day');
if ($type === 'full_day') {
return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId);
} else {
return $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId);
}
}
// 最优时间分配:逐个 item 找最早可用号源,可跨天
private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId)
{
$result = [];
$usedCapacity = [];
$assignedPlans = []; // 已分配的时间段 [{resource_name, begin, end}]
$currentDate = clone $startDate; $currentDate = clone $startDate;
$remainingItems = $items;
while ($currentDate <= $endDate) { while ($currentDate <= $endDate && !empty($remainingItems)) {
$nowdate = $currentDate->format('Y-m-d'); $nowdate = $currentDate->format('Y-m-d');
$availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
// 获取当前日期的可用号源 if (empty($availablePlans)) {
$s = $this->GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
if (!$s['status']) {
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
continue; continue;
} }
$planList = $s['data']['plan_list'] ?? []; $newRemaining = [];
if (count($planList) == 0) { foreach ($remainingItems as $item) {
$assigned = false;
foreach ($availablePlans as $plan) {
$planId = $plan->id;
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0);
$used = $usedCapacity[$planId] ?? 0;
if (($remaining - $used) <= 0) 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) + 1;
$assignedPlans[] = [
'resource_name' => $plan->department_resources_name,
'begin_time' => $plan->begin_time,
'end_time' => $plan->end_time
];
$assigned = true;
break;
}
if (!$assigned) {
$newRemaining[] = $item;
}
}
$remainingItems = $newRemaining;
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
continue;
} }
// 筛选未过期的、可用的号源 return \Yz::Return(true, '获取成功', $result);
$nowtime = date('Y-m-d H:i:s');
$availablePlans = [];
foreach ($planList as $plan) {
$planEndTime = $plan->date . ' ' . $plan->end_time;
if ($planEndTime > $nowtime && $plan->plan_enable) {
$availablePlans[] = $plan;
}
} }
if (count($availablePlans) == 0) { // 单天全检分配:所有 item 必须在同一天完成
private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId)
{
$currentDate = clone $startDate;
while ($currentDate <= $endDate) {
$nowdate = $currentDate->format('Y-m-d');
$availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
if (empty($availablePlans)) {
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
continue; continue;
} }
// 按日期、时间排序 $result = [];
usort($availablePlans, function($a, $b) { $usedCapacity = [];
$timeA = $a->date . ' ' . ($a->begin_time ?? '00:00:00'); $assignedPlans = [];
$timeB = $b->date . ' ' . ($b->begin_time ?? '00:00:00'); $allAssigned = true;
return $timeA <=> $timeB;
});
// 遍历 items逐个分配
foreach ($items as $item) { foreach ($items as $item) {
// 如果该 item 已经分配,跳过 $assigned = false;
$alreadyAssigned = false;
foreach ($result as $r) {
if ($r['id'] == $item['id']) {
$alreadyAssigned = true;
break;
}
}
if ($alreadyAssigned) continue;
// 找可用号源
foreach ($availablePlans as $plan) { foreach ($availablePlans as $plan) {
$planId = $plan->id; $planId = $plan->id;
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0); $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0);
$used = $usedCapacity[$planId] ?? 0; $used = $usedCapacity[$planId] ?? 0;
if (($remaining - $used) > 0) { if (($remaining - $used) <= 0) continue;
// 分配此号源
// 冲突检查:不同排班且时间段重叠
if ($this->isTimeConflict($assignedPlans, $plan)) continue;
$result[] = [ $result[] = [
'id' => $item['id'], 'id' => $item['id'],
'plan_id' => $planId, 'plan_id' => $planId,
@ -504,20 +539,77 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint
'end_time' => $plan->end_time 'end_time' => $plan->end_time
]; ];
$usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1;
$assignedPlans[] = [
'resource_name' => $plan->department_resources_name,
'begin_time' => $plan->begin_time,
'end_time' => $plan->end_time
];
$assigned = true;
break; break;
} }
if (!$assigned) {
$allAssigned = false;
break;
} }
} }
// 如果所有 items 都已分配,停止遍历 if ($allAssigned) {
if (count($result) >= count($items)) { return \Yz::Return(true, '获取成功', $result);
break;
} }
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
} }
return \Yz::Return(true, '获取成功', $result); return \Yz::Return(true, '获取成功', []);
}
// 获取指定日期的可用号源(已排序)
private function getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $date, $scope, $userDeptId)
{
$s = $this->GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $date, $scope, $userDeptId);
if (!$s['status']) return [];
$planList = $s['data']['plan_list'] ?? [];
if (count($planList) == 0) return [];
$nowtime = date('Y-m-d H:i:s');
$availablePlans = [];
foreach ($planList as $plan) {
$planEndTime = $plan->date . ' ' . $plan->end_time;
if ($planEndTime > $nowtime && $plan->plan_enable) {
$availablePlans[] = $plan;
}
}
if (count($availablePlans) == 0) return [];
usort($availablePlans, function($a, $b) {
$timeA = $a->date . ' ' . ($a->begin_time ?? '00:00:00');
$timeB = $b->date . ' ' . ($b->begin_time ?? '00:00:00');
return $timeA <=> $timeB;
});
return $availablePlans;
}
// 冲突检查:同一排班资源允许重叠,不同排班时间不能重叠
private function isTimeConflict($assignedPlans, $plan)
{
foreach ($assignedPlans as $assigned) {
if ($assigned['resource_name'] === $plan->department_resources_name) {
continue; // 同排班,允许重叠
}
// 不同排班:检查时间段是否重叠
$aStart = $assigned['begin_time'];
$aEnd = $assigned['end_time'];
$bStart = $plan->begin_time;
$bEnd = $plan->end_time;
// [aStart, aEnd) ∩ [bStart, bEnd) ≠ ∅ → 冲突
if ($aStart < $bEnd && $bStart < $aEnd) {
return true;
}
}
return false;
} }
//开始预约占用名额 //开始预约占用名额

@ -85,7 +85,7 @@
<el-checkbox label="1">预约完成后打印申请单</el-checkbox> <el-checkbox label="1">预约完成后打印申请单</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
<el-button class="do_button" type="primary" style="margin-left: 20px;" @click="optimalTimeClick"></el-button> <el-button class="do_button" type="primary" style="margin-left: 20px;" @click="optimalTimeClick"></el-button>
<el-button class="do_button" type="primary" @click="">单天全检</el-button> <el-button class="do_button" type="primary" @click="fullDayClick"></el-button>
</div> </div>
<div> <div>
<el-date-picker v-model="startDate" type="date" placeholder="跳转日期" @change="DatePickerChange()" /> <el-date-picker v-model="startDate" type="date" placeholder="跳转日期" @change="DatePickerChange()" />
@ -781,6 +781,74 @@
} }
}) })
} }
//
const fullDayClick = () => {
if (selectedEntrustId.value.length === 0) {
ElMessage.error("请选择检查项目")
return
}
planLoading.value = true
//
selectedRows.value.forEach(row => {
const cachedRow = cachedListData.value.find(v => v.id === row.id)
if (!cachedRow) return
const target = entrustTableDate.value.find(v => v.id === row.id)
if (!target) return
target.department_resources = cachedRow.department_resources
target.reservation_date = cachedRow.reservation_date
target.period_begin_time = cachedRow.period_begin_time
target.period_end_time = cachedRow.period_end_time
target.temp_plan_id = undefined
})
//
const items = entrustTableDate.value
.filter(v => v.list_status === 0)
.map(v => ({
id: v.id,
entrust_id: v.entrust_id,
department_resources_name: v.department_resources?.department_resources_name || '',
date: v.reservation_date || '',
begin_time: v.period_begin_time || '',
end_time: v.period_end_time || ''
}))
//
GetOptimalPlan({
type: 'full_day',
regnum: props.reg_num,
entrustid: selectedEntrustId.value,
episodeid: props.episode_id,
appointment_type: props.appointment_type,
items: items,
scope: props.scope,
do_user: props.do_user
}).then(res => {
planLoading.value = false
if (res.status) {
//
res.data.forEach(item => {
const target = entrustTableDate.value.find(v => v.id === item.id)
if (!target) return
if (!target.department_resources) {
target.department_resources = {}
}
target.department_resources.department_resources_name = item.department_resources_name
target.reservation_date = item.date
target.period_begin_time = item.begin_time
target.period_end_time = item.end_time
target.temp_plan_id = item.plan_id
})
//
if (res.data.length > 0) {
selectedPlanId.value = res.data[0].plan_id
}
} else {
ElMessage.error(res.msg)
}
})
}
const checkDaijianFuc = () => { const checkDaijianFuc = () => {
planLoading.value = true planLoading.value = true
CheckIsDaiJian({ CheckIsDaiJian({

Loading…
Cancel
Save