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.