Gambaran Teknis
Tumpukan teknologi
| Komponen | Versi / 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 |
| Pengujian | PHPUnit ^11.5 |
| Gaya kode | Laravel Pint |
Struktur direktori
| Direktori | Isi |
|---|---|
app/Filament/Resources | CRUD per model (Pages/Schemas/Tables terpisah) |
app/Filament/Pages | Halaman kustom: Chat Rooms, SIMRS Sync, Queue Board, API SDK, Dashboard |
app/Services/Whatsapp | Gerbang WhatsApp, ChatPanel, sinkronisasi kontak, auto-reply |
app/Services/Payment | Duitku, siklus hidup booking, notifikasi |
app/Services/Simrs | SyncManager, 5 syncer, rollback |
app/Services/Ai | Eksekutor tool, pencatat token |
app/Support | Katalog (tool, knowledge, provider LLM), helper schema |
app/Observers | ChatbotSessionObserver, WhatsappMessageObserver |
Topologi produksi
| Kontainer | Peran | Port |
|---|---|---|
crm_nginx | Web server aplikasi | 127.0.0.1:8090 → 80 |
crm_php | PHP-FPM, tempat semua artisan dijalankan | 9000 |
crm_postgres | PostgreSQL laravel_db / laravel_user | 5432 (internal) |
crm_redis | Cache | 6379 (internal) |
crm_queue | Worker antrean | — |
crm_scheduler | Penjadwal cron Laravel | — |
crm_simrs_tunnel | Terowongan ke MySQL SIMRS | — |
Direktori stack: /var/www/crm-hospital, kode aplikasi di /var/www/crm-hospital/src.
Aliran permintaan HTTP
Pekerjaan latar & penjadwalan
| Tugas | Kadens | Bergerbang config? | Fungsi |
|---|---|---|---|
bookings:reconcile | */5 * * * * | ❌ Tidak — sengaja | Selamatkan booking lunas yang belum terkonfirmasi; matikan draft basi |
contacts:sync | */10 * * * * (dapat diubah) | ✅ WHATSAPP_CONTACT_SYNC_ENABLED | Tarik daftar kontak dari Jasnita |
simrs:sync --all | 0 1 * * * | ✅ SIMRS_SYNC_SCHEDULE_ENABLED | Sinkronisasi seluruh entitas SIMRS |
Semuanya memakai withoutOverlapping() dan onOneServer().
Antrean pekerjaan (driver database):
| Job | Dipicu oleh |
|---|---|
ProcessInboundWhatsappMessage | Webhook WhatsApp — 3 percobaan, jeda 10 detik |
RunSimrsSyncJob | Tombol SIMRS Sync di UI — timeout dinaikkan sampai 6 jam |
Tanpa crm_queue, pesan WhatsApp masuk tersimpan tapi tidak pernah dibalas, dan tombol SIMRS Sync di UI tidak melakukan apa-apa.
Event & listener
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
| Konvensi | Keterangan |
|---|---|
| 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::error | error hanya untuk kegagalan yang menyangkut uang yang sudah diterima. Jangan diseragamkan |
\Throwable ditelan | Pola umum di codebase ini, tapi maknanya berbeda per tempat — baca komentarnya sebelum mengubah |
| Query builder vs model | Beberapa penulisan sengaja lewat query builder ber-id supaya instance model yang kotor tidak ikut menulis kolom yatim |
| Enkripsi kolom | llm_api_key, gateway_api_key, gateway_api_secret, gateway_client_secret, gateway_password |
Perbedaan lingkungan
| Produksi | Staging | Lokal | |
|---|---|---|---|
| Database | PostgreSQL (3 schema) | MySQL | SQLite / MySQL |
| Runtime | Docker | Bare metal Apache + PHP-FPM | artisan serve |
| Deploy | Manual via skrip | Auto-pull tiap 5 menit | — |
| SIMRS | Lewat terowongan | Biasanya nonaktif | Biasanya nonaktif |
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.