Files
lis/htdocs/app/Http/Controllers/MobileApiController.php
T
2026-07-21 05:45:47 +07:00

342 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Periksa;
use App\PeriksaSYNC;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Session;
class MobileApiController extends Controller
{
protected array $bookLabels = [
'buku01' => 'Cairan Steril',
'buku02' => 'CCI',
'buku03' => 'Darah',
'buku04' => 'HIV',
'buku05' => 'Kuku, Kulit, Adnexa',
'buku06' => 'Kultur TB',
'buku07' => 'Pus dan Jaringan',
'buku08' => 'Sputum',
'buku09' => 'Swab',
'buku10' => 'TCM TB',
'buku11' => 'TCM non-TB',
'buku12' => 'Urine',
'buku13' => 'Feces',
'buku0' => 'Lainnya',
];
protected function bookPoliMap(): array
{
return [
'buku01' => [15,16,43,47,48,66,96,205,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223],
'buku02' => [20],
'buku03' => [7,8,13,17,29,33,44,107,114,115,199,200,201,202],
'buku04' => [30],
'buku05' => [165,179,180,181,183,184,185,187,188,189,190,192,194,195,196,198],
'buku06' => [26,40,45,52,53,56,60,61,68,69,75,92,102,122,123,136,137,144,145,152,153,233],
'buku07' => [18,19,32,35,46,49,50,54,57,58,59,72,160,182,186,191,193,197,206,207,208,224,225,226,227,228,234,235,236,237],
'buku08' => [1,12,23,24,36,39,229,230,231,232],
'buku09' => [2,4,5,9,10,11,21,22,27,34,41,42,51,63,64,65,85,86,87,88,93,97,118,67,121,125,126,127,128,129,130,132,133,134,135,140,147,158,159,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178],
'buku10' => [14,28,55,62,94,104,105,124,138,146],
'buku11' => [79,80,81,82,83,84,203],
'buku12' => [3,6,25,31,154,155,156,157],
'buku13' => [37,38,148,149,204],
];
}
protected function activeExaminationQuery()
{
return Periksa::query()
->where(function ($query) {
$query->where(function ($sub) {
$sub->whereNotIn('status', ['Selesai', 'Arsip'])
->where('status', 'not like', 'Dibatalkan%');
})->orWhereNull('status');
});
}
protected function userFromRequest(Request $request): ?User
{
$header = (string) $request->bearerToken();
if ($header === '') {
return null;
}
try {
$payload = Crypt::decryptString($header);
$data = json_decode($payload, true);
$user = User::find($data['id'] ?? null);
if (!$user || ($data['password'] ?? '') !== $user->password) {
return null;
}
return $user;
} catch (\Throwable $e) {
return null;
}
}
protected function requireUser(Request $request): User
{
$user = $this->userFromRequest($request);
abort_if(!$user, 401, 'Token login tidak valid.');
return $user;
}
protected function tokenFor(User $user): string
{
return Crypt::encryptString(json_encode([
'id' => $user->id,
'password' => $user->password,
'issued_at' => now()->toDateTimeString(),
]));
}
protected function resolveTargetDays($modaliti2): int
{
return (!empty($modaliti2) && is_numeric($modaliti2) && intval($modaliti2) > 0)
? intval($modaliti2)
: 1;
}
protected function resolveMasterFromPoliId($poliId): string
{
foreach ($this->bookPoliMap() as $master => $poliIds) {
if (in_array((int) $poliId, $poliIds, true)) {
return $master;
}
}
return 'buku0';
}
protected function examinationPayload(Periksa $row): array
{
$formatDate = function ($value): ?string {
if (!$value) {
return null;
}
try {
return Carbon::parse($value)->format('Y-m-d H:i:s');
} catch (\Throwable $e) {
return (string) $value;
}
};
return [
'id' => $row->id,
'master' => $this->resolveMasterFromPoliId($row->poli_id),
'master_label' => $this->bookLabels[$this->resolveMasterFromPoliId($row->poli_id)] ?? 'Lainnya',
'nofoto' => $row->nofoto,
'noregister' => $row->noregister,
'nmpasien' => $row->nmpasien,
'jkpasien' => $row->jkpasien,
'usia' => $row->usia,
'tgllahirpasien' => $row->tgllahirpasien,
'tlppasien' => $row->tlppasien,
'alamatpasien' => $row->alamatpasien,
'reques' => $row->reques,
'asalpasien' => $row->asalpasien,
'ruangan' => $row->ruangan,
'klinisi' => $row->klinisi,
'nmdokter' => $row->nmdokter,
'nm_spesimen' => $row->nm_spesimen,
'tanggalsampel' => $row->tanggalsampel,
'pengambilan' => $row->pengambilan,
'asalpengirim' => $row->asalpengirim,
'status' => $row->status ?: 'NEW',
'daftar' => $formatDate($row->daftar),
'verifikasi' => $formatDate($row->verifikasi),
];
}
public function login(Request $request)
{
$request->validate([
'username' => ['required'],
'password' => ['required'],
]);
$user = User::where('username', $request->input('username'))->first();
if (!$user || !Hash::check((string) $request->input('password'), $user->password)) {
return response()->json(['message' => 'Username atau password salah.'], 401);
}
$photo = $user->getPhoto->xfile ?? '/doctor.png';
$user->active_status = 1;
$user->save();
return response()->json([
'token' => $this->tokenFor($user),
'user' => [
'id' => $user->id,
'nama' => $user->nama,
'username' => $user->username,
'previlage' => $user->previlage,
'photo' => url($photo),
],
]);
}
public function ping()
{
return response()->json([
'status' => 'ok',
'application' => config('global.namaapps', config('app.name')),
'time' => now()->toDateTimeString(),
]);
}
public function dashboard(Request $request)
{
$user = $this->requireUser($request);
$allData = $this->activeExaminationQuery()->get();
$mapped = collect($this->bookPoliMap())->flatten()->all();
$books = [];
foreach ($this->bookLabels as $master => $label) {
$count = $master === 'buku0'
? $allData->filter(fn ($item) => !in_array($item->poli_id, $mapped))->count()
: $allData->whereIn('poli_id', $this->bookPoliMap()[$master] ?? [])->count();
$books[] = ['master' => $master, 'label' => $label, 'total' => $count];
}
return response()->json([
'user' => [
'id' => $user->id,
'nama' => $user->nama,
'username' => $user->username,
'previlage' => $user->previlage,
],
'summary' => [
'total_ppds' => User::where('previlage', 'ppds')->count(),
'butuh_verifikasi' => Periksa::where('status', 'like', '%Un Verified%')->count(),
'antrian_hari_ini' => Periksa::whereDate('daftar', Carbon::today())->count(),
'antrian_sinkron' => PeriksaSYNC::where('created_by', $user->username)->where('status', '')->count(),
],
'books' => $books,
'early_warning_groups' => $this->earlyWarningGroups(),
]);
}
public function examinations(Request $request, string $master)
{
$this->requireUser($request);
$query = $this->activeExaminationQuery();
$mapped = collect($this->bookPoliMap())->flatten()->all();
if ($master === 'buku0') {
$query->whereNotIn('poli_id', $mapped);
} elseif (isset($this->bookPoliMap()[$master])) {
$query->whereIn('poli_id', $this->bookPoliMap()[$master]);
} else {
return response()->json(['message' => 'Kategori pemeriksaan tidak dikenal.'], 404);
}
$search = trim((string) $request->query('search', ''));
if ($search !== '') {
$query->where(function ($q) use ($search) {
$q->where('nofoto', 'like', "%{$search}%")
->orWhere('noregister', 'like', "%{$search}%")
->orWhere('nmpasien', 'like', "%{$search}%")
->orWhere('reques', 'like', "%{$search}%");
});
}
$items = $query->orderBy('daftar', 'ASC')->limit(250)->get()->map(fn ($row) => $this->examinationPayload($row));
return response()->json([
'master' => $master,
'label' => $this->bookLabels[$master],
'items' => $items,
]);
}
public function examination(Request $request, int $id)
{
$this->requireUser($request);
$row = Periksa::find($id);
abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
return response()->json([
'item' => $this->examinationPayload($row),
'expertise_url' => url('mobile/expertise/'.$row->id).'?token='.rawurlencode((string) $request->bearerToken()),
'result_url' => $row->nofoto ? url('hasil/'.$row->nofoto) : null,
]);
}
public function earlyWarning(Request $request)
{
$this->requireUser($request);
return response()->json(['groups' => $this->earlyWarningGroups()]);
}
protected function earlyWarningGroups(): array
{
$now = Carbon::now();
$rows = Periksa::leftJoin('poli', 'periksa.poli_id', '=', 'poli.id')
->select('periksa.id', 'periksa.poli_id', 'periksa.nofoto', 'periksa.noregister', 'periksa.nmpasien', 'periksa.reques', 'periksa.asalpasien', 'periksa.daftar', 'periksa.status', 'poli.subpoli', 'poli.modaliti2')
->whereNotNull('periksa.status')
->where('periksa.status', '<>', '')
->whereRaw('LOWER(periksa.status) <> ?', ['selesai'])
->whereRaw('LOWER(periksa.status) not like ?', ['%dibatalkan%'])
->whereNotNull('periksa.daftar')
->orderBy('periksa.daftar', 'ASC')
->get();
$warnings = [];
foreach ($rows as $row) {
$targetDays = $this->resolveTargetDays($row->modaliti2);
$registeredAt = Carbon::parse($row->daftar);
$deadline = $registeredAt->copy()->addDays($targetDays)->endOfDay();
$remainingMinutes = $now->diffInMinutes($deadline, false);
$warningLimit = $targetDays > 1 ? 1440 : 60;
$isOverdue = $now->gt($deadline);
if ($isOverdue || $remainingMinutes <= $warningLimit) {
$warnings[] = array_merge($this->examinationPayload($row), [
'subpoli' => $row->subpoli ?? 'Tanpa Subpoli',
'target_hari' => $targetDays,
'target_selesai' => $deadline->format('Y-m-d H:i:s'),
'sisa_menit' => $remainingMinutes,
'warning_label' => $isOverdue ? 'Melewati Target' : ($targetDays > 1 ? 'Kurang dari 1 Hari' : 'Kurang dari 1 Jam'),
]);
}
}
return collect($warnings)
->groupBy(fn ($item) => $item['subpoli'] ?: 'Tanpa Subpoli')
->map(fn ($items, $subpoli) => [
'subpoli' => $subpoli,
'total' => count($items),
'items' => array_values($items->toArray()),
])
->values()
->toArray();
}
public function openExpertise(Request $request, int $periksa)
{
$token = (string) $request->query('token', '');
$request->headers->set('Authorization', 'Bearer '.$token);
$user = $this->userFromRequest($request);
abort_if(!$user, 401, 'Token login tidak valid.');
Auth::loginUsingId($user->id);
$photo = $user->getPhoto->xfile ?? '/doctor.png';
Session::put('id', $user->id);
Session::put('nama', $user->nama);
Session::put('username', $user->username);
Session::put('previlage', $user->previlage);
Session::put('photo', $photo);
Session::save();
return redirect()->route('mikro.expertise', ['periksa' => $periksa, 'return_to' => 'dashboard']);
}
}