validate([ 'target_date' => 'nullable|date', ]); $targetDate = $data['target_date'] ?? null; if ($targetDate) { $targetDate = date('Y-m-d', strtotime($targetDate)); } else { $targetDate = date('Y-m-d', strtotime('+15 days')); } Log::info('号源自动生成开始', ['target_date' => $targetDate]); $dayOfWeek = date('N', strtotime($targetDate)); $allModels = DB::table('plan_model as pm') ->join('plan_model_type as pmt', 'pm.model_type_id', '=', 'pmt.id') ->where('pm.deleted', 0) ->where('pm.status', 1) ->where('pmt.deleted', 0) ->select('pm.*', 'pmt.date_info', 'pmt.holiday_mode', 'pmt.type as model_type') ->get(); $channelQuotaRecords = DB::table('plan_model_channel_quota') ->whereIn('model_id', $allModels->pluck('id')->toArray()) ->where('deleted', 0) ->select('id', 'model_id', 'channel_id', 'total_quota', 'allow_overuse_public_pool') ->get(); $channelQuotaMap = []; foreach ($channelQuotaRecords as $cq) { $channelQuotaMap[$cq->model_id][] = $cq; } $holidayMap = []; $holidayRecords = DB::table('holiday') ->where('deleted', 0) ->where('status', 1) ->where('date', $targetDate) ->get(); foreach ($holidayRecords as $h) { $holidayMap[$h->date] = $h->type; } $existingBatches = DB::table('plan_batch') ->where('plan_date', $targetDate) ->where('deleted', 0) ->select('id', 'resource_id', 'plan_date', 'start_time', 'end_time') ->get() ->groupBy('resource_id'); $existingByResource = []; foreach ($existingBatches as $resourceId => $batches) { $existingByResource[$resourceId] = $batches; } $grouped = $allModels->groupBy('department_id'); $totalBatches = 0; $totalPlans = 0; $skippedCount = 0; $errors = []; foreach ($grouped as $deptId => $models) { try { DB::beginTransaction(); $deptBatches = 0; $deptPlans = 0; foreach ($models as $model) { $allowedDateRanges = $this->resolveDateRanges($model); $holidayMode = $model->holiday_mode ?? 0; $currentDate = new \DateTime($targetDate); $shouldGenerate = $this->shouldGenerateForDate($currentDate, $model, $allowedDateRanges, $holidayMode, $holidayMap); if (!$shouldGenerate) { continue; } $resourceId = $model->resource_id; $existing = $existingByResource[$resourceId] ?? collect(); $newStartTime = strtotime('2000-01-01 ' . $model->start_time); $newEndTime = strtotime('2000-01-01 ' . $model->end_time); $shouldSkip = false; foreach ($existing as $plan) { $existingStartTime = strtotime('2000-01-01 ' . $plan->start_time); $existingEndTime = strtotime('2000-01-01 ' . $plan->end_time); if (!($newEndTime <= $existingStartTime || $newStartTime >= $existingEndTime)) { $shouldSkip = true; break; } } if ($shouldSkip) { $skippedCount++; continue; } $channelQuotas = collect($channelQuotaMap[$model->id] ?? []); $hasChannelQuota = $channelQuotas->where('channel_id', '>', 0)->isNotEmpty(); $batchId = DB::table('plan_batch')->insertGetId([ 'model_id' => $model->id, 'slot_mode' => $model->slot_mode, 'plan_date' => $targetDate, 'weekname' => $dayOfWeek, 'department_id' => $model->department_id, 'resource_id' => $model->resource_id, 'patient_type_mask' => $model->patient_type_mask, 'total_quota' => $model->total_quota, 'used_quota' => 0, 'start_time' => $model->start_time, 'end_time' => $model->end_time, 'cutoff_time' => $model->cutoff_time, 'status' => $model->status, 'enable_channel_quota' => $hasChannelQuota ? 1 : 0, 'adduser' => 0, 'deleted' => 0, 'created_at' => now(), 'updated_at' => now(), ]); $deptBatches++; if ($model->slot_mode == 1) { DB::table('plan')->insert([ 'batch_id' => $batchId, 'start_time' => $model->start_time, 'end_time' => $model->end_time, 'total_quota' => $model->total_quota, 'used_quota' => 0, 'status' => $model->status, 'created_at' => now(), 'updated_at' => now(), ]); $deptPlans++; } else { $modelStartTime = strtotime('2000-01-01 ' . $model->start_time); $modelEndTime = strtotime('2000-01-01 ' . $model->end_time); $totalSeconds = $modelEndTime - $modelStartTime; $intervalSeconds = (int)round($totalSeconds / $model->total_quota); for ($i = 0; $i < $model->total_quota; $i++) { $pointStartTime = $modelStartTime + ($i * $intervalSeconds); $pointEndTime = $i == $model->total_quota - 1 ? $modelEndTime : $modelStartTime + (($i + 1) * $intervalSeconds); DB::table('plan')->insert([ 'batch_id' => $batchId, 'start_time' => date('H:i:s', $pointStartTime), 'end_time' => date('H:i:s', $pointEndTime), 'total_quota' => 1, 'used_quota' => 0, 'status' => $model->status, 'created_at' => now(), 'updated_at' => now(), ]); $deptPlans++; } } foreach ($channelQuotas as $quota) { DB::table('plan_batch_channel_quota')->insert([ 'batch_id' => $batchId, 'channel_id' => $quota->channel_id, 'total_quota' => $quota->total_quota, 'used_quota' => 0, 'created_at' => now(), 'updated_at' => now(), ]); } } DB::commit(); $totalBatches += $deptBatches; $totalPlans += $deptPlans; } catch (\Exception $e) { DB::rollBack(); $deptName = DB::table('department')->where('id', $deptId)->value('name'); $errors[] = [ 'department_id' => $deptId, 'department_name' => $deptName ?? '', 'error' => $e->getMessage(), ]; Log::error('号源自动生成科室失败', [ 'department_id' => $deptId, 'error' => $e->getMessage(), ]); } } $result = [ 'target_date' => $targetDate, 'day_of_week' => $dayOfWeek, 'total_batches' => $totalBatches, 'total_plans' => $totalPlans, 'skipped' => $skippedCount, 'errors' => $errors, ]; Log::info('号源自动生成完成', $result); $message = "目标日期 {$targetDate},生成 {$totalBatches} 个批次、{$totalPlans} 个号源"; if ($skippedCount > 0) { $message .= ",跳过 {$skippedCount} 个已存在"; } return Rs::success($result, $message); } private function resolveDateRanges($model): array { $allowedDateRanges = []; if (!empty($model->date_info)) { $dateInfo = json_decode($model->date_info, true); if ($model->model_type == 1) { if (is_array($dateInfo) && count($dateInfo) > 0) { if (is_array($dateInfo[0]) && count($dateInfo[0]) == 2) { $allowedDateRanges = $dateInfo; } else { $allowedDateRanges = [$dateInfo]; } } } else { if (is_array($dateInfo) && count($dateInfo) > 0) { foreach ($dateInfo as $date) { $allowedDateRanges[] = [$date, $date]; } } } } return $allowedDateRanges; } private function shouldGenerateForDate(\DateTime $currentDate, $model, array $allowedDateRanges, int $holidayMode, array $holidayMap): bool { $planDate = $currentDate->format('Y-m-d'); $planMonthDay = $currentDate->format('m-d'); $dayOfWeek = $currentDate->format('N'); $dateAllowed = false; if (empty($allowedDateRanges)) { $dateAllowed = true; } else { foreach ($allowedDateRanges as $range) { if ($planMonthDay >= $range[0] && $planMonthDay <= $range[1]) { $dateAllowed = true; break; } } } $isHoliday = isset($holidayMap[$planDate]) && $holidayMap[$planDate] == 1; $isMakeupWork = isset($holidayMap[$planDate]) && $holidayMap[$planDate] == 2; $weeknameMatch = in_array($dayOfWeek, explode(',', $model->weekname)); if ($holidayMode == 1) { return $dateAllowed && $weeknameMatch; } elseif ($holidayMode == 2) { return $dateAllowed && $isHoliday; } else { if ($isHoliday) { return false; } elseif ($isMakeupWork) { return $dateAllowed; } else { return $dateAllowed && $weeknameMatch; } } } }