号源排序

main
鹿和sa0ChunLuyu 2 weeks ago
parent 3de23f3da3
commit 4286272d5d

@ -402,135 +402,190 @@ public function BatchGenerateByDateRange(Request $request, RosterService $roster
{ {
$dateRange = request('dateRange'); $dateRange = request('dateRange');
$HolidayEnable = request('HolidayEnable', 1); $HolidayEnable = request('HolidayEnable', 1);
$autoMode = request('autoMode', 1);
$userid = 0; $userid = 0;
try { try {
// 1. 查询所有资源,按 time_mode 分组 if ($autoMode == 1) {
$resources = DB::table('s_department_resources') // 自动模式:按顶级科室的 auto_gen_days 生成
->where(['is_del' => 0]) $depts = DB::table('s_department')
->get() ->where('pid', 0)
->groupBy('time_mode'); ->where('is_del', 0)
->where('appointment_enabled', 1)
$results = []; ->where('auto_gen_days', '>', 0)
$totalCount = 0; ->get();
// 2. 遍历不同 time_mode 的资源 $allResults = [];
foreach ($resources as $timeMode => $resourceGroup) { $allTotal = 0;
$resourceIds = $resourceGroup->pluck('id')->toArray();
foreach ($depts as $dept) {
// 3. 根据 time_mode 查询对应的模板 $today = date('Y-m-d');
$templatesQuery = DB::table('s_source_roster') $endDate = date('Y-m-d', strtotime("+{$dept->auto_gen_days} days"));
->leftJoin('s_department_resources', 's_source_roster.resources_id', '=', 's_department_resources.id') $deptDateRange = [$today, $endDate];
->select(
's_source_roster.*', $resources = DB::table('s_department_resources')
's_department_resources.department_resources_name', ->where('department_id', $dept->id)
's_department_resources.time_mode', ->where('is_del', 0)
's_department_resources.time_range' ->get()
) ->groupBy('time_mode');
->whereIn('s_source_roster.resources_id', $resourceIds)
->where(['s_source_roster.status' => 1, 's_source_roster.is_del' => 0]); if ($resources->isEmpty()) {
continue;
if ($timeMode == 1) { }
// 时令资源: 只查询 type=1 或 2 的模板
$templatesQuery->whereIn('s_source_roster.type', [1, 2]);
} else {
// 普通资源: 只查询 type=0 的模板
$templatesQuery->where('s_source_roster.type', 0);
}
$allModels = $templatesQuery->get(); $res = $this->generateBatchForResources(
$resources, $deptDateRange, $HolidayEnable, $userid, $rosterService
);
if ($allModels->isEmpty()) { $allResults = array_merge($allResults, $res['details']);
continue; // 该 time_mode 下没有模板,跳过 $allTotal += $res['total_count'];
} }
// 4. 时令模板根据资源的 time_range 进行匹配 $successCount = collect($allResults)->where('status', 'success')->count();
if ($timeMode == 1) { $failedCount = collect($allResults)->where('status', 'failed')->count();
$validTemplates = collect();
$startDate = new DateTime($dateRange[0]); return \Yz::Return(
$endDate = new DateTime($dateRange[1]); true,
$currentDate = clone $startDate; "自动生成完成,共 {$allTotal} 条 (成功: {$successCount} 资源, 失败: {$failedCount} 资源)",
[
while ($currentDate <= $endDate) { 'total_count' => $allTotal,
foreach ($allModels as $model) { 'success_count' => $successCount,
$resource = $resourceGroup->where('id', $model->resources_id)->first(); 'failed_count' => $failedCount,
'details' => $allResults
// 检查日期是否在时令范围内 ]
if ($this->isDateInSeasonalRange($currentDate, $model->type, $resource)) { );
if (!$validTemplates->contains('id', $model->id)) { } else {
$validTemplates->push($model); // 手动模式:使用入参 dateRange走原有逻辑
} $resources = DB::table('s_department_resources')
->where(['is_del' => 0])
->get()
->groupBy('time_mode');
$res = $this->generateBatchForResources(
$resources, $dateRange, $HolidayEnable, $userid, $rosterService
);
return \Yz::Return(
true,
"批量生成完成,共 {$res['total_count']} 条 (成功: {$res['success_count']} 资源, 失败: {$res['failed_count']} 资源)",
$res
);
}
} catch (\Exception $e) {
return \Yz::echoError1($e->getMessage());
}
}
/**
* 批量生成号源(核心逻辑,按资源分组遍历)
*/
private function generateBatchForResources($resources, $dateRange, $HolidayEnable, $userid, RosterService $rosterService)
{
$results = [];
$totalCount = 0;
foreach ($resources as $timeMode => $resourceGroup) {
$resourceIds = $resourceGroup->pluck('id')->toArray();
$templatesQuery = DB::table('s_source_roster')
->leftJoin('s_department_resources', 's_source_roster.resources_id', '=', 's_department_resources.id')
->select(
's_source_roster.*',
's_department_resources.department_resources_name',
's_department_resources.time_mode',
's_department_resources.time_range'
)
->whereIn('s_source_roster.resources_id', $resourceIds)
->where(['s_source_roster.status' => 1, 's_source_roster.is_del' => 0]);
if ($timeMode == 1) {
$templatesQuery->whereIn('s_source_roster.type', [1, 2]);
} else {
$templatesQuery->where('s_source_roster.type', 0);
}
$allModels = $templatesQuery->get();
if ($allModels->isEmpty()) {
continue;
}
if ($timeMode == 1) {
$validTemplates = collect();
$startDate = new DateTime($dateRange[0]);
$endDate = new DateTime($dateRange[1]);
$currentDate = clone $startDate;
while ($currentDate <= $endDate) {
foreach ($allModels as $model) {
$resource = $resourceGroup->where('id', $model->resources_id)->first();
if ($this->isDateInSeasonalRange($currentDate, $model->type, $resource)) {
if (!$validTemplates->contains('id', $model->id)) {
$validTemplates->push($model);
} }
} }
$currentDate->modify('+1 day');
} }
$currentDate->modify('+1 day');
}
$allModels = $validTemplates; $allModels = $validTemplates;
if ($allModels->isEmpty()) { if ($allModels->isEmpty()) {
continue; // 没有匹配的时令模板,跳过 continue;
}
} }
}
// 5. 按资源分组,按 date_type 细分,循环调用 generatePlans $groupedByResource = $allModels->groupBy('resources_id');
$groupedByResource = $allModels->groupBy('resources_id');
foreach ($groupedByResource as $resourcesId => $models) {
foreach ($groupedByResource as $resourcesId => $models) { $groupedByDateType = $models->groupBy('date_type');
$groupedByDateType = $models->groupBy('date_type'); $resourceCount = 0;
$resourceCount = 0;
try {
try { foreach ($groupedByDateType as $dateType => $typeModels) {
foreach ($groupedByDateType as $dateType => $typeModels) { $typeModelIds = $typeModels->pluck('id')->toArray();
$typeModelIds = $typeModels->pluck('id')->toArray();
$result = $rosterService->generatePlans(
$dateRange,
$typeModelIds,
$userid,
$dateType,
$HolidayEnable
);
$resourceCount += $result['count'];
}
$results[] = [ $result = $rosterService->generatePlans(
'resources_id' => $resourcesId, $dateRange,
'resources_name' => $models->first()->department_resources_name ?? '', $typeModelIds,
'status' => 'success', $userid,
'count' => $resourceCount $dateType,
]; $HolidayEnable
$totalCount += $resourceCount; );
$resourceCount += $result['count'];
} catch (\Exception $e) {
$results[] = [
'resources_id' => $resourcesId,
'resources_name' => $models->first()->department_resources_name ?? '',
'status' => 'failed',
'error' => $e->getMessage(),
'count' => 0
];
} }
$results[] = [
'resources_id' => $resourcesId,
'resources_name' => $models->first()->department_resources_name ?? '',
'status' => 'success',
'count' => $resourceCount
];
$totalCount += $resourceCount;
} catch (\Exception $e) {
$results[] = [
'resources_id' => $resourcesId,
'resources_name' => $models->first()->department_resources_name ?? '',
'status' => 'failed',
'error' => $e->getMessage(),
'count' => 0
];
} }
} }
}
// 6. 统计结果 $successCount = collect($results)->where('status', 'success')->count();
$successCount = collect($results)->where('status', 'success')->count(); $failedCount = collect($results)->where('status', 'failed')->count();
$failedCount = collect($results)->where('status', 'failed')->count();
return \Yz::Return(
true,
"批量生成完成,共 {$totalCount} 条 (成功: {$successCount} 资源, 失败: {$failedCount} 资源)",
[
'total_count' => $totalCount,
'success_count' => $successCount,
'failed_count' => $failedCount,
'details' => $results
]
);
} catch (\Exception $e) { return [
return \Yz::echoError1($e->getMessage()); 'total_count' => $totalCount,
} 'success_count' => $successCount,
'failed_count' => $failedCount,
'details' => $results
];
} }
/** /**

@ -257,6 +257,101 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$pp = array_merge($pl1, $pl2); $pp = array_merge($pl1, $pl2);
// 诊室排序:按最早可用时间升序 → 最早时段剩余号源降序 → 随机
$grouped = [];
$nowTimestamp = time();
foreach ($pp as $plan) {
$name = $plan->department_resources_name;
if (!isset($grouped[$name])) {
$grouped[$name] = ['plans' => [], 'remaining' => 0, 'earliest_remaining' => 0];
}
$grouped[$name]['plans'][] = $plan;
// 累计全天总剩余
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0);
$grouped[$name]['remaining'] += max($remaining, 0);
// 判断是否未过期,收集最早开始时间及该时段剩余
$endDateTime = $plan->date . ' ' . ($plan->end_time ?? '23:59:59');
if (strtotime($endDateTime) > $nowTimestamp) {
$startDateTime = $plan->date . ' ' . ($plan->begin_time ?? '00:00:00');
$ts = strtotime($startDateTime);
if (!isset($grouped[$name]['earliest']) || $ts < $grouped[$name]['earliest']) {
$grouped[$name]['earliest'] = $ts;
$grouped[$name]['earliest_remaining'] = max($remaining, 0);
} elseif ($ts == $grouped[$name]['earliest']) {
// 同一 earliest 时段,累加该时段剩余
$grouped[$name]['earliest_remaining'] += max($remaining, 0);
}
}
}
// 诊室排序:先转为索引数组,再手动排序
$grouped = array_values($grouped);
if (count($grouped) >= 2) {
// 按规则排序:有可用号源优先 → 最早时间升序 → 剩余号源降序 → 随机
$hasEarliest = [];
foreach ($grouped as $idx => $g) {
$hasEarliest[$idx] = isset($g['earliest']);
}
// 简单冒泡排序(只有 2 个诊室时足够)
for ($i = 0; $i < count($grouped) - 1; $i++) {
for ($j = $i + 1; $j < count($grouped); $j++) {
$a = $grouped[$i];
$b = $grouped[$j];
$aHas = isset($a['earliest']);
$bHas = isset($b['earliest']);
$swap = false;
if ($aHas != $bHas) {
$swap = $bHas; // 有可用号源的排前面
} elseif ($aHas) {
if ($a['earliest'] != $b['earliest']) {
$swap = ($a['earliest'] > $b['earliest']); // 最早时间升序
} elseif ($a['earliest_remaining'] != $b['earliest_remaining']) {
$swap = ($a['earliest_remaining'] < $b['earliest_remaining']); // 最早时段剩余号源降序
} else {
$swap = (rand(0, 1) == 1); // 随机
}
} else {
$swap = (rand(0, 1) == 1); // 都无可用号源,随机
}
if ($swap) {
$temp = $grouped[$i];
$grouped[$i] = $grouped[$j];
$grouped[$j] = $temp;
}
}
}
}
// 按诊室排序后,同诊室 plan 按时间排序,最后打散
$pp = [];
$debugGroupedOrder = [];
foreach ($grouped as $g) {
$plans = $g['plans'];
$debugGroupedOrder[] = [
'name' => $plans[0]->department_resources_name ?? '',
'earliest' => isset($g['earliest']) ? date('Y-m-d H:i:s', $g['earliest']) : null,
'remaining' => $g['remaining'],
];
usort($plans, function($a, $b) {
$timeA = $a->date . ' ' . ($a->begin_time ?? '00:00:00');
$timeB = $b->date . ' ' . ($b->begin_time ?? '00:00:00');
if ($timeA !== $timeB) {
return $timeA <=> $timeB;
}
$availableA = ($a->count ?? 0) - ($a->used_count ?? 0) - ($a->locked_count ?? 0);
$availableB = ($b->count ?? 0) - ($b->used_count ?? 0) - ($b->locked_count ?? 0);
return ($availableB <=> $availableA);
});
foreach ($plans as $plan) {
$pp[] = $plan;
}
}
return \Yz::Return(true, '查询完成', [ return \Yz::Return(true, '查询完成', [
'today_date' => date("Y-m-d"), 'today_date' => date("Y-m-d"),
@ -272,6 +367,7 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
'debug_allRids' => $debugAllRids ?? [], 'debug_allRids' => $debugAllRids ?? [],
'debug_skipped' => $debugSkipped ?? [], 'debug_skipped' => $debugSkipped ?? [],
'debug_passedRids' => array_values(array_unique($debugPassed ?? [])), 'debug_passedRids' => array_values(array_unique($debugPassed ?? [])),
'debug_grouped_order' => $debugGroupedOrder ?? [],
]); ]);
} }

Loading…
Cancel
Save