Работа над журналом для ст. мед сестер
This commit is contained in:
@@ -17,10 +17,28 @@ use Illuminate\Support\Facades\Log;
|
||||
class AutoReportService
|
||||
{
|
||||
public function __construct(
|
||||
protected ReportService $reportService,
|
||||
protected DateRangeService $dateRangeService,
|
||||
protected ReportSavePathService $reportSavePathService,
|
||||
) {}
|
||||
mixed $dateRangeService = null,
|
||||
mixed $reportSavePathService = null,
|
||||
?ReportSavePathService $legacyReportSavePathService = null,
|
||||
) {
|
||||
if ($dateRangeService instanceof ReportService) {
|
||||
$this->dateRangeService = $reportSavePathService instanceof DateRangeService
|
||||
? $reportSavePathService
|
||||
: app(DateRangeService::class);
|
||||
$this->reportSavePathService = $legacyReportSavePathService ?? app(ReportSavePathService::class);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dateRangeService = $dateRangeService instanceof DateRangeService
|
||||
? $dateRangeService
|
||||
: app(DateRangeService::class);
|
||||
$this->reportSavePathService = $reportSavePathService ?? app(ReportSavePathService::class);
|
||||
}
|
||||
|
||||
protected DateRangeService $dateRangeService;
|
||||
|
||||
protected ReportSavePathService $reportSavePathService;
|
||||
|
||||
/**
|
||||
* Заполнить отчеты для пользователя за период
|
||||
@@ -34,16 +52,11 @@ class AutoReportService
|
||||
): int {
|
||||
$createdCount = 0;
|
||||
|
||||
// Для многодневного диапазона расширяем конец на 1 день,
|
||||
// чтобы покрыть последние сутки (07:00 -> 07:00) целиком.
|
||||
$start = \Carbon\Carbon::createFromFormat('Y-m-d', $startDate, 'Asia/Yakutsk');
|
||||
$end = \Carbon\Carbon::createFromFormat('Y-m-d', $endDate, 'Asia/Yakutsk');
|
||||
$periodEnd = $start->equalTo($end)
|
||||
? $end->copy()
|
||||
: $end->copy()->addDay();
|
||||
|
||||
// Создаем период по дням
|
||||
$period = CarbonPeriod::create($start->toDateString(), $periodEnd->toDateString());
|
||||
$period = CarbonPeriod::create($start->toDateString(), $end->toDateString());
|
||||
|
||||
foreach ($period as $date) {
|
||||
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $date, $date);
|
||||
|
||||
@@ -65,12 +65,12 @@ readonly class DateRange
|
||||
|
||||
public function startFirstOfMonth()
|
||||
{
|
||||
return $this->startDate->copy()->firstOfMonth()->setHour(6)->format('Y-m-d H:i:s');
|
||||
return $this->startDate->copy()->firstOfMonth()->setHour(9)->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
public function endFirstOfMonth()
|
||||
{
|
||||
return $this->endDate->copy()->firstOfMonth()->setHour(6)->format('Y-m-d H:i:s');
|
||||
return $this->endDate->copy()->firstOfMonth()->setHour(9)->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
117
app/Services/MedicalHistoryService.php
Normal file
117
app/Services/MedicalHistoryService.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\MedicalHistory;
|
||||
use App\Models\MigrationPatient;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class MedicalHistoryService
|
||||
{
|
||||
public function getHistories(DateRange $dateRange, int $departmentId)
|
||||
{
|
||||
$query = MedicalHistory::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 = MedicalHistory::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 MedicalHistory::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 MedicalHistory::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 MedicalHistory::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 MedicalHistory::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 MedicalHistory::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();
|
||||
}
|
||||
}
|
||||
@@ -74,16 +74,21 @@ class PreoperativeDaysCalculator extends BaseMetricService implements MetricCalc
|
||||
}
|
||||
|
||||
$op = $operations[$historyId];
|
||||
$days = Carbon::parse($op->first_admission)
|
||||
->diffInDays(Carbon::parse($op->first_operation));
|
||||
$admittedAt = Carbon::parse($op->first_admission);
|
||||
$operationAt = Carbon::parse($op->first_operation);
|
||||
|
||||
if ($days >= 0) {
|
||||
if (! isset($results[$deptId])) {
|
||||
$results[$deptId] = ['total' => 0, 'count' => 0];
|
||||
}
|
||||
$results[$deptId]['total'] += $days;
|
||||
$results[$deptId]['count']++;
|
||||
if ($operationAt->lt($admittedAt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$days = $admittedAt->copy()->startOfDay()
|
||||
->diffInDays($operationAt->copy()->startOfDay());
|
||||
|
||||
if (! isset($results[$deptId])) {
|
||||
$results[$deptId] = ['total' => 0, 'count' => 0];
|
||||
}
|
||||
$results[$deptId]['total'] += $days;
|
||||
$results[$deptId]['count']++;
|
||||
}
|
||||
|
||||
// Усредняем по отделениям
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ use App\Models\MedicalHistorySnapshot;
|
||||
use App\Models\MisStationarBranch;
|
||||
use App\Models\Report;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SnapshotService
|
||||
@@ -280,8 +279,8 @@ class SnapshotService
|
||||
private function parseDates(array $dates): array
|
||||
{
|
||||
return [
|
||||
Carbon::createFromTimestampMs($dates[0])->setTimezone('Asia/Yakutsk'),
|
||||
Carbon::createFromTimestampMs($dates[1])->setTimezone('Asia/Yakutsk'),
|
||||
$this->dateRangeService->parseDate($dates[0]),
|
||||
$this->dateRangeService->parseDate($dates[1]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,11 @@ class StatisticsService
|
||||
$bedDaysSum = 0;
|
||||
$avgBedDays = 0;
|
||||
$preoperativeSum = 0;
|
||||
$preoperativePatientCount = 0;
|
||||
$preoperativeTotalRecords = 0;
|
||||
$preoperativePatientRecords = 0;
|
||||
$preoperativeAverageSum = 0;
|
||||
$preoperativeAverageCount = 0;
|
||||
|
||||
if (isset($metrics[$deptId])) {
|
||||
foreach ($metrics[$deptId] as $item) {
|
||||
@@ -153,10 +158,24 @@ class StatisticsService
|
||||
16 => $unwanted = (int) $value,
|
||||
25 => $bedDaysSum += $value,
|
||||
19 => $lethalitySum = $value,
|
||||
21 => $preoperativeAverageSum += $value,
|
||||
26 => $preoperativeSum += $value,
|
||||
27 => $preoperativePatientCount += (int) $value,
|
||||
// 24 => $completePlanProgress = (int)$value,
|
||||
default => null
|
||||
};
|
||||
|
||||
if ((int) $item->rf_metrika_item_id === 21) {
|
||||
$preoperativeAverageCount += (int) $item->records_count;
|
||||
}
|
||||
|
||||
if ((int) $item->rf_metrika_item_id === 26) {
|
||||
$preoperativeTotalRecords += (int) $item->records_count;
|
||||
}
|
||||
|
||||
if ((int) $item->rf_metrika_item_id === 27) {
|
||||
$preoperativePatientRecords += (int) $item->records_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,10 +189,15 @@ class StatisticsService
|
||||
$percentLoaded = $bedsCount > 0 ? round($currentCount * 100 / $bedsCount) : 0;
|
||||
|
||||
// Средний койко-день
|
||||
$avgBedDays = $outcome > 0 ? round($bedDaysSum / $outcome, 2) : 0;
|
||||
$avgBedDays = $outcome > 0 ? round($bedDaysSum / $outcome, 1) : 0;
|
||||
|
||||
// Предоперационный койко-день
|
||||
$preoperativeValue =
|
||||
$canUsePreoperativeTotals = $preoperativePatientCount > 0
|
||||
&& $preoperativeTotalRecords > 0
|
||||
&& $preoperativePatientRecords >= $preoperativeTotalRecords;
|
||||
$preoperativeValue = $canUsePreoperativeTotals
|
||||
? round($preoperativeSum / $preoperativePatientCount, 1)
|
||||
: ($preoperativeAverageCount > 0 ? round($preoperativeAverageSum / $preoperativeAverageCount, 1) : 0);
|
||||
|
||||
// Летальность
|
||||
$lethality = $outcome > 0 ? round(($deceased / $outcome) * 100, 2) : 0;
|
||||
@@ -203,8 +227,14 @@ class StatisticsService
|
||||
'countStaff' => $staff,
|
||||
'countObservable' => $observable,
|
||||
'countUnwanted' => $unwanted,
|
||||
|
||||
'averageBedDays' => $avgBedDays,
|
||||
'bedDaysSum' => $bedDaysSum,
|
||||
|
||||
'preoperativeDays' => $preoperativeValue,
|
||||
'preoperativeSum' => $preoperativeSum,
|
||||
'preoperativePatientCount' => $preoperativePatientCount,
|
||||
|
||||
'progressPlanOfYear' => $periodPlan,
|
||||
'percentPlanOfYear' => $percentPlanOfYear,
|
||||
'lethality' => $lethality,
|
||||
@@ -248,6 +278,11 @@ class StatisticsService
|
||||
'deceased_sum' => 0,
|
||||
'percentLoadedBeds_total' => 0,
|
||||
'percentLoadedBeds_count' => 0,
|
||||
'bedDaysSum' => 0,
|
||||
|
||||
'preoperativeSum' => 0,
|
||||
'preoperativePatientCount' => 0,
|
||||
|
||||
'staff_sum' => 0,
|
||||
'observable_sum' => 0,
|
||||
'unwanted_sum' => 0,
|
||||
@@ -272,6 +307,9 @@ class StatisticsService
|
||||
$totals['deceased_sum'] += $data['deceased'];
|
||||
$totals['percentLoadedBeds_total'] += $data['percentLoadedBeds'];
|
||||
$totals['percentLoadedBeds_count']++;
|
||||
$totals['bedDaysSum'] += $data['bedDaysSum'];
|
||||
$totals['preoperativeSum'] += $data['preoperativeSum'];
|
||||
$totals['preoperativePatientCount'] += $data['preoperativePatientCount'];
|
||||
$totals['staff_sum'] += $data['countStaff'];
|
||||
$totals['observable_sum'] += $data['countObservable'];
|
||||
$totals['unwanted_sum'] += $data['countUnwanted'];
|
||||
@@ -325,11 +363,13 @@ class StatisticsService
|
||||
*/
|
||||
private function createTotalRow(string $type, array $total, bool $isGrandTotal): array
|
||||
{
|
||||
if ($total['preoperativePatientCount'] === 0) $total['preoperativePatientCount'] = 1;
|
||||
if ($total['outcome_sum'] === 0) $total['outcome_sum'] = 1;
|
||||
return [
|
||||
'isTotalRow' => ! $isGrandTotal,
|
||||
'isGrandTotal' => $isGrandTotal,
|
||||
'department' => $isGrandTotal ? 'ОБЩИЕ ИТОГИ:' : 'ИТОГО:',
|
||||
'beds' => '—',
|
||||
'beds' => $total['beds_sum'],
|
||||
'recipients' => [
|
||||
'all' => $total['recipients_all_sum'],
|
||||
'plan' => $total['recipients_plan_sum'],
|
||||
@@ -344,9 +384,9 @@ class StatisticsService
|
||||
'emergency' => $total['emergency_surgical_sum'],
|
||||
],
|
||||
'deceased' => $total['deceased_sum'],
|
||||
'averageBedDays' => '—',
|
||||
'preoperativeDays' => '—',
|
||||
'lethality' => '—',
|
||||
'averageBedDays' => round($total['bedDaysSum'] / $total['outcome_sum'], 1),
|
||||
'preoperativeDays' => round($total['preoperativeSum'] / $total['preoperativePatientCount'] < 0 ?? 1, 1),
|
||||
'lethality' => round(($total['deceased_sum'] / $total['outcome_sum']) * 100, 2),
|
||||
'type' => $type,
|
||||
'departments_count' => $total['departments_count'],
|
||||
'countStaff' => $total['staff_sum'],
|
||||
|
||||
Reference in New Issue
Block a user