* изменил таблицы в основном отчете
* изменил метод сохранения пациентов основного отчета
This commit is contained in:
118
app/Http/Controllers/Web/DutyReportController.php
Normal file
118
app/Http/Controllers/Web/DutyReportController.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\MedicalHistoryResource;
|
||||
use App\Models\Department;
|
||||
use App\Models\ReportDuty;
|
||||
use App\Models\ReportNurse;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\DutyMedicalHistoryService;
|
||||
use App\Services\DutyReportService;
|
||||
use App\Services\MedicalHistoryService;
|
||||
use App\Services\NurseMedicalHistoryService;
|
||||
use App\Services\NurseReportService;
|
||||
use App\Services\UnifiedMedicalHistoryService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class DutyReportController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected DateRangeService $dateRangeService,
|
||||
protected MedicalHistoryService $medicalHistoryService,
|
||||
protected DutyMedicalHistoryService $dutyMedicalHistoryService,
|
||||
protected DutyReportService $dutyReportService,
|
||||
protected NurseMedicalHistoryService $nurseMedicalHistoryService
|
||||
)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Получение базовой информации для заполнения отчета для дежурного
|
||||
* @param Request $request
|
||||
* @return \Inertia\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$departmentId = $request->query('departmentId', $user->department->department_id);
|
||||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
// Проверяем, есть ли отчет за этот период
|
||||
$isPastPeriod = $this->dateRangeService->isPastPeriod($dateRange);
|
||||
$existsReport = ReportDuty::where('rf_department_id', $departmentId)
|
||||
->where('period_end', '>', $dateRange->startSql())
|
||||
->where('period_end', '<=', $dateRange->endSql())
|
||||
->exists();
|
||||
|
||||
$hasReport = $existsReport && $isPastPeriod;
|
||||
|
||||
if ($hasReport) {
|
||||
$inDepartmentHistories = $this->dutyMedicalHistoryService->getDepartmentHistories($dateRange, $department->rf_mis_department_id);
|
||||
$recipientHistories = $this->dutyMedicalHistoryService->getRecipientHistories($dateRange, $department->rf_mis_department_id);
|
||||
$dischargedHistories = $this->dutyMedicalHistoryService->getDischargedHistories($dateRange, $department->rf_mis_department_id);
|
||||
$deceasedHistories = $this->dutyMedicalHistoryService->getDeceasedHistories($dateRange, $department->rf_mis_department_id);
|
||||
$transferredHistories = $this->dutyMedicalHistoryService->getTransferredHistories($dateRange, $department->rf_mis_department_id);
|
||||
} else if ($this->dateRangeService->isPastPeriod($dateRange)) {
|
||||
$inDepartmentHistories = collect([]);
|
||||
$recipientHistories = collect([]);
|
||||
$dischargedHistories = collect([]);
|
||||
$deceasedHistories = collect([]);
|
||||
$transferredHistories = collect([]);
|
||||
} else {
|
||||
$inDepartmentHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getDepartmentHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$plannedHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getPlannedHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$emergencyHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getEmergencyHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$recipientHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getRecipientHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$dischargedHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getDischargedHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$deceasedHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getDeceasedHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
$transferredHistories = MedicalHistoryResource::collection(
|
||||
$this->medicalHistoryService->getTransferredHistories($dateRange, $department->rf_mis_department_id)
|
||||
);
|
||||
}
|
||||
|
||||
return Inertia::render('Report/Index', [
|
||||
'inDepartmentHistories' => $inDepartmentHistories,
|
||||
'plannedHistories' => $plannedHistories,
|
||||
'emergencyHistories' => $emergencyHistories,
|
||||
'recipientHistories' => $recipientHistories,
|
||||
'dischargedHistories' => $dischargedHistories,
|
||||
'deceasedHistories' => $deceasedHistories,
|
||||
'transferredHistories' => $transferredHistories,
|
||||
'dates' => [
|
||||
$dateRange->startDate->getTimestampMs(),
|
||||
$dateRange->endDate->getTimestampMs(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение отчета от роли мед. сестра
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
$report = $this->dutyReportService->saveReport($dateRange);
|
||||
$this->dutyReportService->saveSnapshot($dateRange, $report);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
22
app/Http/Resources/MedicalHistoryResource.php
Normal file
22
app/Http/Resources/MedicalHistoryResource.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class MedicalHistoryResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
...$this->resource->toArray(),
|
||||
'admitted_today' => $this->latestMigration->admittedInCurrent
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,7 @@ class SnapshotPatientSource
|
||||
}
|
||||
|
||||
return MedicalHistory::query()
|
||||
->whereIn('original_id', $historyIds)
|
||||
->whereIn('id', $historyIds)
|
||||
->with(['operations'])
|
||||
->get()
|
||||
->mapWithKeys(function (MedicalHistory $history) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Services\DateRange;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MigrationPatient extends MaterializedViewModel
|
||||
@@ -11,6 +12,14 @@ class MigrationPatient extends MaterializedViewModel
|
||||
protected $table = 'mv_migrationpatient_details';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $casts = [
|
||||
'birth_date' => 'date:Y-m-d',
|
||||
'recipient_date' => 'datetime:Y-m-d H:i:s',
|
||||
'extract_date' => 'datetime:Y-m-d H:i:s',
|
||||
'death_date' => 'datetime:Y-m-d H:i:s',
|
||||
'male' => 'boolean',
|
||||
];
|
||||
|
||||
public function medicalHistory()
|
||||
{
|
||||
return $this->belongsTo(MedicalHistory::class, 'medical_history_id', 'id');
|
||||
@@ -18,7 +27,8 @@ class MigrationPatient extends MaterializedViewModel
|
||||
|
||||
public function operations()
|
||||
{
|
||||
return $this->hasMany(SurgicalOperation::class, 'migration_patient_id', 'id');
|
||||
return $this->hasMany(SurgicalOperation::class, 'migration_patient_id', 'id')
|
||||
->orderBy('end_date', 'desc');
|
||||
}
|
||||
|
||||
// Пересечение с отчетным периодом
|
||||
@@ -40,24 +50,6 @@ class MigrationPatient extends MaterializedViewModel
|
||||
return $query->whereIn('stationar_branch_id', $branchIds);
|
||||
}
|
||||
|
||||
// Добавляет вычисляемый столбец `category` (только для отображения)
|
||||
public function scopeWithCategory($query, string $from, string $to)
|
||||
{
|
||||
$sql = "CASE
|
||||
WHEN ingoing_date BETWEEN ? AND ? THEN 'admitted'
|
||||
WHEN out_date BETWEEN ? AND ? THEN
|
||||
CASE
|
||||
WHEN death_date IS NOT NULL AND death_date BETWEEN ? AND ? THEN 'deceased'
|
||||
WHEN stat_cure_result_id IN (3,4,7) THEN 'transferred'
|
||||
ELSE 'discharged'
|
||||
END
|
||||
WHEN ingoing_date < ? AND (out_date IS NULL OR out_date > ?) THEN 'current'
|
||||
ELSE 'historical'
|
||||
END as category";
|
||||
|
||||
return $query->selectRaw($sql, [$from, $to, $from, $to, $from, $to, $to, $to]);
|
||||
}
|
||||
|
||||
// Быстрые фильтры по статусам (используют индексы MV, а не computed column)
|
||||
public function scopeAdmitted($query, string $from, string $to)
|
||||
{
|
||||
@@ -89,7 +81,29 @@ class MigrationPatient extends MaterializedViewModel
|
||||
|
||||
public function scopeCurrent($query, DateRange $dateRange)
|
||||
{
|
||||
return $query->where('is_actually_current', true);
|
||||
return $query->whereNull('out_date')
|
||||
->whereNotNull('medical_history_id')
|
||||
->where('ingoing_date', '<', $dateRange->startSql())
|
||||
->wherehas('medicalHistory', function ($query) use ($dateRange) {
|
||||
$query->whereNull('extract_date');
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeCurrentOrAdmitted($query, DateRange $dateRange)
|
||||
{
|
||||
return $query->where(function ($q) use ($dateRange) {
|
||||
// Вариант А: Пациент уже лежит (текущий)
|
||||
$q->whereNull('out_date')
|
||||
->whereNotNull('medical_history_id')
|
||||
->where('ingoing_date', '<', $dateRange->startSql())
|
||||
->wherehas('medicalHistory', function ($query) use ($dateRange) {
|
||||
$query->whereNull('extract_date');
|
||||
}); // опционально: поступил не раньше 2 лет назад
|
||||
})
|
||||
->orWhere(function ($q) use ($dateRange) {
|
||||
$q->where('ingoing_date', '<=', $dateRange->endSql())
|
||||
->where('ingoing_date', '>', $dateRange->startSql());
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeCurrentMigration($query, $historyId, $departmentId)
|
||||
@@ -99,4 +113,23 @@ class MigrationPatient extends MaterializedViewModel
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
}
|
||||
|
||||
public function getAdmittedInCurrentAttribute(): bool
|
||||
{
|
||||
// Получаем дату поступления из последнего движения
|
||||
$ingoing = $this->ingoing_date;
|
||||
|
||||
if (!$ingoing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ingoingLocal = Carbon::parse($ingoing)->setTimezone(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
|
||||
// Окно смены: вчера 09:00 → сегодня 09:00
|
||||
$shiftStart = $now->copy()->subDay()->setTime(9, 0);
|
||||
$shiftEnd = $now->copy()->setTime(9, 0);
|
||||
|
||||
return $ingoingLocal->between($shiftStart, $shiftEnd);
|
||||
}
|
||||
}
|
||||
|
||||
42
app/Models/ReportDuty.php
Normal file
42
app/Models/ReportDuty.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ReportDuty extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'report_date',
|
||||
'sent_at',
|
||||
'period_type',
|
||||
'period_start',
|
||||
'period_end',
|
||||
'status_id',
|
||||
'rf_lpudoctor_id',
|
||||
'rf_department_id',
|
||||
'rf_user_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'report_date' => 'date:Y-m-d',
|
||||
'sent_at' => 'datetime:Y-m-d H:i:s',
|
||||
'period_start' => 'datetime:Y-m-d H:i:s',
|
||||
'period_end' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
public function department()
|
||||
{
|
||||
return $this->belongsTo(Department::class, 'rf_department_id', 'department_id');
|
||||
}
|
||||
|
||||
public function patients()
|
||||
{
|
||||
return $this->hasMany(ReportNursePatient::class, 'report_nurse_id');
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
return $this->belongsTo(ReportStatus::class);
|
||||
}
|
||||
}
|
||||
108
app/Models/ReportDutyMigrationPatient.php
Normal file
108
app/Models/ReportDutyMigrationPatient.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Services\DateRange;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ReportDutyMigrationPatient extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'medical_history_id',
|
||||
'ingoing_date',
|
||||
'out_date',
|
||||
'diagnosis_id',
|
||||
'diagnosis_code',
|
||||
'diagnosis_name',
|
||||
'interrupted_event_id',
|
||||
'stationar_branch_id',
|
||||
'department_id',
|
||||
'visit_result_id',
|
||||
'stat_cure_result_id',
|
||||
'user_id',
|
||||
'mis_user_id',
|
||||
'comment',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ingoing_date' => 'datetime:Y-m-d H:i:s',
|
||||
'out_date' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
public function medicalHistory()
|
||||
{
|
||||
return $this->belongsTo(ReportNursePatient::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function operations()
|
||||
{
|
||||
return $this->hasMany(SurgicalOperation::class, 'migration_patient_id', 'id');
|
||||
}
|
||||
|
||||
// Пересечение с отчетным периодом
|
||||
public function scopeDateRange($query, string $from, string $to)
|
||||
{
|
||||
return $query->where('ingoing_date', '<=', $to)
|
||||
->where(function ($q) use ($from) {
|
||||
$q->whereNull('out_date')->orWhere('out_date', '>=', $from);
|
||||
});
|
||||
}
|
||||
|
||||
// Фильтр по подразделению (получает ID отделений)
|
||||
public function scopeDepartment($query, int $departmentId)
|
||||
{
|
||||
$branchIds = DB::table('stt_stationarbranch')
|
||||
->where('rf_DepartmentID', $departmentId)
|
||||
->pluck('StationarBranchID');
|
||||
|
||||
return $query->whereIn('stationar_branch_id', $branchIds);
|
||||
}
|
||||
|
||||
// Быстрые фильтры по статусам (используют индексы MV, а не computed column)
|
||||
public function scopeAdmitted($query, string $from, string $to)
|
||||
{
|
||||
return $query->where('ingoing_date', '>', $from)
|
||||
->where('ingoing_date', '<=', $to);
|
||||
}
|
||||
|
||||
public function scopeDischarged($query, string $from, string $to)
|
||||
{
|
||||
return $query->where('out_date', '>', $from)
|
||||
->where('out_date', '<=', $to)
|
||||
->whereNotIn('visit_result_id', [3, 4, 5, 6])
|
||||
->whereNull('death_date'); // умершие не считаются "выбывшими домой"
|
||||
}
|
||||
|
||||
public function scopeTransferred($query, string $from, string $to)
|
||||
{
|
||||
return $query->where('out_date', '>', $from)
|
||||
->where('out_date', '<=', $to)
|
||||
->whereIn('visit_result_id', [3, 4]);
|
||||
}
|
||||
|
||||
public function scopeDeceased($query, string $from, string $to)
|
||||
{
|
||||
return $query->whereNotNull('death_date')
|
||||
->where('death_date', '>', $from)
|
||||
->where('death_date', '<=', $to);
|
||||
}
|
||||
|
||||
public function scopeCurrent($query, DateRange $dateRange)
|
||||
{
|
||||
return $query->whereNull('out_date')
|
||||
->whereNotNull('medical_history_id')
|
||||
->where('ingoing_date', '<', $dateRange->startSql())
|
||||
->whereHas('medicalHistory', function ($query) use ($dateRange) {
|
||||
$query->whereNull('extract_date');
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeCurrentMigration($query, $historyId, $departmentId)
|
||||
{
|
||||
return $query->where('medical_history_id', $historyId)
|
||||
->department($departmentId)
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
}
|
||||
}
|
||||
61
app/Models/ReportDutyPatient.php
Normal file
61
app/Models/ReportDutyPatient.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ReportDutyPatient extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'report_duty_id',
|
||||
'source_type',
|
||||
'original_id',
|
||||
'medical_card_number',
|
||||
'full_name',
|
||||
'birth_date',
|
||||
'recipient_date',
|
||||
'extract_date',
|
||||
'death_date',
|
||||
'male',
|
||||
'urgency_id',
|
||||
'hospital_result_id',
|
||||
'visit_result_id',
|
||||
'comment',
|
||||
'user_id'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'birth_date' => 'date:Y-m-d',
|
||||
'recipient_date' => 'datetime:Y-m-d H:i:s',
|
||||
'extract_date' => 'datetime:Y-m-d H:i:s',
|
||||
'death_date' => 'datetime:Y-m-d H:i:s',
|
||||
'male' => 'boolean',
|
||||
];
|
||||
|
||||
public function migrations(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(ReportDutyMigrationPatient::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function operations(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(SurgicalOperation::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function latestMigration()
|
||||
{
|
||||
return $this->hasOne(ReportDutyMigrationPatient::class, 'medical_history_id', 'id')
|
||||
->latest('ingoing_date');
|
||||
}
|
||||
|
||||
public function operationsInDepartment($query, $departmentId)
|
||||
{
|
||||
return $this->operations()->where('department_id', $departmentId);
|
||||
}
|
||||
|
||||
// Скоупы
|
||||
public function scopeUrgency($query, $typeId) // 1 = Экстренно, 2 = Планово
|
||||
{
|
||||
return $query->where('urgency_id', $typeId);
|
||||
}
|
||||
}
|
||||
116
app/Services/DutyMedicalHistoryService.php
Normal file
116
app/Services/DutyMedicalHistoryService.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ReportDutyPatient;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class DutyMedicalHistoryService
|
||||
{
|
||||
public function getHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
$query = ReportDutyPatient::query();
|
||||
|
||||
$query->where('recipient_date', '>=', $dateRange->startSql())
|
||||
->where('recipient_date', '<', $dateRange->endSql())
|
||||
// 1. Оставляем только тех пациентов, у которых БЫЛО движение в этом отделении
|
||||
->whereHas('latestMigration', fn($q) => $q->where('department_id', $departmentId))
|
||||
|
||||
// 2. Загружаем ТОЛЬКО последнее движение в этом отделении (не все миграции)
|
||||
->with(['latestMigration' => fn($q) => $q->where('department_id', $departmentId)]);
|
||||
|
||||
$result = $query->paginate();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getUrgencyHistory(DateRange $dateRange, int $departmentId, int $urgencyId)
|
||||
{
|
||||
$query = ReportDutyPatient::query();
|
||||
|
||||
$query->where('recipient_date', '>=', $dateRange->startSql())
|
||||
->where('recipient_date', '<', $dateRange->endSql())
|
||||
->urgency($urgencyId)
|
||||
->whereHas('migrations', function ($m) use ($departmentId) {
|
||||
$m->where('department_id', $departmentId);
|
||||
})
|
||||
->with([
|
||||
'migrations' => fn ($m) => $m->where('department_id', $departmentId),
|
||||
'migrations.operations'
|
||||
]);
|
||||
|
||||
$result = $query->paginate();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getDepartmentHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return ReportDutyPatient::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->current($dateRange);
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->current($dateRange); // подгружаем только отфильтрованные движения
|
||||
}])
|
||||
->get()
|
||||
// Сортировка по дате поступления в отделение (поле дочерней таблицы)
|
||||
->sortByDesc(fn ($mh) => $mh->latestMigration->ingoing_date ?? $mh->recipient_date)
|
||||
->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить карты поступившие сегодня
|
||||
* @param DateRange $dateRange
|
||||
* @param int $departmentId
|
||||
*/
|
||||
public function getRecipientHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
$now = Carbon::now();
|
||||
|
||||
return ReportDutyPatient::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->admitted($dateRange->startSql(), $dateRange->endSql());
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getDischargedHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return ReportDutyPatient::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->discharged($dateRange->startSql(), $dateRange->endSql());
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getDeceasedHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return ReportDutyPatient::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->deceased($dateRange->startSql(), $dateRange->endSql());
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getTransferredHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return ReportDutyPatient::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->transferred($dateRange->startSql(), $dateRange->endSql());
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
->get();
|
||||
}
|
||||
}
|
||||
249
app/Services/DutyReportService.php
Normal file
249
app/Services/DutyReportService.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\MedicalHistory;
|
||||
use App\Models\ReportDuty;
|
||||
use App\Models\ReportNurse;
|
||||
use App\Models\UnifiedMedicalHistory;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DutyReportService
|
||||
{
|
||||
/**
|
||||
* Базовый запрос для всех отчётов
|
||||
* Фильтрует по отделению и периоду, подгружает связи
|
||||
*/
|
||||
protected function baseQuery(DateRange $dateRange, int $departmentId): Builder
|
||||
{
|
||||
return UnifiedMedicalHistory::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql());
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->dateRange($dateRange->startSql(), $dateRange->endSql());
|
||||
}]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить отчет
|
||||
*/
|
||||
public function saveReport(DateRange $dateRange, ?int $userId = null, ?int $lpuDoctorId = null, ?int $departmentId = null)
|
||||
{
|
||||
$user = $userId ? User::find($userId) : auth()->user();
|
||||
|
||||
$lpuDoctorId = $lpuDoctorId ?? $user->rf_lpudoctor_id;
|
||||
$departmentId = $departmentId ?? $user->rf_department_id;
|
||||
|
||||
$data = [
|
||||
'report_date' => Carbon::now()->format('Y-m-d'),
|
||||
'sent_at' => Carbon::now()->format('Y-m-d H:i:s'),
|
||||
'period_type' => 'day',
|
||||
'period_start' => $dateRange->startSql(),
|
||||
'period_end' => $dateRange->endSql(),
|
||||
'status_id' => 2, // опубликован
|
||||
'rf_lpudoctor_id' => $lpuDoctorId,
|
||||
'rf_department_id' => $departmentId,
|
||||
'rf_user_id' => $user->id,
|
||||
];
|
||||
|
||||
$report = ReportDuty::updateOrCreate(
|
||||
[
|
||||
'report_date' => $data['report_date'], 'period_start' => $data['period_start'],
|
||||
'period_end' => $data['period_end']
|
||||
],
|
||||
$data
|
||||
);
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить снимок пациентов за период
|
||||
*/
|
||||
public function saveSnapshot(DateRange $dateRange, ReportNurse $reportNurse, ?int $departmentId = null, ?int $userId = null): array
|
||||
{
|
||||
$departmentId = $departmentId ?? $reportNurse->department->rf_mis_department_id;
|
||||
$userId = $userId ?? $reportNurse->rf_user_id;
|
||||
$startYear = Carbon::now()->startOfYear()->format('Y-m-d');
|
||||
|
||||
$query = MedicalHistory::query()
|
||||
// Фильтруем движения по отделению + пересечение дат
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange, $startYear) {
|
||||
$q->where('department_id', $departmentId)
|
||||
// пребывание пересекается с отчётным периодом
|
||||
->where('ingoing_date', '<=', $dateRange->endSql())
|
||||
->where('ingoing_date', '>=', $startYear)
|
||||
->where(function ($sub) use ($dateRange) {
|
||||
$sub->whereNull('out_date')
|
||||
->orWhere('out_date', '>=', $dateRange->startSql())
|
||||
->where('out_date', '<=', $dateRange->endSql());
|
||||
});
|
||||
});
|
||||
|
||||
// Получаем данные (chunk для памяти, если пациентов > 1000)
|
||||
$patients = $query->cursor();
|
||||
|
||||
$savedStats = $this->saveReportSnapshot($reportNurse->id, $patients, $userId);
|
||||
|
||||
return [
|
||||
...$savedStats,
|
||||
'report_date' => $dateRange->startSql(),
|
||||
'department_id' => $departmentId,
|
||||
];
|
||||
}
|
||||
|
||||
public function saveReportSnapshot(int $reportDutyId, iterable $patients, int $userId): array
|
||||
{
|
||||
if (empty($patients)) {
|
||||
return ['saved_patients' => 0, 'saved_migrations' => 0];
|
||||
}
|
||||
|
||||
$patientBatch = [];
|
||||
$migrationBatch = [];
|
||||
$batchSize = 100;
|
||||
|
||||
foreach ($patients as $patient) {
|
||||
// Подготовка данных пациента
|
||||
$patientBatch[] = [
|
||||
'report_nurse_id' => $reportDutyId,
|
||||
'source_type' => $patient->source_type,
|
||||
'original_id' => $patient->original_id,
|
||||
'medical_card_number' => $patient->medical_card_number,
|
||||
'full_name' => $patient->full_name,
|
||||
'birth_date' => $patient->birth_date,
|
||||
'recipient_date' => $patient->recipient_date,
|
||||
'extract_date' => $patient->extract_date,
|
||||
'death_date' => $patient->death_date,
|
||||
'male' => $patient->male,
|
||||
'urgency_id' => $patient->urgency_id,
|
||||
'hospital_result_id' => $patient->hospital_result_id,
|
||||
'visit_result_id' => $patient->visit_result_id,
|
||||
'comment' => $patient->comment,
|
||||
'user_id' => $userId,
|
||||
];
|
||||
|
||||
// Подготовка данных миграции (если есть)
|
||||
if (!empty($patient->migrations)) {
|
||||
foreach ($patient->migrations as $migration) {
|
||||
$migrationBatch[] = [
|
||||
// Временный ключ для связи с пациентом (заполним после первого upsert)
|
||||
'_temp_key' => [
|
||||
'report_duty_id' => $reportDutyId,
|
||||
'source_type' => $patient->source_type,
|
||||
'original_id' => $patient->original_id,
|
||||
],
|
||||
'ingoing_date' => $migration->ingoing_date,
|
||||
'out_date' => $migration->out_date,
|
||||
'diagnosis_id' => $migration->diagnosis_id,
|
||||
'diagnosis_code' => $migration->diagnosis_code,
|
||||
'diagnosis_name' => $migration->diagnosis_name,
|
||||
'interrupted_event_id' => $migration->interrupted_event_id,
|
||||
'stationar_branch_id' => $migration->stationar_branch_id,
|
||||
'department_id' => $migration->department_id,
|
||||
'visit_result_id' => $migration->visit_result_id,
|
||||
'stat_cure_result_id' => $migration->stat_cure_result_id,
|
||||
'user_id' => $migration->user_id,
|
||||
'mis_user_id' => $migration->mis_user_id,
|
||||
'comment' => $migration->comment,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Пакетная запись каждые $batchSize записей
|
||||
if (count($patientBatch) >= $batchSize) {
|
||||
[$savedP, $savedM] = $this->upsertBatches($reportDutyId, $patientBatch, $migrationBatch);
|
||||
$patientBatch = [];
|
||||
$migrationBatch = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем остаток
|
||||
[$savedP, $savedM] = $this->upsertBatches($reportDutyId, $patientBatch, $migrationBatch);
|
||||
|
||||
return ['saved_patients' => $savedP, 'saved_migrations' => $savedM];
|
||||
}
|
||||
|
||||
/**
|
||||
* Вспомогательный метод: выполняет upsert для пациентов и миграций
|
||||
*/
|
||||
private function upsertBatches(int $reportDutyId, array $patientBatch, array $migrationBatch): array
|
||||
{
|
||||
if (empty($patientBatch)) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
$savedPatients = 0;
|
||||
$savedMigrations = 0;
|
||||
|
||||
DB::transaction(function () use ($reportDutyId, $patientBatch, $migrationBatch, &$savedPatients, &$savedMigrations) {
|
||||
// UPSERT пациентов
|
||||
$patientUniqueBy = ['report_duty_id', 'source_type', 'original_id'];
|
||||
$patientUpdateColumns = array_diff(array_keys($patientBatch[0]), $patientUniqueBy);
|
||||
|
||||
DB::table('report_nurse_patients')->upsert(
|
||||
$patientBatch,
|
||||
$patientUniqueBy,
|
||||
$patientUpdateColumns
|
||||
);
|
||||
$savedPatients = count($patientBatch);
|
||||
|
||||
// Получаем ID сохранённых пациентов для связи с миграциями
|
||||
if (!empty($migrationBatch)) {
|
||||
// Извлекаем уникальные ключи для поиска
|
||||
$tempKeys = array_map(fn($m) => $m['_temp_key'], $migrationBatch);
|
||||
|
||||
// Получаем реальные ID из БД
|
||||
$patientIds = DB::table('report_duty_patients')
|
||||
->whereIn('report_duty_id', [$reportDutyId])
|
||||
->get()
|
||||
->pluck('id', 'original_id') // key=original_id, value=id
|
||||
->toArray();
|
||||
|
||||
// Формируем финальный массив миграций с реальными medical_history_id
|
||||
$finalMigrations = [];
|
||||
foreach ($migrationBatch as $m) {
|
||||
$tempKey = $m['_temp_key'];
|
||||
$originalId = $tempKey['original_id'];
|
||||
|
||||
if (isset($patientIds[$originalId])) {
|
||||
$finalMigrations[] = [
|
||||
'medical_history_id' => $patientIds[$originalId], // Реальный ID
|
||||
'ingoing_date' => $m['ingoing_date'],
|
||||
'out_date' => $m['out_date'],
|
||||
'diagnosis_id' => $m['diagnosis_id'],
|
||||
'diagnosis_code' => $m['diagnosis_code'],
|
||||
'diagnosis_name' => $m['diagnosis_name'],
|
||||
'interrupted_event_id' => $m['interrupted_event_id'],
|
||||
'stationar_branch_id' => $m['stationar_branch_id'],
|
||||
'department_id' => $m['department_id'],
|
||||
'visit_result_id' => $m['visit_result_id'],
|
||||
'stat_cure_result_id' => $m['stat_cure_result_id'],
|
||||
'user_id' => $m['user_id'],
|
||||
'mis_user_id' => $m['mis_user_id'],
|
||||
'comment' => $m['comment'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($finalMigrations)) {
|
||||
// UPSERT миграций
|
||||
$migrationUniqueBy = ['medical_history_id', 'ingoing_date'];
|
||||
$migrationUpdateColumns = array_diff(array_keys($finalMigrations[0]), $migrationUniqueBy);
|
||||
|
||||
DB::table('report_duty_migration_patients')->upsert(
|
||||
$finalMigrations,
|
||||
$migrationUniqueBy,
|
||||
$migrationUpdateColumns
|
||||
);
|
||||
$savedMigrations = count($finalMigrations);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [$savedPatients, $savedMigrations];
|
||||
}
|
||||
}
|
||||
@@ -49,11 +49,43 @@ class MedicalHistoryService
|
||||
{
|
||||
return MedicalHistory::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->current($dateRange);
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange);
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->current($dateRange); // подгружаем только отфильтрованные движения
|
||||
}])
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange)->latest('ingoing_date'); // подгружаем только отфильтрованные движения
|
||||
}, 'latestMigration.operations'])
|
||||
->get()
|
||||
// Сортировка по дате поступления в отделение (поле дочерней таблицы)
|
||||
->sortByDesc(fn ($mh) => $mh->latestMigration->ingoing_date ?? $mh->recipient_date)
|
||||
->values();
|
||||
}
|
||||
|
||||
public function getPlannedHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return MedicalHistory::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange);
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange)
|
||||
->latest('ingoing_date'); // подгружаем только отфильтрованные движения
|
||||
}, 'latestMigration.operations'])
|
||||
->urgency(1)
|
||||
->get()
|
||||
// Сортировка по дате поступления в отделение (поле дочерней таблицы)
|
||||
->sortByDesc(fn ($mh) => $mh->latestMigration->ingoing_date ?? $mh->recipient_date)
|
||||
->values();
|
||||
}
|
||||
public function getEmergencyHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
return MedicalHistory::query()
|
||||
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange);
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
|
||||
$q->department($departmentId)->currentOrAdmitted($dateRange)->latest('ingoing_date'); // подгружаем только отфильтрованные движения
|
||||
}, 'latestMigration.operations'])
|
||||
->urgency(2)
|
||||
->get()
|
||||
// Сортировка по дате поступления в отделение (поле дочерней таблицы)
|
||||
->sortByDesc(fn ($mh) => $mh->latestMigration->ingoing_date ?? $mh->recipient_date)
|
||||
@@ -75,7 +107,7 @@ class MedicalHistoryService
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
}, 'latestMigration.operations'])
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -87,7 +119,7 @@ class MedicalHistoryService
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
}, 'latestMigration.operations'])
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -99,7 +131,7 @@ class MedicalHistoryService
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
}, 'latestMigration.operations'])
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -111,7 +143,7 @@ class MedicalHistoryService
|
||||
})
|
||||
->with(['latestMigration' => function ($q) use ($departmentId) {
|
||||
$q->department($departmentId);
|
||||
}])
|
||||
}, 'latestMigration.operations'])
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user