You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\API\Internal;
use App\Http\Controllers\Controller;
use App\Lib\Rs;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class PlanAutoGenerateController extends Controller
{
public function generate(Request $request)
{
$data = $request->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;
}
}
}
}