modified: .gitignore
This commit is contained in:
@@ -15,7 +15,9 @@ class OperationController extends Controller
|
||||
'historyId' => 'required|integer'
|
||||
]);
|
||||
|
||||
$operations = MisSurgicalOperation::where('rf_MedicalHistoryID', $request->historyId)->get();
|
||||
$operations = MisSurgicalOperation::where('rf_MedicalHistoryID', $request->historyId)
|
||||
->completed()
|
||||
->get();
|
||||
|
||||
return response()->json(
|
||||
OperationsResource::collection($operations)
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\DepartmentPatientOperationResource;
|
||||
use App\Http\Resources\Mis\FormattedPatientResource;
|
||||
use App\Models\Department;
|
||||
use App\Models\MedicalHistorySnapshot;
|
||||
use App\Models\MetrikaGroup;
|
||||
use App\Models\MetrikaResult;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Models\MisMKB;
|
||||
use App\Models\MisMedicalHistory;
|
||||
use App\Models\MisMigrationPatient;
|
||||
use App\Models\MisStationarBranch;
|
||||
@@ -23,6 +25,7 @@ use App\Services\ReportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -39,188 +42,29 @@ class ReportController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$department = $user->department;
|
||||
|
||||
$startDateCarbon = Carbon::now()->firstOfMonth();
|
||||
$endDateCarbon = Carbon::now();
|
||||
|
||||
// Определяем даты в зависимости от роли
|
||||
[$startDate, $endDate] = $this->dateRangeService->getDateRangeForUser($user, $request->query('startAt'), $request->query('endAt'));
|
||||
if (Carbon::parse($startDate)->isValid()) {
|
||||
$startDateCarbon = Carbon::parse($startDate)->setTimeZone('Asia/Yakutsk');
|
||||
}
|
||||
if (Carbon::parse($endDate)->isValid()) {
|
||||
$endDateCarbon = Carbon::parse($endDate)->setTimeZone('Asia/Yakutsk');
|
||||
}
|
||||
|
||||
$reportIds = [];
|
||||
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
|
||||
$reportIds = $reports->pluck('report_id')->toArray();
|
||||
|
||||
// Определяем, используем ли мы снапшоты
|
||||
$reportToday = Report::whereDate('sent_at', $endDate)
|
||||
->whereDate('created_at', $endDate)
|
||||
->first();
|
||||
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || (Carbon::parse($endDate)->isToday() === false || $reportToday);
|
||||
if ($useSnapshots && ($user->isHeadOfDepartment() || $user->isAdmin())) {
|
||||
$report = Report::whereDate('sent_at', $endDate)
|
||||
->where('rf_department_id', $department->department_id)
|
||||
->first();
|
||||
$fillableUserId = $report->rf_lpudoctor_id ?? null;
|
||||
} else {
|
||||
$fillableUserId = $request->query('userId', $user->rf_lpudoctor_id);
|
||||
}
|
||||
|
||||
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
|
||||
$occupiedBeds = optional(Report::where('rf_department_id', $user->rf_department_id)
|
||||
->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
|
||||
->where('metrika_results.rf_metrika_item_id', 8)
|
||||
->orderBy('sent_at', 'desc')->first())->value ?? 0;
|
||||
|
||||
$percentLoadedBeds = round(intval($occupiedBeds) * 100 / $beds); //intval($occupiedBeds) * 100 / $beds;
|
||||
|
||||
$departmentId = $request->query('departmentId', $user->department->department_id);
|
||||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
$statistics = $this->reportService->getReportStatistics($department, $user, $dateRange);
|
||||
$reportInfo = $this->reportService->getCurrentReportInfo($department, $user, $dateRange);
|
||||
$metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
|
||||
$metrikaItems = $metrikaGroup->metrikaItems;
|
||||
|
||||
$misDepartmentId = $request->user()->department->rf_mis_department_id;
|
||||
|
||||
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
|
||||
->value('StationarBranchID');
|
||||
|
||||
$unwantedEvents = UnwantedEvent::whereHas('report', function ($query) use ($user, $startDate, $endDate) {
|
||||
$query->where('rf_department_id', $user->rf_department_id)
|
||||
->whereDate('created_at', $endDate);
|
||||
})
|
||||
->get()->map(function ($item) {
|
||||
return [
|
||||
...$item->toArray(),
|
||||
'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'),
|
||||
];
|
||||
});
|
||||
|
||||
// Определяем, является ли пользователь заведующим/администратором
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
// Получаем статистику в зависимости от источника данных
|
||||
if ($useSnapshots || $reportToday) {
|
||||
// Используем снапшоты для статистики
|
||||
// $plan = $this->getCountFromSnapshots('plan', $reportIds);
|
||||
// $emergency = $this->getCountFromSnapshots('emergency', $reportIds);
|
||||
$recipientCount = $this->getPatientsCountFromSnapshot('recipient', $reportIds);
|
||||
$outcomeCount = $this->getPatientsCountFromSnapshot('outcome', $reportIds);
|
||||
$currentCount = $this->getPatientsCountFromSnapshot('current', $reportIds);
|
||||
$deadCount = $this->getPatientsCountFromSnapshot('deceased', $reportIds);
|
||||
|
||||
// Для операций все равно используем реплику с фильтрацией по датам
|
||||
$surgicalCount = [
|
||||
$this->getSurgicalPatientsFromSnapshot('plan', $reportIds),
|
||||
$this->getSurgicalPatientsFromSnapshot('emergency', $reportIds)
|
||||
];
|
||||
|
||||
$recipientIds = $this->getRecipientIdsFromSnapshots($reportIds);
|
||||
} else {
|
||||
// Используем реплику для статистики
|
||||
$plan = $this->getPlanOrEmergencyPatients(
|
||||
'plan',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
true,
|
||||
today: true
|
||||
);
|
||||
$emergency = $this->getPlanOrEmergencyPatients(
|
||||
'emergency',
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
true,
|
||||
today: true
|
||||
);
|
||||
$outcomeCount = $this->getAllOutcomePatients(
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
true
|
||||
);
|
||||
$currentCount = $this->getCurrentPatients($branchId, true);
|
||||
$deadCount = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, true);
|
||||
|
||||
$surgicalCount = [
|
||||
$this->getSurgicalPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, true),
|
||||
$this->getSurgicalPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, true)
|
||||
];
|
||||
|
||||
$recipientIds = $this->getPlanOrEmergencyPatients(
|
||||
null,
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
today: true
|
||||
);
|
||||
}
|
||||
|
||||
$isActiveSendButton = Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() &&
|
||||
(!$user->isHeadOfDepartment() && !$user->isAdmin()) && $reportToday == null;
|
||||
|
||||
$reportDoctor = $reportToday?->lpuDoctor;
|
||||
$message = null;
|
||||
if ($reportToday) {
|
||||
if ($reportDoctor && $reportDoctor->LPUDoctorID === intval($fillableUserId)) {
|
||||
$isActiveSendButton = true;
|
||||
} else {
|
||||
$message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V";
|
||||
}
|
||||
|
||||
$lpuDoctor = $reportDoctor;
|
||||
} else {
|
||||
if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) {
|
||||
$lpuDoctor = null;
|
||||
} else {
|
||||
$lpuDoctor = MisLpuDoctor::where('LPUDoctorID', $fillableUserId)->first();
|
||||
}
|
||||
}
|
||||
|
||||
$isRangeOneDay = $this->dateRangeService->isRangeOneDay($startDate, $endDate);
|
||||
|
||||
$date = $isHeadOrAdmin ? [
|
||||
$this->dateRangeService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(),
|
||||
$this->dateRangeService->parseDate($endDate)->getTimestampMs()
|
||||
] : $this->dateRangeService->parseDate($endDate)->getTimestampMs();
|
||||
|
||||
return response()->json([
|
||||
'department' => [
|
||||
'beds' => $beds,
|
||||
'percentLoadedBeds' => $percentLoadedBeds,
|
||||
|
||||
'recipientCount' => $useSnapshots ? $recipientCount : $plan + $emergency, //$recipientCount,
|
||||
'extractCount' => $outcomeCount, //$extractedCount,
|
||||
'currentCount' => $currentCount,
|
||||
'deadCount' => $deadCount,
|
||||
'surgicalCount' => $surgicalCount,
|
||||
'recipientIds' => $recipientIds,
|
||||
'department_id' => $department->department_id,
|
||||
'department_name' => $department->name_full,
|
||||
'beds' => $department->beds,
|
||||
...$statistics,
|
||||
],
|
||||
'dates' => [
|
||||
'startAt' => $startDateCarbon->getTimestampMs(),
|
||||
'endAt' => $endDateCarbon->getTimestampMs()
|
||||
],
|
||||
'report' => [
|
||||
'report_id' => $reportToday?->report_id,
|
||||
'unwantedEvents' => $unwantedEvents,
|
||||
'isActiveSendButton' => $isActiveSendButton,
|
||||
'message' => $message,
|
||||
'isOneDay' => $isRangeOneDay,
|
||||
'isHeadOrAdmin' => $isHeadOrAdmin,
|
||||
'dates' => $date
|
||||
'startAt' => $dateRange->startTimestamp(),
|
||||
'endAt' => $dateRange->endTimestamp(),
|
||||
],
|
||||
'report' => $reportInfo,
|
||||
'metrikaItems' => $metrikaItems,
|
||||
'userId' => $fillableUserId,
|
||||
'userName' => $lpuDoctor ? "$lpuDoctor->FAM_V $lpuDoctor->IM_V $lpuDoctor->OT_V" : null
|
||||
'userId' => $reportInfo['userId'],
|
||||
'userName' => $reportInfo['userName'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -660,22 +504,53 @@ class ReportController extends Controller
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
'departmentId' => 'nullable'
|
||||
'departmentId' => 'nullable',
|
||||
'page' => 'nullable|integer|min:1',
|
||||
'perPage' => 'nullable|integer|min:1|max:1000',
|
||||
'search' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
$departmentId = $request->get('departmentId', $user->department->department_id);
|
||||
$department = Department::where('department_id', $departmentId)->first();
|
||||
$page = (int) ($validated['page'] ?? 1);
|
||||
$perPage = (int) ($validated['perPage'] ?? 20);
|
||||
$search = trim((string) ($validated['search'] ?? ''));
|
||||
|
||||
$patients = $this->reportService->getPatientsByStatus(
|
||||
$patients = collect($this->reportService->getPatientsByStatus(
|
||||
$department,
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange
|
||||
);
|
||||
));
|
||||
|
||||
return response()->json(FormattedPatientResource::collection($patients));
|
||||
if ($search !== '') {
|
||||
$needle = mb_strtolower($search);
|
||||
$patients = $patients->filter(function ($patient) use ($needle) {
|
||||
$fullName = mb_strtolower(trim((string) ($patient->fullname ?? "{$patient->FAMILY} {$patient->Name} {$patient->OT}")));
|
||||
$diagnosisCode = mb_strtolower((string) ($patient->mkb['ds'] ?? ''));
|
||||
$diagnosisName = mb_strtolower((string) ($patient->mkb['name'] ?? ''));
|
||||
|
||||
return str_contains($fullName, $needle)
|
||||
|| str_contains($diagnosisCode, $needle)
|
||||
|| str_contains($diagnosisName, $needle);
|
||||
})->values();
|
||||
}
|
||||
|
||||
$total = $patients->count();
|
||||
$items = $patients->forPage($page, $perPage)->values();
|
||||
$data = FormattedPatientResource::collection($items)->resolve();
|
||||
|
||||
return response()->json([
|
||||
'data' => $data,
|
||||
'meta' => [
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'perPage' => $perPage,
|
||||
'lastPage' => max((int) ceil($total / max($perPage, 1)), 1),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPatientsCount(Request $request)
|
||||
@@ -704,6 +579,45 @@ class ReportController extends Controller
|
||||
return response()->json($count);
|
||||
}
|
||||
|
||||
public function getPatientsCounts(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$request->validate([
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
'departmentId' => 'nullable',
|
||||
'force' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
$departmentId = $request->get('departmentId', $user->department->department_id);
|
||||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||||
|
||||
$cacheKey = sprintf(
|
||||
'report:patients-counts:%d:%d:%s:%s',
|
||||
$user->id,
|
||||
$department->department_id,
|
||||
$dateRange->startSql(),
|
||||
$dateRange->endSql()
|
||||
);
|
||||
|
||||
$force = (bool) $request->boolean('force', false);
|
||||
|
||||
if ($force) {
|
||||
$counts = $this->reportService->getPatientsCountsMap($department, $user, $dateRange);
|
||||
Cache::put($cacheKey, $counts, now()->addSeconds(30));
|
||||
} else {
|
||||
$counts = Cache::remember($cacheKey, now()->addSeconds(30), function () use ($department, $user, $dateRange) {
|
||||
return $this->reportService->getPatientsCountsMap($department, $user, $dateRange);
|
||||
});
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'counts' => $counts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить пациентов (плановых или экстренных)
|
||||
*/
|
||||
@@ -721,17 +635,35 @@ class ReportController extends Controller
|
||||
$isOutcomeStatus = in_array($status, ['outcome-transferred', 'outcome-discharged', 'outcome-deceased']);
|
||||
|
||||
if ($isOutcomeStatus) {
|
||||
switch ($status) {
|
||||
case 'outcome-transferred':
|
||||
$query = MisMigrationPatient::transferred($branchId, $startDate, $endDate);
|
||||
break;
|
||||
case 'outcome-discharged':
|
||||
$query = MisMigrationPatient::discharged($branchId, $startDate, $endDate);
|
||||
break;
|
||||
case 'outcome-deceased':
|
||||
$query = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate);
|
||||
break;
|
||||
$visitResultIds = match ($status) {
|
||||
'outcome-transferred' => [4, 14],
|
||||
'outcome-discharged' => [1, 11, 2, 12, 7, 18, 48],
|
||||
'outcome-deceased' => [5, 6, 15, 16],
|
||||
default => [],
|
||||
};
|
||||
|
||||
$historyQuery = $this->buildOutcomeMedicalHistoryQuery(
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
$visitResultIds
|
||||
);
|
||||
|
||||
if ($onlyIds) {
|
||||
return $historyQuery->pluck('MedicalHistoryID')->values();
|
||||
}
|
||||
|
||||
if ($returnedCount) {
|
||||
return $historyQuery->count();
|
||||
}
|
||||
|
||||
return $historyQuery
|
||||
->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
|
||||
$query->where('Date', '>=', $startDate)
|
||||
->where('Date', '<=', $endDate);
|
||||
}])
|
||||
->orderBy('DateRecipient', 'DESC')
|
||||
->get();
|
||||
} else {
|
||||
// Разная логика для заведующего и врача
|
||||
if ($isHeadOrAdmin) {
|
||||
@@ -795,42 +727,16 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function getAllOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false)
|
||||
{
|
||||
// Сначала получаем миграции с типами выбытия
|
||||
$migrations = MisMigrationPatient::outcomePatients($branchId, $startDate, $endDate)
|
||||
->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
|
||||
->get()
|
||||
->groupBy('rf_MedicalHistoryID');
|
||||
$query = $this->buildOutcomeMedicalHistoryQuery($branchId, $startDate, $endDate);
|
||||
|
||||
if ($migrations->isEmpty()) {
|
||||
if ($returnedCount) return 0;
|
||||
return collect();
|
||||
if ($returnedCount) {
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
$medicalHistoryIds = $migrations->keys()->toArray();
|
||||
|
||||
// Получаем истории
|
||||
$patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||||
return $query
|
||||
->with(['surgicalOperations'])
|
||||
->orderBy('DateRecipient', 'DESC');
|
||||
|
||||
if ($returnedCount) return $patients->count();
|
||||
else $patients = $patients->get();
|
||||
|
||||
// Добавляем информацию о типе выбытия
|
||||
return $patients->map(function ($patient) use ($migrations) {
|
||||
$patientMigrations = $migrations->get($patient->MedicalHistoryID, collect());
|
||||
|
||||
// Определяем основной тип выбытия (берем последнюю миграцию)
|
||||
$latestMigration = $patientMigrations->sortByDesc('DateOut')->first();
|
||||
|
||||
if ($latestMigration) {
|
||||
$patient->outcome_type = $this->getOutcomeTypeName($latestMigration->rf_kl_VisitResultID);
|
||||
$patient->outcome_date = $latestMigration->DateOut;
|
||||
$patient->visit_result_id = $latestMigration->rf_kl_VisitResultID;
|
||||
}
|
||||
|
||||
return $patient;
|
||||
});
|
||||
->orderBy('DateRecipient', 'DESC')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -852,26 +758,45 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function getDeceasedOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false, bool $onlyIds = false)
|
||||
{
|
||||
$medicalHistoryIds = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate)
|
||||
->pluck('rf_MedicalHistoryID')
|
||||
->unique()
|
||||
->toArray();
|
||||
|
||||
if (empty($medicalHistoryIds)) {
|
||||
if ($returnedCount) return 0;
|
||||
return collect();
|
||||
}
|
||||
$query = $this->buildOutcomeMedicalHistoryQuery(
|
||||
$branchId,
|
||||
$startDate,
|
||||
$endDate,
|
||||
[5, 6, 15, 16]
|
||||
);
|
||||
|
||||
if ($onlyIds) {
|
||||
return $medicalHistoryIds;
|
||||
return $query->pluck('MedicalHistoryID')->values();
|
||||
}
|
||||
|
||||
$query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
|
||||
->with(['surgicalOperations'])
|
||||
->orderBy('DateRecipient', 'DESC');
|
||||
|
||||
if ($returnedCount) return $query->count();
|
||||
else return $query->get();
|
||||
else return $query
|
||||
->with(['surgicalOperations'])
|
||||
->orderBy('DateRecipient', 'DESC')
|
||||
->get();
|
||||
}
|
||||
|
||||
private function buildOutcomeMedicalHistoryQuery(
|
||||
int $branchId,
|
||||
string $startDate,
|
||||
string $endDate,
|
||||
?array $visitResultIds = null
|
||||
)
|
||||
{
|
||||
$startDateOnly = Carbon::parse($startDate)->toDateString();
|
||||
$endDateOnly = Carbon::parse($endDate)->toDateString();
|
||||
|
||||
return MisMedicalHistory::query()
|
||||
->where('MedicalHistoryID', '<>', 0)
|
||||
->whereDate('DateExtract', '>', $startDateOnly)
|
||||
->whereDate('DateExtract', '<=', $endDateOnly)
|
||||
->whereHas('migrations', function ($migrationQuery) use ($branchId, $visitResultIds) {
|
||||
$migrationQuery->where('rf_StationarBranchID', $branchId);
|
||||
|
||||
if ($visitResultIds !== null && !empty($visitResultIds)) {
|
||||
$migrationQuery->whereIn('rf_kl_VisitResultID', $visitResultIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -880,6 +805,7 @@ class ReportController extends Controller
|
||||
private function getSurgicalPatients(string $status, bool $isHeadOrAdmin, $branchId, $startDate, $endDate, bool $returnedCount = false)
|
||||
{
|
||||
$query = MisSurgicalOperation::where('rf_StationarBranchID', $branchId)
|
||||
->completed()
|
||||
// ->whereBetween('Date', [$startDate, $endDate])
|
||||
->where('Date', '>=', $startDate)
|
||||
->where('Date', '<=', $endDate)
|
||||
@@ -930,14 +856,199 @@ class ReportController extends Controller
|
||||
Request $request,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'id' => 'required'
|
||||
'id' => 'required|string'
|
||||
]);
|
||||
|
||||
ObservationPatient::where('rf_medicalhistory_id', $data['id'])->delete();
|
||||
$this->reportService->removeObservationPatient($data['id']);
|
||||
|
||||
return response()->json()->setStatusCode(200);
|
||||
}
|
||||
|
||||
public function createManualPatient(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$data = $request->validate([
|
||||
'departmentId' => 'required|integer',
|
||||
'full_name' => 'required|string|max:255',
|
||||
'birth_date' => 'required|date',
|
||||
'patient_kind' => 'required|in:plan,emergency',
|
||||
'diagnosis_code' => 'nullable|string|max:255',
|
||||
'diagnosis_name' => 'nullable|string|max:1000',
|
||||
'admitted_at' => 'nullable|date',
|
||||
]);
|
||||
|
||||
$department = Department::where('department_id', $data['departmentId'])->firstOrFail();
|
||||
$patient = $this->reportService->createManualPatient($department, $user, $data);
|
||||
|
||||
return response()->json($patient, 201);
|
||||
}
|
||||
|
||||
public function setManualPatientOutcome(Request $request, int $departmentPatientId)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'outcome_type' => 'required|in:discharged,transferred,deceased',
|
||||
'outcome_at' => 'nullable|date',
|
||||
]);
|
||||
|
||||
return response()->json(
|
||||
$this->reportService->setManualPatientOutcome($departmentPatientId, $data)
|
||||
);
|
||||
}
|
||||
|
||||
public function updateManualPatient(Request $request, int $departmentPatientId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$data = $request->validate([
|
||||
'full_name' => 'required|string|max:255',
|
||||
'birth_date' => 'required|date',
|
||||
'patient_kind' => 'required|in:plan,emergency',
|
||||
'diagnosis_code' => 'nullable|string|max:255',
|
||||
'diagnosis_name' => 'nullable|string|max:1000',
|
||||
'admitted_at' => 'nullable|date',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
return response()->json(
|
||||
$this->reportService->updateManualPatient(
|
||||
$user,
|
||||
$departmentPatientId,
|
||||
$data
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function linkManualPatient(Request $request, int $departmentPatientId)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'medical_history_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
return response()->json(
|
||||
$this->reportService->linkManualPatientToMis($departmentPatientId, $data['medical_history_id'])
|
||||
);
|
||||
}
|
||||
|
||||
public function getManualPatientOperations(int $departmentPatientId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$operations = $this->reportService->getManualPatientOperations($user, $departmentPatientId);
|
||||
|
||||
return response()->json(
|
||||
DepartmentPatientOperationResource::collection($operations)
|
||||
);
|
||||
}
|
||||
|
||||
public function createManualPatientOperation(Request $request, int $departmentPatientId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$data = $request->validate([
|
||||
'service_id' => 'required|integer',
|
||||
'urgency' => 'required|in:plan,emergency',
|
||||
'started_at' => 'required|date',
|
||||
'ended_at' => 'required|date|after_or_equal:started_at',
|
||||
]);
|
||||
|
||||
$operation = $this->reportService->createManualPatientOperation($user, $departmentPatientId, $data);
|
||||
|
||||
return response()->json(new DepartmentPatientOperationResource($operation), 201);
|
||||
}
|
||||
|
||||
public function updateManualPatientOperation(Request $request, int $departmentPatientId, int $operationId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$data = $request->validate([
|
||||
'service_id' => 'required|integer',
|
||||
'urgency' => 'required|in:plan,emergency',
|
||||
'started_at' => 'required|date',
|
||||
'ended_at' => 'required|date|after_or_equal:started_at',
|
||||
]);
|
||||
|
||||
$operation = $this->reportService->updateManualPatientOperation($user, $departmentPatientId, $operationId, $data);
|
||||
|
||||
return response()->json(new DepartmentPatientOperationResource($operation));
|
||||
}
|
||||
|
||||
public function deleteManualPatientOperation(int $departmentPatientId, int $operationId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$this->reportService->deleteManualPatientOperation($user, $departmentPatientId, $operationId);
|
||||
|
||||
return response()->json()->setStatusCode(204);
|
||||
}
|
||||
|
||||
public function searchMisPatients(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'departmentId' => 'required|integer',
|
||||
'query' => 'required|string|min:2',
|
||||
]);
|
||||
|
||||
$department = Department::where('department_id', $data['departmentId'])->firstOrFail();
|
||||
$patients = $this->reportService->searchMisPatientsForDepartment($department, $data['query']);
|
||||
|
||||
return response()->json(FormattedPatientResource::collection($patients));
|
||||
}
|
||||
|
||||
public function searchMkb(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'query' => 'required|string|min:1|max:255',
|
||||
]);
|
||||
|
||||
$query = trim($data['query']);
|
||||
$needle = mb_strtolower($query, 'UTF-8');
|
||||
$like = "%{$needle}%";
|
||||
|
||||
$items = MisMKB::query()
|
||||
->select(['MKBID', 'DS', 'NAME'])
|
||||
->where(function ($builder) use ($like) {
|
||||
$builder->whereRaw('LOWER("DS") LIKE ?', [$like])
|
||||
->orWhereRaw('LOWER("NAME") LIKE ?', [$like]);
|
||||
})
|
||||
->orderBy('DS')
|
||||
->limit(30)
|
||||
->get()
|
||||
->map(fn (MisMKB $item) => [
|
||||
'id' => $item->MKBID,
|
||||
'code' => $item->DS,
|
||||
'name' => $item->NAME,
|
||||
'label' => trim(($item->DS ? "{$item->DS} " : '') . ($item->NAME ?? '')),
|
||||
])
|
||||
->values();
|
||||
|
||||
return response()->json($items);
|
||||
}
|
||||
|
||||
public function searchMedicalServices(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'query' => 'required|string|min:2|max:255',
|
||||
]);
|
||||
|
||||
$query = trim($data['query']);
|
||||
|
||||
$items = \App\Models\MisServiceMedical::query()
|
||||
->select(['ServiceMedicalID', 'ServiceMedicalCode', 'ServiceMedicalName'])
|
||||
->where(function ($builder) use ($query) {
|
||||
$builder->where('ServiceMedicalCode', 'like', "%{$query}%")
|
||||
->orWhere('ServiceMedicalName', 'like', "%{$query}%");
|
||||
})
|
||||
->orderBy('ServiceMedicalCode')
|
||||
->limit(30)
|
||||
->get()
|
||||
->map(fn (\App\Models\MisServiceMedical $item) => [
|
||||
'id' => $item->ServiceMedicalID,
|
||||
'code' => $item->ServiceMedicalCode,
|
||||
'name' => $item->ServiceMedicalName,
|
||||
'label' => trim(($item->ServiceMedicalCode ? "{$item->ServiceMedicalCode} " : '') . ($item->ServiceMedicalName ?? '')),
|
||||
])
|
||||
->values();
|
||||
|
||||
return response()->json($items);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// api/report/unwanted-event
|
||||
|
||||
25
app/Http/Controllers/TestController.php
Normal file
25
app/Http/Controllers/TestController.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\PatientMigrationService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class TestController extends Controller
|
||||
{
|
||||
public function testMigrations(PatientMigrationService $migrationService)
|
||||
{
|
||||
$startAt = Carbon::parse('2026-01-01T00:00:00')->format('Y-m-d H:i:s');
|
||||
$endAt = Carbon::parse('2026-03-31T23:59:00')->format('Y-m-d H:i:s');
|
||||
|
||||
$cacheKey = "branch_current_2";
|
||||
\Cache::tags(["migrations_in_branch_outcome"])->flush();
|
||||
|
||||
$data = $migrationService->getMigrationsInBranchOutcome(2, $startAt, $endAt);
|
||||
return Inertia::render('TestQuery', [
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,18 @@
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Mis\FormattedPatientResource;
|
||||
use App\Models\Department;
|
||||
use App\Models\MetrikaGroup;
|
||||
use App\Models\MetrikaItem;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Models\Report;
|
||||
use App\Models\UnwantedEvent;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\ReportPageService;
|
||||
use App\Services\ReportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected ReportPageService $reportPageService,
|
||||
protected ReportService $reportService,
|
||||
protected DateRangeService $dateRangeService
|
||||
) {}
|
||||
@@ -28,40 +23,10 @@ class ReportController extends Controller
|
||||
{
|
||||
$user = Auth::user();
|
||||
$departmentId = $request->query('departmentId', $user->department->department_id);
|
||||
$department = Department::where('department_id', $departmentId)->first(); //$user->department;
|
||||
|
||||
$department = Department::where('department_id', $departmentId)->firstOrFail();
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
// Получаем статистику
|
||||
$statistics = $this->reportService->getReportStatistics($department, $user, $dateRange);
|
||||
|
||||
// Получаем метрики
|
||||
// $metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
|
||||
// $metrikaItems = $metrikaGroup->metrikaItems;
|
||||
$metrikaItems = MetrikaItem::whereIn('metrika_item_id', [3, 7, 8, 17])->get();
|
||||
|
||||
// Получаем информацию о текущем отчете
|
||||
$reportInfo = $this->reportService->getCurrentReportInfo($department, $user, $dateRange);
|
||||
|
||||
return Inertia::render('Report/Index', [
|
||||
'department' => [
|
||||
'department_name' => $department->name_full,
|
||||
'department_id' => $department->department_id,
|
||||
'beds' => $department->beds,
|
||||
'percentLoadedBeds' => $this->calculateBedOccupancy($department, $user),
|
||||
'recipientPlanOfYear' => $this->reportService->getRecipientPlanOfYear($department, $dateRange)['plan'],
|
||||
'progressPlanOfYear' => $this->reportService->getRecipientPlanOfYear($department, $dateRange)['progress'],
|
||||
...$statistics,
|
||||
],
|
||||
'dates' => [
|
||||
'startAt' => $dateRange->startTimestamp(),
|
||||
'endAt' => $dateRange->endTimestamp()
|
||||
],
|
||||
'report' => $reportInfo,
|
||||
'metrikaItems' => $metrikaItems,
|
||||
'userId' => $reportInfo['userId'],
|
||||
'userName' => $reportInfo['userName']
|
||||
]);
|
||||
return Inertia::render('Report/Index', $this->reportPageService->build($department, $user, $dateRange));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
@@ -76,90 +41,8 @@ class ReportController extends Controller
|
||||
'reportId' => 'nullable|integer'
|
||||
]);
|
||||
|
||||
$report = $this->reportService->storeReport($validated, Auth::user(), false);
|
||||
$this->reportService->storeReport($validated, Auth::user(), false);
|
||||
|
||||
return redirect()->route('start');
|
||||
}
|
||||
|
||||
public function getPatients(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
$patients = $this->reportService->getPatientsByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange,
|
||||
);
|
||||
|
||||
return response()->json(FormattedPatientResource::collection($patients));
|
||||
}
|
||||
|
||||
public function getPatientsCount(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
$count = $this->reportService->getPatientsCountByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange,
|
||||
);
|
||||
|
||||
return response()->json($count);
|
||||
}
|
||||
|
||||
public function removeObservation(Request $request)
|
||||
{
|
||||
$validated = $request->validate(['id' => 'required|integer']);
|
||||
|
||||
$this->reportService->removeObservationPatient($validated['id']);
|
||||
|
||||
return response()->json(['message' => 'Удалено'], 200);
|
||||
}
|
||||
|
||||
public function removeUnwantedEvent(UnwantedEvent $unwantedEvent)
|
||||
{
|
||||
$unwantedEvent->delete();
|
||||
return response()->json(['message' => 'Удалено'], 200);
|
||||
}
|
||||
|
||||
public function getDepartmentUsers()
|
||||
{
|
||||
$users = MisLpuDoctor::select(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V'])
|
||||
->active()
|
||||
->inMyDepartment()
|
||||
->get();
|
||||
|
||||
return response()->json($users, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитать загруженность коек
|
||||
*/
|
||||
private function calculateBedOccupancy(Department $department, $user): int
|
||||
{
|
||||
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
|
||||
$occupiedBeds = optional(Report::where('rf_department_id', $department->department_id)
|
||||
->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
|
||||
->where('metrika_results.rf_metrika_item_id', 8)
|
||||
->orderBy('sent_at', 'desc')
|
||||
->first())->value ?? 0;
|
||||
|
||||
return $beds > 0 ? round(intval($occupiedBeds) * 100 / $beds) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ class HandleInertiaRequests extends Middleware
|
||||
'version' => config('app.version'),
|
||||
'tag' => config('app.tag')
|
||||
],
|
||||
'config' => [
|
||||
'timeEventSourceUrl' => config('time.eventSourceUrl')
|
||||
],
|
||||
'user' => $user ? [
|
||||
'name' => $user->name,
|
||||
'token' => Session::get('token'),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources\Api;
|
||||
|
||||
use App\Models\DepartmentPatientOperation;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class DepartmentPatientOperationResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
/** @var DepartmentPatientOperation $operation */
|
||||
$operation = $this->resource;
|
||||
|
||||
$serviceCode = $operation->serviceMedical?->ServiceMedicalCode ?? $operation->service_code;
|
||||
$serviceName = $operation->serviceMedical?->ServiceMedicalName ?? $operation->service_name;
|
||||
|
||||
return [
|
||||
'id' => $operation->department_patient_operation_id,
|
||||
'urgency' => $operation->urgency,
|
||||
'service' => [
|
||||
'id' => $operation->rf_kl_service_medical_id,
|
||||
'code' => $serviceCode,
|
||||
'name' => $serviceName,
|
||||
'label' => trim(($serviceCode ? "{$serviceCode} " : '') . ($serviceName ?? '')),
|
||||
],
|
||||
'startAt' => $operation->started_at?->toIso8601String(),
|
||||
'endAt' => $operation->ended_at?->toIso8601String(),
|
||||
'duration' => $operation->started_at && $operation->ended_at
|
||||
? Carbon::parse($operation->started_at)->diffInMinutes(Carbon::parse($operation->ended_at))
|
||||
: null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Resources\Mis;
|
||||
|
||||
use App\Models\MisSurgicalOperation;
|
||||
use App\Data\UnifiedPatientData;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Carbon;
|
||||
@@ -17,9 +17,33 @@ class FormattedPatientResource extends JsonResource
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
if ($this->resource instanceof UnifiedPatientData) {
|
||||
$age = $this->formatAge($this->age);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'patient_uid' => $this->patientUid,
|
||||
'source_type' => $this->sourceType,
|
||||
'department_patient_id' => $this->departmentPatientId,
|
||||
'medical_history_id' => $this->medicalHistoryId,
|
||||
'mkb' => $this->mkb,
|
||||
'operations' => $this->operations,
|
||||
'fullname' => $this->fullname,
|
||||
'age' => $age,
|
||||
'birth_date' => $this->birthDate ? Carbon::parse($this->birthDate)->format('d.m.Y') : null,
|
||||
'patient_kind' => $this->patientKind,
|
||||
'admitted_at' => $this->admittedAt ? Carbon::parse($this->admittedAt)->format('d.m.Y H:i') : null,
|
||||
'outcome_type' => $this->outcomeType,
|
||||
'outcome_date' => $this->outcomeDate,
|
||||
'comment' => $this->comment,
|
||||
'is_recipient_today' => $this->isRecipientToday,
|
||||
'is_manual' => $this->isManual,
|
||||
'can_manage_manual' => $this->canManageManual,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $this->MedicalHistoryID,
|
||||
'num' => $this->num,
|
||||
'mkb' => [
|
||||
'ds' => $this->outcomeMigration->first()->mainDiagnosis?->mkb?->DS,
|
||||
'name' => $this->outcomeMigration->first()->mainDiagnosis?->mkb?->NAME
|
||||
@@ -31,10 +55,41 @@ class FormattedPatientResource extends JsonResource
|
||||
];
|
||||
}),
|
||||
'fullname' => Str::ucwords(Str::lower("$this->FAMILY $this->Name $this->OT")),
|
||||
'age' => Carbon::parse($this->BD)->diff(Carbon::now())->format('%y'),
|
||||
'age' => $this->formatAge(Carbon::parse($this->BD)->diffInYears(Carbon::now(), false)),
|
||||
'birth_date' => Carbon::parse($this->BD)->format('d.m.Y'),
|
||||
'admitted_at' => $this->DateRecipient ? Carbon::parse($this->DateRecipient)->format('d.m.Y H:i') : null,
|
||||
'outcome_type' => $this->when($this->outcome_type, $this->outcome_type),
|
||||
'comment' => $this->when($this->comment, $this->comment)
|
||||
'outcome_date' => $this->when($this->outcome_date, $this->outcome_date),
|
||||
'comment' => $this->when($this->comment, $this->comment),
|
||||
'is_recipient_today' => (bool) ($this->is_recipient_today ?? false),
|
||||
'is_manual' => false,
|
||||
'can_manage_manual' => false,
|
||||
];
|
||||
}
|
||||
|
||||
private function formatAge($value): ?string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$age = abs((int) $value);
|
||||
$suffix = $this->ageSuffix($age);
|
||||
|
||||
return "{$age} {$suffix}";
|
||||
}
|
||||
|
||||
private function ageSuffix(int $age): string
|
||||
{
|
||||
$mod100 = $age % 100;
|
||||
if ($mod100 >= 11 && $mod100 <= 14) {
|
||||
return 'лет';
|
||||
}
|
||||
|
||||
return match ($age % 10) {
|
||||
1 => 'год',
|
||||
2, 3, 4 => 'года',
|
||||
default => 'лет',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user