Lewati ke konten utama

Gambaran Teknis


Tumpukan teknologi

KomponenVersi / catatan
Laravel^12.0
PHP^8.2, platform di-pin ke 8.3 di composer.json
Filament^4.0
Filament Shield^4.2
Spatie Permission^7.2
filament-impersonate^5.3
smalot/pdfparser^2.12
PengujianPHPUnit ^11.5
Gaya kodeLaravel Pint

Struktur direktori

DirektoriIsi
app/Filament/ResourcesCRUD per model (Pages/Schemas/Tables terpisah)
app/Filament/PagesHalaman kustom: Chat Rooms, SIMRS Sync, Queue Board, API SDK, Dashboard
app/Services/WhatsappGerbang WhatsApp, ChatPanel, sinkronisasi kontak, auto-reply
app/Services/PaymentDuitku, siklus hidup booking, notifikasi
app/Services/SimrsSyncManager, 5 syncer, rollback
app/Services/AiEksekutor tool, pencatat token
app/SupportKatalog (tool, knowledge, provider LLM), helper schema
app/ObserversChatbotSessionObserver, WhatsappMessageObserver

Topologi produksi

KontainerPeranPort
crm_nginxWeb server aplikasi127.0.0.1:8090 → 80
crm_phpPHP-FPM, tempat semua artisan dijalankan9000
crm_postgresPostgreSQL laravel_db / laravel_user5432 (internal)
crm_redisCache6379 (internal)
crm_queueWorker antrean
crm_schedulerPenjadwal cron Laravel
crm_simrs_tunnelTerowongan ke MySQL SIMRS

Direktori stack: /var/www/crm-hospital, kode aplikasi di /var/www/crm-hospital/src.


Aliran permintaan HTTP


Pekerjaan latar & penjadwalan

TugasKadensBergerbang config?Fungsi
bookings:reconcile*/5 * * * *Tidak — sengajaSelamatkan booking lunas yang belum terkonfirmasi; matikan draft basi
contacts:sync*/10 * * * * (dapat diubah)WHATSAPP_CONTACT_SYNC_ENABLEDTarik daftar kontak dari Jasnita
simrs:sync --all0 1 * * *SIMRS_SYNC_SCHEDULE_ENABLEDSinkronisasi seluruh entitas SIMRS

Semuanya memakai withoutOverlapping() dan onOneServer().

Antrean pekerjaan (driver database):

JobDipicu oleh
ProcessInboundWhatsappMessageWebhook WhatsApp — 3 percobaan, jeda 10 detik
RunSimrsSyncJobTombol SIMRS Sync di UI — timeout dinaikkan sampai 6 jam
Queue worker wajib hidup

Tanpa crm_queue, pesan WhatsApp masuk tersimpan tapi tidak pernah dibalas, dan tombol SIMRS Sync di UI tidak melakukan apa-apa.


Event & listener

Jangan daftarkan listener secara manual

Laravel menemukan ConfirmBookingOnPayment sendiri dengan memindai app/Listeners dan membaca type-hint ReservationPaid pada handle(). Menambahkan Event::listen() untuknya membuat setiap event dijalankan dua kali — dan outcome none tidak idempoten, sehingga pasien menerima dua pesan konfirmasi.


Konvensi kode yang berlaku

KonvensiKeterangan
Komentar "kenapa", bukan "apa"Kode ini penuh komentar panjang yang menjelaskan keputusan desain dan konsekuensi yang diterima. Jangan hapus saat refactor
Log::warning vs Log::errorerror hanya untuk kegagalan yang menyangkut uang yang sudah diterima. Jangan diseragamkan
\Throwable ditelanPola umum di codebase ini, tapi maknanya berbeda per tempat — baca komentarnya sebelum mengubah
Query builder vs modelBeberapa penulisan sengaja lewat query builder ber-id supaya instance model yang kotor tidak ikut menulis kolom yatim
Enkripsi kolomllm_api_key, gateway_api_key, gateway_api_secret, gateway_client_secret, gateway_password

Perbedaan lingkungan

ProduksiStagingLokal
DatabasePostgreSQL (3 schema)MySQLSQLite / MySQL
RuntimeDockerBare metal Apache + PHP-FPMartisan serve
DeployManual via skripAuto-pull tiap 5 menit
SIMRSLewat terowonganBiasanya nonaktifBiasanya nonaktif
Postgres vs MySQL bukan hal sepele

Beberapa perilaku hanya ada di PostgreSQL: lockForUpdate() (no-op di SQLite), CHECK constraint pada enum, pemisahan schema crm/ai, dan insertGetId yang butuh kolom PK eksplisit. Migrasi ber-SQL mentah bisa lolos di satu database dan gagal di yang lain.

Detail deploy ada di Deployment.