Обновлен стартовый экран

Переписаны запросы для статистики, отчетов
Добавлена интеграция отчета сестры
This commit is contained in:
brusnitsyn
2026-05-28 22:10:00 +09:00
parent 90e0d04dfd
commit 739168d427
96 changed files with 6663 additions and 1465 deletions

View File

@@ -10,6 +10,9 @@ use App\Models\MigrationPatient;
use App\Models\MigrationPatientCorrection;
use App\Models\MigrationPatientNurse;
use App\Models\MisStationarBranch;
use App\Models\ReportNurse;
use App\Models\ReportNurseMigrationPatient;
use App\Models\ReportNursePatient;
use App\Models\UnifiedMedicalHistory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -146,6 +149,10 @@ class NurseController extends Controller
if ($historyCorrection && $migrationCorrection) {
DB::commit();
// Синхронизируем снапшот, чтобы правки были видны в МИС-вкладке
$reportNurseId = $request->input('report_nurse_id');
$this->syncNurseSnapshot($originalId, $data, $migrationData, $departmentId, $reportNurseId);
return response()->json([
'data' => $historyCorrection,
], 201);
@@ -157,17 +164,64 @@ class NurseController extends Controller
}
}
private function syncNurseSnapshot(
mixed $originalId,
array $data,
array $migrationData,
int $departmentId,
?int $reportNurseId
): void {
$patientUpdate = collect($data)
->only(['recipient_date', 'extract_date', 'death_date', 'urgency_id',
'visit_result_id', 'hospital_result_id', 'full_name', 'birth_date'])
->toArray();
if (empty($patientUpdate)) return;
// Если известен отчёт — обновляем только его и все последующие по этому отделению
$reportNurseQuery = ReportNursePatient::where('original_id', $originalId);
if ($reportNurseId) {
$fromReport = ReportNurse::find($reportNurseId);
if ($fromReport) {
$affectedReportIds = ReportNurse::where('rf_department_id', $fromReport->rf_department_id)
->where('period_start', '>=', $fromReport->period_start)
->pluck('id');
$reportNurseQuery->whereIn('report_nurse_id', $affectedReportIds);
}
}
$snapshotPatientIds = $reportNurseQuery->pluck('id');
if ($snapshotPatientIds->isEmpty()) return;
ReportNursePatient::whereIn('id', $snapshotPatientIds)->update($patientUpdate);
$migrationUpdate = array_filter([
'ingoing_date' => $migrationData['ingoing_date'] ?? null,
'out_date' => $migrationData['out_date'] ?? null,
'visit_result_id' => $migrationData['visit_result_id'] ?? null,
]);
if (!empty($migrationUpdate)) {
ReportNurseMigrationPatient::whereIn('medical_history_id', $snapshotPatientIds)
->where('department_id', $departmentId)
->update($migrationUpdate);
}
}
public function deleteHistory($id, Request $request)
{
$medicalHistory = UnifiedMedicalHistory::where('id', $id)->first();
$medicalHistoryId = $medicalHistory->original_id;
$originalMedicalHistoryId = $medicalHistory->original_id;
$nurseMedicalHistory = ReportNursePatient::where('original_id', $originalMedicalHistoryId)->first();
$migrationPatientsIds = $medicalHistory->migrations()->pluck('original_id');
foreach ($migrationPatientsIds as $migrationPatientsId) {
MigrationPatientNurse::query()->where('id', $migrationPatientsId)->delete();
foreach ($nurseMedicalHistory->migrations() as $nurseMigrationPatient) {
$nurseMigrationPatient->delete();
}
MedicalHistoryNurse::where('id', $medicalHistoryId)->delete();
$nurseMedicalHistory->delete();
return response()->json([]);
}

View File

@@ -4,10 +4,13 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Department;
use App\Models\DutyUnwantedEvent;
use App\Services\DateRangeService;
use App\Services\ReportService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class StatisticController extends Controller
{
@@ -28,7 +31,17 @@ class StatisticController extends Controller
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $validated['startAt'], $validated['endAt']);
$department = Department::findSole($request->departmentId, 'department_id');
$unwantedEvents = $this->reportService->getUnwantedEvents($department, $dateRange);
$unwantedEvents = DutyUnwantedEvent::query()
->whereHas('reportDuty', function ($q) use ($department, $dateRange) {
$q->where('rf_department_id', $department->department_id)
->where('period_start', '>=', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql());
})
->get()
->map(fn ($e) => [
...$e->toArray(),
'created_at' => Carbon::parse($e->created_at)->format('Создано d.m.Y в H:i'),
]);
return response()->json($unwantedEvents);
}
@@ -44,7 +57,35 @@ class StatisticController extends Controller
$dateRange = $this->dateRangeService->getNormalizedDateRange($user, $validated['startAt'], $validated['endAt']);
$department = Department::findSole($request->departmentId, 'department_id');
$observablePatients = $this->reportService->getPatientsByStatus($department, $user, 'observation', $dateRange);
$observablePatients = DB::table('observable_medical_histories as omh')
->join('report_duty_patients as rdp', 'rdp.original_id', '=', 'omh.original_id')
->join('report_duties as rd', 'rd.id', '=', 'rdp.report_duty_id')
->leftJoin('report_duty_migration_patients as rdm', function ($join) {
$join->on('rdm.medical_history_id', '=', 'rdp.id')
->whereNull('rdm.out_date');
})
->where('rd.rf_department_id', $department->department_id)
->where('omh.observable_in', '>=', $dateRange->startSql())
->where('omh.observable_in', '<=', $dateRange->endSql())
->select('omh.*', 'rdm.diagnosis_code', 'rdm.diagnosis_name', 'rdm.ingoing_date as migration_ingoing_date')
->distinct()
->get()
->map(fn ($row) => [
'id' => $row->id,
'full_name' => $row->full_name,
'birth_date' => $row->birth_date,
'comment' => $row->comment,
'observable_reason'=> $row->observable_reason,
'observable_in' => $row->observable_in,
'observable_out' => $row->observable_out,
'migrations' => [[
'ingoing_date' => $row->migration_ingoing_date ?? $row->recipient_date,
'diagnosis_code' => $row->diagnosis_code,
'diagnosis_name' => $row->diagnosis_name,
]],
'operations' => [],
]);
return response()->json($observablePatients);
}

View File

@@ -75,20 +75,17 @@ class AuthController extends Controller
$token = $user->tokens()->where('name', $sessionId)->first();
if ($token) {
$token->abilities = ['role:'.$request->role_id];
$token->abilities = ['role:'.$data['role_id']];
$token->save();
}
// Store in session payload (web) and DB column (API) for dual access
session(['role_id' => $data['role_id']]);
DB::table('sessions')
->where('id', $sessionId)
->update(['role_id' => $request->role_id]);
->update(['role_id' => $data['role_id']]);
// $sessionKey = 'user_' . $user->id . '_current_role';
//
// $user->current_role_id = $data['role_id'];
// $user->save();
return redirect()->route('start')->setStatusCode(302);
return redirect()->route('start');
}
public function logout(Request $request)

View File

@@ -9,10 +9,8 @@ class AdminController extends Controller
{
public function index()
{
return Inertia::render('Admin/Index',
[
abort_unless(auth()->user()->isAdmin(), 403);
]
);
return Inertia::render('Admin/Index', []);
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers\Web\Admin;
use App\Http\Controllers\Controller;
use App\Models\MetrikaGroup;
use App\Models\MetrikaGroupItem;
use App\Models\MetrikaItem;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Inertia\Inertia;
class MetrikaController extends Controller
{
public function index()
{
abort_unless(auth()->user()->isAdmin(), 403);
$groups = MetrikaGroup::withCount('groupItems')->get()->map(fn($g) => [
'id' => $g->metrika_group_id,
'name' => $g->name,
'description' => $g->description,
'items_count' => $g->group_items_count,
]);
$items = MetrikaItem::all()->map(fn($i) => [
'id' => $i->metrika_item_id,
'name' => $i->name,
'description' => $i->description,
'data_type' => $i->data_type,
'is_active' => $i->is_active,
'code' => $i->code,
]);
return Inertia::render('Admin/Metrics/Index', [
'groups' => $groups,
'items' => $items,
]);
}
// ── Группы ──
public function createGroup()
{
abort_unless(auth()->user()->isAdmin(), 403);
return Inertia::render('Admin/Metrics/Group', [
'group' => null,
'itemIds' => [],
'allItems' => $this->allItems(),
]);
}
public function storeGroup(Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'items' => 'array',
'items.*' => 'integer|exists:metrika_items,metrika_item_id',
]);
$group = MetrikaGroup::create([
'name' => $validated['name'],
'description' => $validated['description'] ?? null,
]);
foreach ($validated['items'] ?? [] as $itemId) {
MetrikaGroupItem::create([
'rf_metrika_group_id' => $group->metrika_group_id,
'rf_metrika_item_id' => $itemId,
]);
}
return redirect('/admin/metrics')->with('success', 'Группа создана');
}
public function showGroup(MetrikaGroup $group)
{
abort_unless(auth()->user()->isAdmin(), 403);
$itemIds = MetrikaGroupItem::where('rf_metrika_group_id', $group->metrika_group_id)
->pluck('rf_metrika_item_id')
->toArray();
return Inertia::render('Admin/Metrics/Group', [
'group' => ['id' => $group->metrika_group_id, 'name' => $group->name, 'description' => $group->description],
'itemIds' => $itemIds,
'allItems' => $this->allItems(),
]);
}
public function updateGroup(MetrikaGroup $group, Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'items' => 'array',
'items.*' => 'integer|exists:metrika_items,metrika_item_id',
]);
$group->update([
'name' => $validated['name'],
'description' => $validated['description'] ?? null,
]);
MetrikaGroupItem::where('rf_metrika_group_id', $group->metrika_group_id)->delete();
foreach ($validated['items'] ?? [] as $itemId) {
MetrikaGroupItem::create([
'rf_metrika_group_id' => $group->metrika_group_id,
'rf_metrika_item_id' => $itemId,
]);
}
return redirect('/admin/metrics')->with('success', 'Группа сохранена');
}
// ── Показатели ──
public function createItem()
{
abort_unless(auth()->user()->isAdmin(), 403);
return Inertia::render('Admin/Metrics/Item', ['item' => null]);
}
public function storeItem(Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'data_type' => 'required|string|in:integer,float,string,text,boolean,select',
'is_active' => 'required|boolean',
'is_required' => 'boolean',
'default_value' => 'nullable|string',
'placeholder' => 'nullable|string',
]);
MetrikaItem::create([
...$validated,
'code' => Str::slug($validated['name']),
]);
return redirect('/admin/metrics')->with('success', 'Показатель создан');
}
public function showItem(MetrikaItem $item)
{
abort_unless(auth()->user()->isAdmin(), 403);
return Inertia::render('Admin/Metrics/Item', [
'item' => [
'id' => $item->metrika_item_id,
'name' => $item->name,
'description' => $item->description,
'data_type' => $item->data_type,
'is_active' => $item->is_active,
'is_required' => $item->is_required,
'default_value' => $item->default_value,
'placeholder' => $item->placeholder,
'code' => $item->code,
],
]);
}
public function updateItem(MetrikaItem $item, Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'data_type' => 'required|string|in:integer,float,string,text,boolean,select',
'is_active' => 'required|boolean',
'is_required' => 'boolean',
'default_value' => 'nullable|string',
'placeholder' => 'nullable|string',
]);
$item->update($validated);
return redirect('/admin/metrics')->with('success', 'Показатель сохранён');
}
private function allItems(): array
{
return MetrikaItem::where('is_active', true)->get()->map(fn($i) => [
'metrika_item_id' => $i->metrika_item_id,
'name' => $i->name,
'data_type' => $i->data_type,
])->toArray();
}
}

View File

@@ -7,14 +7,17 @@ use App\Models\Department;
use App\Models\Role;
use App\Models\User;
use App\Models\UserDepartment;
use App\Models\UserRole;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Inertia\Inertia;
class UserController extends Controller
{
public function index()
{
$users = User::with(['roles', 'department'])->get()->map(function ($user) {
abort_unless(auth()->user()->isAdmin(), 403);
$users = User::with(['appRoles', 'department'])->get()->map(function ($user) {
return [
'id' => $user->id,
'name' => $user->name,
@@ -25,76 +28,169 @@ class UserController extends Controller
];
});
return Inertia::render('Admin/Users/Index',
[
'users' => $users,
]
);
return Inertia::render('Admin/Users/Index', ['users' => $users]);
}
public function create()
{
$rolesData = Role::all()->map(function ($role) {
return [
'role_id' => $role->role_id,
'name' => $role->name,
];
});
$departmentData = Department::all()->map(function (Department $department) {
return [
'department_id' => $department->department_id,
'name_full' => $department->name_full,
];
});
abort_unless(auth()->user()->isAdmin(), 403);
return Inertia::render('Admin/Users/Create', [
'departments' => $departmentData,
'roles' => $rolesData,
'departments' => $this->allDepartments(),
'roles' => $this->allRoles(),
]);
}
public function store(Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string',
'login' => 'required|string',
'password' => 'required|string',
'is_active' => 'required|boolean',
'name' => 'required|string|max:255',
'login' => 'required|string|max:255|unique:users,login',
'password' => 'required|string|min:6',
'is_active' => 'required|boolean',
'department_id' => 'required|integer|exists:departments,department_id',
'departments' => 'array',
'departments.*' => 'integer|exists:departments,department_id',
'roles' => 'required|array|min:1',
'roles.*' => 'integer|exists:roles,role_id',
]);
dd($validated);
$user = User::create([
'name' => $validated['name'],
'login' => $validated['login'],
'password' => Hash::make($validated['password']),
'is_active' => $validated['is_active'],
'rf_department_id' => $validated['department_id'],
]);
// Назначаем роли — первая роль становится дефолтной
foreach ($validated['roles'] as $i => $roleId) {
UserRole::create([
'rf_user_id' => $user->id,
'rf_role_id' => $roleId,
'is_active' => true,
'is_default' => $i === 0,
]);
}
// Привязываем дополнительные отделения
foreach ($validated['departments'] ?? [] as $i => $deptId) {
UserDepartment::create([
'rf_user_id' => $user->id,
'rf_department_id' => $deptId,
'is_favorite' => false,
'order' => $i,
]);
}
return redirect('/admin/users')->with('success', 'Пользователь создан');
}
public function show(User $user, Request $request)
public function show(User $user)
{
abort_unless(auth()->user()->isAdmin(), 403);
$userData = [
'id' => $user->id,
'name' => $user->name,
'login' => $user->login,
'is_active' => $user->is_active,
'created_at' => $user->created_at->format('d.m.Y H:i:s'),
'updated_at' => $user->updated_at->format('d.m.Y H:i:s'),
'id' => $user->id,
'name' => $user->name,
'login' => $user->login,
'is_active' => $user->is_active,
'department_id' => $user->rf_department_id,
'created_at' => $user->created_at->format('d.m.Y H:i:s'),
'updated_at' => $user->updated_at->format('d.m.Y H:i:s'),
];
$rolesData = $user->roles->map(function ($role) {
return [
'role_id' => $role->role_id,
'name' => $role->name,
];
});
$userRoleIds = $user->userRoles()->pluck('rf_role_id')->toArray();
$departmentData = $user->departments->map(function (UserDepartment $userDepartment) {
return [
'department_id' => $userDepartment->department->department_id,
'name_full' => $userDepartment->department->name_full,
];
});
$userDepartmentIds = UserDepartment::where('rf_user_id', $user->id)
->pluck('rf_department_id')
->toArray();
return Inertia::render('Admin/Users/User', [
'userData' => $userData,
'roles' => $rolesData,
'departments' => $departmentData,
'userData' => $userData,
'userRoleIds' => $userRoleIds,
'userDepartmentIds' => $userDepartmentIds,
'allRoles' => $this->allRoles(),
'allDepartments' => $this->allDepartments(),
]);
}
public function update(User $user, Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$validated = $request->validate([
'name' => 'required|string|max:255',
'login' => 'required|string|max:255|unique:users,login,' . $user->id,
'is_active' => 'required|boolean',
'department_id' => 'required|integer|exists:departments,department_id',
'departments' => 'array',
'departments.*' => 'integer|exists:departments,department_id',
'roles' => 'required|array|min:1',
'roles.*' => 'integer|exists:roles,role_id',
]);
$user->update([
'name' => $validated['name'],
'login' => $validated['login'],
'is_active' => $validated['is_active'],
'rf_department_id' => $validated['department_id'],
]);
// Синхронизируем роли
UserRole::where('rf_user_id', $user->id)->delete();
foreach ($validated['roles'] as $i => $roleId) {
UserRole::create([
'rf_user_id' => $user->id,
'rf_role_id' => $roleId,
'is_active' => true,
'is_default' => $i === 0,
]);
}
// Синхронизируем доп. отделения через updateOrCreate
$newDeptIds = $validated['departments'] ?? [];
UserDepartment::where('rf_user_id', $user->id)
->whereNotIn('rf_department_id', $newDeptIds)
->delete();
foreach ($newDeptIds as $i => $deptId) {
UserDepartment::updateOrCreate(
['rf_user_id' => $user->id, 'rf_department_id' => $deptId],
['is_favorite' => false, 'order' => $i]
);
}
return redirect('/admin/users/' . $user->id)->with('success', 'Данные сохранены');
}
public function resetPassword(User $user, Request $request)
{
abort_unless(auth()->user()->isAdmin(), 403);
$request->validate([
'password' => 'required|string|min:6|confirmed',
]);
$user->update(['password' => Hash::make($request->password)]);
return redirect('/admin/users/' . $user->id)->with('success', 'Пароль изменён');
}
private function allRoles(): array
{
return Role::all()->map(fn($r) => [
'role_id' => $r->role_id,
'name' => $r->name,
])->toArray();
}
private function allDepartments(): array
{
return Department::orderBy('name_full')->get()->map(fn($d) => [
'department_id' => $d->department_id,
'name_full' => $d->name_full,
])->toArray();
}
}

View File

@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use App\Http\Resources\MedicalHistoryResource;
use App\Models\Department;
use App\Models\DepartmentMetrikaDefault;
use App\Models\ObservableMedicalHistory;
use App\Models\ReportDuty;
use App\Models\ReportNurse;
use App\Services\DateRangeService;
@@ -15,6 +17,7 @@ use App\Services\NurseMedicalHistoryService;
use App\Services\NurseReportService;
use App\Services\UnifiedMedicalHistoryService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
@@ -37,65 +40,306 @@ class DutyReportController extends Controller
public function index(Request $request)
{
$user = Auth::user();
$search = $request->get('search');
$selectedUserId = $request->query('userId') ? (int) $request->query('userId') : null;
$departmentId = $request->query('departmentId', $user->department->department_id);
$department = Department::where('department_id', $departmentId)->firstOrFail();
$bedsInDepartment = DepartmentMetrikaDefault::where('rf_department_id', $departmentId)
->where('rf_metrika_item_id', 1)->first()->value ?? 0;
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
// Проверяем, есть ли отчет за этот период
$isRangeOneDay = $this->dateRangeService->isRangeOneDay($dateRange->startDate, $dateRange->endDate);
$isPastPeriod = $this->dateRangeService->isPastPeriod($dateRange);
$existsReport = ReportDuty::where('rf_department_id', $departmentId)
->where('period_end', '>', $dateRange->startSql())
$isCurrentPeriod = !$isPastPeriod;
// Всегда загружаем отчеты за период
$reportsDuty = ReportDuty::where('rf_department_id', $departmentId)
->where('period_start', '>=', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql())
->exists();
->orderBy('period_end', 'desc')
->with(['unwantedEvents', 'doctor'])
->get();
$hasReport = $existsReport && $isPastPeriod;
$reportsNurse = ReportNurse::where('rf_department_id', $departmentId)
->where('period_start', '>=', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql())
->orderBy('period_end', 'desc')
->get();
$hasDutyReport = $reportsDuty->count() > 0;
$hasNurseReport = $reportsNurse->count() > 0;
$reportDutyIds = $reportsDuty->pluck('id')->toArray();
$reportNurseIds = $reportsNurse->pluck('id')->toArray();
// dd($reportsDuty, $dateRange->endSql());
// Получаем пациентов (источник зависит от периода)
if ($isCurrentPeriod) {
// Для текущего периода - пациенты из МИС
$patients = $this->medicalHistoryService->getGroupedHistories(
$dateRange,
$department->rf_mis_department_id,
$search
);
// Если есть отчет, загружаем из него дополнительные данные
if ($hasDutyReport) {
// Получаем нежелательные события и наблюдения из отчета
$reportData = $this->getReportAdditionalData($reportsDuty);
// Добавляем эти данные к пациентам
$patients = $this->mergeReportData($patients, $reportData);
}
$nursePatients = $hasNurseReport
? $this->nurseMedicalHistoryService->getGroupedHistories(
$dateRange,
$department->rf_mis_department_id,
$reportNurseIds
)
: [];
$currentPatients = $patients['meta']['counts']['in_department'];
$loaded = $bedsInDepartment > 0
? round(($currentPatients * 100) / $bedsInDepartment)
: 0;
$latestReport = $reportsDuty->first();
if ($hasReport) {
$inDepartmentHistories = $this->dutyMedicalHistoryService->getDepartmentHistories($dateRange, $department->rf_mis_department_id);
$plannedHistories = collect([ 'data' => [] ]);
$emergencyHistories = collect([ 'data' => [] ]);
$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);
$reanimationHistories = collect([ 'data' => [] ]);
} else if ($this->dateRangeService->isPastPeriod($dateRange)) {
$inDepartmentHistories = collect([ 'data' => [] ]);
$plannedHistories = collect([ 'data' => [] ]);
$emergencyHistories = collect([ 'data' => [] ]);
$recipientHistories = collect([ 'data' => [] ]);
$dischargedHistories = collect([ 'data' => [] ]);
$deceasedHistories = collect([ 'data' => [] ]);
$transferredHistories = collect([ 'data' => [] ]);
$reanimationHistories = collect([ 'data' => [] ]);
} else {
$patients = $this->medicalHistoryService->getGroupedHistories($dateRange, $department->rf_mis_department_id);
// Для прошедшего периода - данные из отчета
$patients = $hasDutyReport
? $this->dutyMedicalHistoryService->getGroupedHistories(
$dateRange,
$department->rf_mis_department_id,
$reportDutyIds,
$search
)
: $this->getEmptyPatientsData();
$nursePatients = $hasNurseReport
? $this->nurseMedicalHistoryService->getGroupedHistories(
$dateRange,
$department->rf_mis_department_id,
$reportNurseIds
)
: [];
$latestReport = $reportsDuty->first();
if ($latestReport && $hasDutyReport) {
$currentPatients = $patients['meta']['counts']['in_department'];
$loaded = $latestReport->getLoadedDepartmentAttribute($currentPatients);
} else {
$loaded = 0;
}
}
return Inertia::render('Report/Index', [
'department' => $department,
'patients' => $patients,
'departmentInfo' => [
// TODO: Добавить вывод информации из шапки
],
'nursePatients' => $nursePatients,
'latestReport' => $latestReport ?? null,
'canSaveReport' => $isRangeOneDay && $user->currentRoleCan('report.create'),
'canEditPastReport' => $user->currentRoleCan('report.edit.past'),
'canSaveNurseReport' => $isRangeOneDay && $user->currentRoleCan('nurse.report.create'),
'stats' => $this->prepareStats($patients, $nursePatients, $loaded, $bedsInDepartment),
'dates' => [
$dateRange->startDate->getTimestampMs(),
$dateRange->endDate->getTimestampMs(),
]
],
'selectedUserId' => $selectedUserId,
'selectedDepartmentId' => (int) $departmentId,
]);
}
/**
* Сохранение отчета от роли мед. сестра
* Получает дополнительные данные из отчета (нежелательные события, наблюдения)
*/
private function getReportAdditionalData($reportsDuty): array
{
$unwantedEvents = [];
$observations = [];
foreach ($reportsDuty as $report) {
// Нежелательные события
foreach ($report->unwantedEvents as $event) {
$unwantedEvents[] = [
...$event->toArray(),
];
}
// Наблюдения (если есть связь)
if ($report->relationLoaded('observations') || $report->observations) {
foreach ($report->observations as $observation) {
$observations[] = [
...$observation->toArray(),
];
}
}
}
return [
'unwanted_events' => $unwantedEvents,
'observations' => $observations
];
}
/**
* Объединяет данные пациентов из МИС с данными из отчета
*/
private function mergeReportData(array $patients, array $reportData): array
{
// Группируем нежелательные события по отчетам
// $eventsByPatient = [];
// foreach ($reportData['unwanted_events'] as $event) {
// $reportId = $event['report_duty_id'];
// if (!isset($eventsByPatient[$reportId])) {
// $eventsByPatient[$reportId] = [];
// }
// $eventsByPatient[$reportId][] = $event;
// }
// Группируем наблюдения по пациентам
$observationsByPatient = [];
foreach ($reportData['observations'] as $observation) {
$patientId = $observation['patient_id'];
if (!isset($observationsByPatient[$patientId])) {
$observationsByPatient[$patientId] = [];
}
$observationsByPatient[$patientId][] = $observation;
}
// Добавляем данные из отчета к каждому пациенту
foreach ($patients['data'] as &$patient) {
$patientId = $patient['original_id'] ?? $patient['id'] ?? null;
// Может быть позже пригодится
// if ($patientId && isset($eventsByPatient[$patientId])) {
// $patient['unwanted_events'] = $eventsByPatient[$patientId];
// } else {
// $patient['unwanted_events'] = [];
// }
if ($patientId && isset($observationsByPatient[$patientId])) {
$patient['observations'] = $observationsByPatient[$patientId];
} else {
$patient['observations'] = [];
}
}
return $patients;
}
/**
* Получает данные из сестринского отчета
*/
private function getNurseReportAdditionalData($reportsNurse): array
{
$nurseData = [];
foreach ($reportsNurse as $report) {
// Загружаем необходимые данные из сестринского отчета
// Например, наблюдения медсестер, процедуры и т.д.
if ($report->relationLoaded('nurseObservations')) {
$nurseData = array_merge($nurseData, $report->nurseObservations->toArray());
}
}
return $nurseData;
}
/**
* Возвращает пустую структуру данных для пациентов
*/
private function getEmptyPatientsData(): array
{
return [
'data' => [],
'meta' => [
'total' => 0,
'sortBy' => 'ingoing_date',
'sortOrder' => 'desc',
'counts' => [
'in_department' => 0,
'recipient' => 0,
'discharged' => 0,
'deceased' => 0,
'urgent' => 0,
'planned' => 0,
'reanimations' => 0,
'surgical_planned' => 0,
'surgical_urgent' => 0,
]
]
];
}
/**
* Подготавливает статистику для отображения на фронтенде
*/
private function prepareStats(array $patients, array $nursePatients, int $loaded, ?int $bedsInDepartment): array
{
return [
'nurse' => [
'current' => empty($nursePatients) ? 0 : ($nursePatients['meta']['counts']['in_department'] ?? 0),
'recipient' => empty($nursePatients) ? 0 : ($nursePatients['meta']['counts']['recipient'] ?? 0),
'discharged' => empty($nursePatients) ? 0 : ($nursePatients['meta']['counts']['discharged'] ?? 0),
],
'duty' => [
'beds' => $bedsInDepartment ?? 0,
'loaded' => $loaded,
'current' => $patients['meta']['counts']['in_department'] ?? 0,
'recipient' => $patients['meta']['counts']['recipient'] ?? 0,
'discharged' => ($patients['meta']['counts']['discharged'] ?? 0) + ($patients['meta']['counts']['deceased'] ?? 0),
'deceased' => $patients['meta']['counts']['deceased'] ?? 0,
'surgical_planned' => $patients['meta']['counts']['surgical_planned'] ?? 0,
'surgical_urgent' => $patients['meta']['counts']['surgical_urgent'] ?? 0,
]
];
}
/**
* Сохранение отчета
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
abort_if(!auth()->user()->currentRoleCan('report.create'), 403);
$user = auth()->user();
$observables = $request->get('observables', []);
$unwantedEvents = $request->get('unwanted_events', []);
$selectedUserId = $request->get('userId') ? (int) $request->get('userId') : null;
$selectedDepartmentId = $request->get('departmentId') ? (int) $request->get('departmentId') : null;
$staff = (int) $request->get('staff', 0);
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
$report = $this->dutyReportService->saveReport($dateRange);
$this->dutyReportService->saveSnapshot($dateRange, $report);
$report = $this->dutyReportService->saveReport($dateRange, null, $selectedUserId, $selectedDepartmentId);
$stats = $this->dutyReportService->saveSnapshot($dateRange, $report, null, auth()->id());
$this->dutyReportService->saveObservables($observables, $report);
$this->dutyReportService->saveUnwantedEvents($unwantedEvents, $report);
$this->dutyReportService->saveMetrics($stats, $report, $staff);
return redirect()->back();
}
public function closeObservation(Request $request)
{
$user = auth()->user();
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
$observable = $request->get('observable');
$observableId = $observable['id'] ?? null;
if ($observableId === null) return response()->json('Observable not found', 404);
$observable = ObservableMedicalHistory::find($observableId);
$observable->update([
'observable_out' => $dateRange->endDate,
'out_reason' => 'Закрыто пользователем'
]);
}
}

View File

@@ -18,12 +18,10 @@ class IndexController extends Controller
->where('rf_metrika_group_id', $metriks->metrika_group_id)
->get();
$fillableModel =
$departments = Department::all();
return Inertia::render('Report/Index', [
'depatments' => $departments,
'departments' => $departments,
'metriks' => $metriksItems->map(fn ($item) => [
'metrika_group_id' => $item->group->metrika_group_id,
'metrika_group_name' => $item->group->name,

View File

@@ -31,6 +31,7 @@ class NurseReportController extends Controller
public function index(Request $request)
{
$user = Auth::user();
$selectedUserId = $request->query('userId') ? (int) $request->query('userId') : null;
$departmentId = $request->query('departmentId', $user->department->department_id);
$department = Department::where('department_id', $departmentId)->firstOrFail();
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
@@ -66,8 +67,22 @@ class NurseReportController extends Controller
$data = $this->unifiedMedicalHistoryService->getGroupedHistories($dateRange, $department->rf_mis_department_id);
$currentReport = ReportNurse::where('rf_department_id', $departmentId)
->where('period_start', '>=', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql())
->orderBy('period_end', 'desc')
->first();
$isRangeOneDay = $this->dateRangeService->isRangeOneDay($dateRange->startDate, $dateRange->endDate);
return Inertia::render('Nurse/Report/Index', [
'patients' => $data,
'reportNurseId' => $currentReport?->id,
'canSaveReport' => $isRangeOneDay && $user->currentRoleCan('nurse.report.create'),
'canEditPastReport' => $user->currentRoleCan('nurse.report.edit.past'),
'department' => $department,
'selectedUserId' => $selectedUserId,
'selectedDepartmentId' => (int) $departmentId,
'dates' => [
$dateRange->startDate->getTimestampMs(),
$dateRange->endDate->getTimestampMs(),
@@ -81,11 +96,15 @@ class NurseReportController extends Controller
*/
public function store(Request $request)
{
abort_if(!auth()->user()->currentRoleCan('nurse.report.create'), 403);
$user = auth()->user();
$selectedUserId = $request->input('userId') ? (int) $request->input('userId') : null;
$departmentId = $request->input('departmentId') ? (int) $request->input('departmentId') : null;
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
$report = $this->nurseReportService->saveReport($dateRange);
$this->nurseReportService->saveSnapshot($dateRange, $report);
$report = $this->nurseReportService->saveReport($dateRange, null, $selectedUserId, $departmentId);
$this->nurseReportService->saveSnapshot($dateRange, $report, null, auth()->id());
return redirect()->back();
}

View File

@@ -23,27 +23,30 @@ class StatisticController extends Controller
$queryStartDate = $request->query('startAt');
$queryEndDate = $request->query('endAt');
[$startDate, $endDate] = $this->dateService->getStatisticsDateRange($user, $queryStartDate, $queryEndDate);
$isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
$dateRange = $this->dateService->getDateRangeFromRequest($request, $user);
$isRangeOneDay = $this->dateService->isRangeOneDay($dateRange->startDate, $dateRange->endDate);
// Генерируем ключ кэша на основе параметров запроса
// $cacheKey = $this->generateCacheKey($user, $startDate, $endDate, $isRangeOneDay);
// Получаем данные из кэша или вычисляем
$finalData = $this->statisticsService->getStatisticsData($user, $startDate, $endDate, $isRangeOneDay);
$finalData = $this->statisticsService->getStatisticsData($user, $dateRange->startDate, $dateRange->endDate, $isRangeOneDay);
$isHeadOrAdmin = $user->isAdmin() || $user->isHeadOfDepartment();
$date = $isHeadOrAdmin ? [
$this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(),
$this->dateService->parseDate($endDate)->getTimestampMs(),
] : $this->dateService->parseDate($endDate)->getTimestampMs();
$isHeadOrAdmin = $user->isSeniorStaff();
// $date = $isHeadOrAdmin ? [
// $this->dateService->parseDate($isRangeOneDay ? $dateRange->endDate : $dateRange->startDate)->getTimestampMs(),
// $this->dateService->parseDate($dateRange->endDate)->getTimestampMs(),
// ] : $this->dateService->parseDate($dateRange->endDate)->getTimestampMs();
return Inertia::render('Statistic/Index', [
'data' => $finalData['data'],
'totalsByType' => $finalData['totalsByType'],
'grandTotals' => $finalData['grandTotals'],
'isHeadOrAdmin' => $isHeadOrAdmin,
'date' => $date,
'date' => [
$dateRange->startDate->getTimestampMs(),
$dateRange->endDate->getTimestampMs(),
],
'isOneDay' => $isRangeOneDay,
'recipientPlanOfYear' => $finalData['recipientPlanOfYear'],
]);

View File

@@ -41,6 +41,10 @@ class HandleInertiaRequests extends Middleware
return [
...parent::share($request),
'flash' => [
'success' => $request->session()->get('success'),
'error' => $request->session()->get('error'),
],
'app' => [
'version' => config('app.version'),
'tag' => config('app.tag'),
@@ -51,9 +55,17 @@ class HandleInertiaRequests extends Middleware
'user' => $user ? [
'name' => $user->name,
'token' => Session::get('token'),
'permissions' => $user->permissions(),
'permissions' => \Spatie\Permission\Models\Role::findByName($user->currentRole()->slug)?->permissions->pluck('name') ?? collect(),
'role' => $user->currentRole(),
'available_roles' => $user->roles,
'available_roles' => \App\Models\UserRole::where('rf_user_id', $user->id)
->with('role')
->get()
->map(fn($ur) => [
'role_id' => $ur->role->role_id,
'name' => $ur->role->name,
'slug' => $ur->role->slug,
'is_default' => (bool) $ur->is_default,
]),
'available_departments' => $user->availableDepartments(),
'current_department' => $user->department->load('departmentType'),
] : null,