Files
onboard/app/Console/Commands/GenerateDutyReport.php
brusnitsyn 739168d427 Обновлен стартовый экран
Переписаны запросы для статистики, отчетов
Добавлена интеграция отчета сестры
2026-05-28 22:10:00 +09:00

192 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Console\Commands;
use App\Models\Department;
use App\Models\ReportDuty;
use App\Models\User;
use App\Services\DateRange;
use App\Services\DateRangeService;
use App\Services\DutyReportService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Command\Command as CommandAlias;
use Throwable;
class GenerateDutyReport extends Command
{
protected $signature = 'duty:generate
{--start= : Начальная дата смены (YYYY-MM-DD). По умолчанию сегодня}
{--end= : Конечная дата смены (YYYY-MM-DD). По умолчанию равно --start}
{--departments= : ID отделений через запятую или "all"}
{--user= : ID пользователя для аудита (обязательно для CLI)}
{--shift-start=09:00 : Время начала смены (HH:MM)}
{--timezone= : Часовой пояс смены (по умолчанию config(\'app.timezone\'))}
{--dry-run : Тестовый режим без записи в БД}
{--skip-existing : Пропускать смены, где отчёт уже существует}';
protected $description = 'Пакетная генерация суточных отчётов за период по сменам (09:0009:00)';
public function __construct(
protected DutyReportService $reportService,
protected DateRangeService $dateRangeService
) {
parent::__construct();
}
public function handle()
{
$tz = $this->option('timezone') ?: config('app.timezone', 'Europe/Moscow');
$shiftStartTime = $this->option('shift-start') ?: '09:00';
// 1. Валидация и парсинг дат
$startDate = Carbon::parse($this->option('start') ?: now($tz)->format('Y-m-d'), $tz)->setTimeFromTimeString($shiftStartTime);
$endDate = Carbon::parse($this->option('end') ?: $this->option('start') ?: now($tz)->format('Y-m-d'), $tz)->setTimeFromTimeString($shiftStartTime);
if ($endDate->lt($startDate)) {
$this->error('Конечная дата не может быть раньше начальной.');
return CommandAlias::FAILURE;
}
// 2. Разрешение списка отделений
$departments = $this->resolveDepartments($this->option('departments'));
if ($departments->isEmpty()) {
$this->error('Отделения не найдены.');
return CommandAlias::FAILURE;
}
// 3. Генерация массива смен
$shifts = [];
$current = $startDate->copy();
while ($current->lte($endDate)) {
$shifts[] = [
'start' => $current->copy(),
'end' => $current->copy()->addDay(), // 09:00 → 09:00 следующего дня
];
$current->addDay();
}
// 4. Пользователь CLI
$userId = $this->option('user') ? (int) $this->option('user') : null;
if (!$userId && !$this->option('dry-run')) {
$this->error('Для записи в БД в CLI режиме укажите параметр --user=<ID>');
return CommandAlias::FAILURE;
}
// 5. Вывод информации
$totalTasks = count($shifts) * $departments->count();
if ($totalTasks === 0) {
$this->warn('Нет задач для выполнения.');
return CommandAlias::SUCCESS;
}
$this->info("Период: {$shifts[0]['start']->format('Y-m-d H:i')}{$shifts[array_key_last($shifts)]['end']->format('Y-m-d H:i')} ({$tz})");
$this->info("Отделений: {$departments->count()}");
$this->info("Всего смен: {$totalTasks}");
if ($this->option('dry-run')) $this->warn("Режим DRY RUN");
if ($this->option('skip-existing')) $this->warn("Пропуск существующих отчётов включён");
$progressBar = $this->output->createProgressBar($totalTasks);
$progressBar->start();
$success = 0; $skipped = 0; $errors = 0;
// 6. Основной цикл
foreach ($departments as $dept) {
foreach ($shifts as $shift) {
try {
$status = $this->processShift(
$shift['start'], $shift['end'], $dept, $userId,
$this->reportService, $this->dateRangeService
);
if ($status === 'skip' || $status === 'dry_run') {
$skipped++;
} else {
$success++;
}
} catch (Throwable $e) {
$errors++;
// Безопасное получение имени отделения
$deptName = $dept->name ?? $dept->department_name ?? "Отдел #{$dept->department_id}";
$this->error("\n[{$deptName}] {$shift['start']->format('Y-m-d H:i')}: {$e->getMessage()}");
Log::error('DutyReportShiftGeneration', [
'department_id' => $dept->department_id,
'shift_start' => $shift['start']->format('Y-m-d H:i:s'),
'shift_end' => $shift['end']->format('Y-m-d H:i:s'),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
} finally {
// ✅ ГАРАНТИРУЕМ ровно 1 шаг прогресса на каждую итерацию
$progressBar->advance();
}
}
}
$progressBar->finish();
$this->newLine(2);
$this->info("Завершено. Успешно: {$success} | Пропущено: {$skipped} | Ошибок: {$errors}");
return $errors > 0 ? CommandAlias::FAILURE : CommandAlias::SUCCESS;
}
/**
* Получение списка отделений
*/
private function resolveDepartments($input)
{
if (!$input || strtolower($input) === 'all') {
return Department::orderBy('department_id')->get();
}
$ids = array_map('trim', explode(',', $input));
return Department::whereIn('department_id', $ids)->get();
}
/**
* Обработка одной смены
* @return string 'success' | 'skip' | 'dry_run'
*/
private function processShift(
Carbon $shiftStart,
Carbon $shiftEnd,
$dept,
int $userId,
DutyReportService $reportService,
DateRangeService $dateRangeService
): string {
$deptId = $dept->department_id;
$misDeptId = $dept->rf_mis_department_id;
// Пропуск, если отчёт уже существует за ЭТУ ЖЕ СМЕНУ
if ($this->option('skip-existing')) {
$exists = ReportDuty::where('rf_department_id', $deptId)
->where('period_start', $shiftStart->format('Y-m-d H:i:s'))
->where('period_end', $shiftEnd->format('Y-m-d H:i:s'))
->exists();
if ($exists) {
return 'skip';
}
}
// Тестовый режим
if ($this->option('dry-run')) {
return 'dry_run';
}
// Формируем DateRange через ваш сервис (учёт смен, часовых поясов)
$user = User::find($userId);
$lpuDoctorId = $user->rf_lpudoctor_id ?? 1;
$dateRange = $dateRangeService->createDateRangeForDate($shiftEnd, $user);
// Цепочка из вашего контроллера
$report = $reportService->saveReport($dateRange, $userId, $lpuDoctorId, $deptId);
$stats = $reportService->saveSnapshot($dateRange, $report, $misDeptId, $userId);
$reportService->saveMetrics($stats, $report);
return 'success';
}
}