# ๐Ÿ“‘ Product Requirements Document (PRD) **Nama Produk:** GoPrint Service General โ€” *SatuSehat Bridging & SIMRS Master Gateway* **Repository:** `worker-satusehat` **Versi Dokumen:** 1.0 **Tanggal:** 2026-05-13 **Status:** `DRAFT` (sinkron dengan kondisi kode terkini) --- ## 1. Latar Belakang Fasilitas kesehatan (faskes) di Indonesia wajib melaporkan transaksi pelayanan medis ke platform nasional **SatuSehat** (Kemenkes RI) dalam format FHIR R4, dan tetap terhubung dengan layanan **BPJS Kesehatan** (VClaim / Antrol / Apotek / IHS) untuk klaim & antrean. Saat ini Sistem Informasi Manajemen Rumah Sakit (SIMRS) lokal tidak dirancang untuk menangani latensi, autentikasi (OAuth2 + HMAC + AES), dan rate limit dari API pemerintah secara langsung. **Visi:** GoPrint Service General menjadi *bridging gateway* tunggal yang: 1. Memisahkan beban integrasi eksternal dari SIMRS inti. 2. Menjamin pengiriman data ke SatuSehat tahan terhadap *rate limit* dan kegagalan token. 3. Menyediakan abstraksi REST/gRPC sederhana bagi tim aplikasi internal. 4. Menampung modul administrasi (RBAC hierarkis, master data) yang dipakai bersama oleh seluruh aplikasi rumah sakit. --- ## 2. Pengguna & Persona | Persona | Interaksi | Kebutuhan Utama | | :--- | :--- | :--- | | **SIMRS Backend / Aplikasi Klinis** | Konsumsi REST `/api/v1/...` & gRPC `:9090` | Latensi rendah, abstraksi FHIR, tidak menangani token SatuSehat sendiri | | **Worker (Sub-system internal)** | Polling DB SIMRS โ†’ POST ke API internal `service-satusehat` | Pengiriman asinkron, recovery 401/429, tracker per resource | | **Admin Faskes (IT)** | Web UI (eksternal) memanggil REST RBAC | CRUD role/pages/permission hirarkis, cache invalidasi otomatis | | **DevOps / Operator** | Dashboard log + metrik | Log JSON terstruktur (WIB), Prometheus, graceful shutdown | | **Auditor Kemenkes** | Tidak langsung โ€” via histori sync | Jejak audit per dokumen (request, response, FHIR ID) | --- ## 3. Ruang Lingkup Fitur (Scope) ### 3.1. Modul Integrasi Eksternal #### A. SatuSehat (Kemenkes) - **Klien resmi `satusehat.SatuSehatClient`** ([client.go](internal/interfaces/satusehat/client.go)): - OAuth2 *client-credentials* dengan caching token + safety margin 60 detik. - Auto-retry pada HTTP 401 (refresh token, request ulang). - Tiga endpoint terpisah: `DoRequest` (FHIR R4), `DoKFA` (master farmasi), `DoConsent`. - **FHIRPayload Builder** (`types.go`): fluent interface (`Set/Append/ToJSON`) untuk membangun payload resource FHIR tanpa risiko *nil pointer*. - **Resource yang DIDUKUNG** (struktur worker tersedia di [internal/worker/satusehat/](internal/worker/satusehat/)): - Implementasi lengkap (ready-to-activate): `Encounter`, `ImagingStudy`, `ServiceRequest` (default + radiology), `Medication`, `MedicationRequest`, `MedicationDispense`. - Kerangka stub (perlu pengisian logika sinkron): `AllergyIntolerance`, `CarePlan`, `ClinicalImpression`, `Composition`, `Condition`, `DiagnosticReport`, `EpisodeOfCare`, `Immunization`, `MedicationStatement`, `Observation`, `Procedure`, `QuestionnaireResponse`, `Specimen`. #### B. BPJS Kesehatan - **Klien resmi `bpjs.Client`** ([client.go](internal/interfaces/bpjs/client.go)): - Header otomatis `X-cons-id`, `X-timestamp`, `X-signature` (HMAC-SHA256). - Dekripsi respons V2 (AES-256-CBC, key = SHA256(ConsID+SecretKey+Timestamp), IV = 16 byte awal key, padding PKCS7). - Auto-decompression berurutan: LZString โ†’ Gzip โ†’ plaintext JSON. - **Service yang siap**: VClaim, AntreanRS, AntreanFKTP, Aplicare, Apotek, PCare, ICare, ERekamMedis. *Belum* terhubung sebagai worker aktif โ€” disediakan sebagai SDK on-demand. #### C. Master KFA (Kamus Farmasi & Alkes) - **Worker `KFA Master Puller`** ([puller.go](internal/master/kfa/puller.go)): - Polling halaman `/satusehat/reference/kfa/products?product_type=farmasi` via API internal `service-satusehat`. - Fetch detail per `kfa_code`, UPSERT bundle 3 tabel: `kfa_product`, `kfa_product_active_ingredient`, `kfa_product_packaging`. - Pencegahan rate-limit: 500ms antar item, 30s pada 429 list API, 60s pada 429 detail API, reset ke halaman 1 + idle 6 jam jika halaman kosong. - **Status: SATU-SATUNYA WORKER YANG AKTIF** di runtime saat ini. ### 3.2. Modul Internal (System Management) #### A. Multi-Database & CQRS - **Database Manager** mendukung PostgreSQL, MySQL, SQL Server, SQLite, MongoDB melalui GORM + `sqlx` + driver native. - Setiap modul bisnis (auth, role/pages, role/permission, role/master, kfa) mengikuti pola: - `CommandRepository` (GORM, write โ†’ primary DB) - `QueryRepository` (sqlx raw SQL, read โ†’ replica jika dikonfigurasi) - `Service` (orkestrasi, validasi, cache-aside) - Multi-koneksi sekaligus (config: `databases.postgres`, `databases.satudata`, `databases.simrs`). #### B. RBAC Hierarkis - Empat sub-modul ([internal/master/role/](internal/master/role/)): - `master` โ€” definisi role induk. - `pages` โ€” daftar halaman/menu dengan parent-child + level. - `permission` โ€” CRUD permission per role per page (Create/Read/Update/Delete/Disable). - `accses` โ€” endpoint agregasi `GetRoleAccess(userID)` untuk konsumsi UI. - Caching tree menu via Redis dengan kunci hash SHA-256 dari parameter query; *invalidate by prefix* pada mutasi. #### C. Transport - **REST API** (Gin) โ€” port 8080 default, registry-based service wiring di [http/servers](internal/infrastructure/transport/http/servers/), Swagger UI ([/docs/swagger](docs/swagger/)). - **gRPC** (port 9090 default) โ€” kerangka tersedia, registrasi handler belum diaktifkan ([main.go:200-204](cmd/api/main.go#L200-L204) โ€” `permissionHandler` masih dikomentari). - Middleware: Auth (JWT/Keycloak/Static/Hybrid), CORS, Request-ID, rate-limit Redis-backed. #### D. Auth Pluggable - Provider yang didukung di config: `jwt` (default), `keycloak`, `static`, `hybrid` dengan fallback. JWT signing key, Keycloak issuer/JWKS URL semuanya via env. ### 3.3. Object Storage - **MinIO/S3** terkoneksi via [interfaces/minio](internal/interfaces/minio/minio.go) untuk penyimpanan dokumen klinis (PDF, hasil radiologi) โ€” diinisialisasi di startup. ### 3.4. Observability - Logger Logrus kustom (JSON, WIB timezone, rotasi harian `logs/YYYY/MM/YYYY-MM-DD.log`). - Prometheus client (`prometheus/client_golang`) ter-import; metric endpoint perlu dipasang di routes. - Field log standar: `service`, `environment`, `request_id`, `caller`, `function_name`. --- ## 4. Persyaratan Non-Fungsional | Kategori | Target | | :--- | :--- | | **Latensi REST internal** | p95 โ‰ค 200 ms (cache hit), โ‰ค 800 ms (cache miss + DB query) | | **Throughput Worker KFA** | โ‰ฅ 100 produk / menit (dibatasi rate limit Kemenkes) | | **Ketahanan API gov't** | Retry otomatis pada 401 (refresh), 429 (sleep 30-60s), 5xx (retry dengan backoff) | | **Availability** | 99.5% (single-instance), 99.9% (multi-instance โ€” *butuh migrasi tracker DB*) | | **Resource startup** | Token initial login, retry 6ร—, fail-fast Fatal jika tidak berhasil | | **Graceful shutdown** | SIGTERM โ†’ `errgroup` cancel โ†’ wait-group worker โ†’ close DB & cache | | **Keamanan** | TLS pada API gov't, parameter binding SQL, payload BPJS dekripsi sebelum log, password DB & secret via env | | **Konfigurasi** | Viper YAML + overlay env var (`DATABASES_POSTGRES_HOST` dsb) | --- ## 5. Asumsi & Ketergantungan - API internal **`service-satusehat`** (port 8096 default, `internal_fhir_server_url`) sudah running dan menyediakan endpoint: - `/auth/login`, `/auth/refresh` โ€” token JWT untuk worker. - `/satusehat/reference/kfa/products` & `/products/{kfa_code}`. - `/satusehat/medication`, `/satusehat/imaging-study`, `/satusehat/service-request`, dst. โ€” proxy ke Kemenkes. - Database SIMRS read-only (atau read-write via koneksi `simrs`/`satudata`) berisi tabel sumber: `m_pasien`, transaksi pesan obat, dispensing, imaging study order. - Redis tersedia untuk cache + rate-limit; tanpa Redis sistem fallback ke NoOp cache (bukan failure). - Akses outbound ke `https://api-satusehat.kemkes.go.id` dan `https://apijkn.bpjs-kesehatan.go.id`. --- ## 6. Kriteria Sukses (Acceptance Criteria) ### v1 (Saat Ini โ€” Production untuk KFA saja) - [x] Service start dengan REST + gRPC + KFA Worker tanpa error. - [x] Master KFA tertarik secara periodik & ter-UPSERT lengkap ke 3 tabel. - [x] RBAC CRUD via REST berfungsi, cache hit > 80% setelah warm-up. - [x] Login worker SatuSehat berhasil retry 6ร— sebelum fatal. ### v1.x (Aktivasi Worker Klinis โ€” *Target Q3 2026*) - [ ] Worker `Medication`, `MedicationRequest`, `MedicationDispense` aktif โ†’ sync log tersimpan, mapping file harian terbentuk, FHIR ID kembali dan tersimpan. - [ ] Worker `ServiceRequest (Radiology)` & `ImagingStudy` aktif end-to-end. - [ ] Worker `Encounter` aktif sebagai prasyarat referensi resource lain. - [ ] Bug `satusehatClient = nil` di [main.go:235](cmd/api/main.go#L235) diselesaikan. ### v2 (Stateless & Event-Driven โ€” *Target Q4 2026 / Q1 2027*) - [ ] State tracker (`*.txt`) dipindah ke tabel `sync_tracker` atau Redis hash โ†’ mendukung multi-replica. - [ ] Kafka producer aktif (saat ini dikomentari di [main.go:111-113](cmd/api/main.go#L111-L113)). - [ ] PostgreSQL `LISTEN/NOTIFY` menggantikan sebagian polling worker. - [ ] Endpoint manual retry per `sync_log.id` untuk koreksi data. --- ## 7. Di Luar Cakupan (Out of Scope) - UI dashboard (akan dibangun di repository terpisah, frontend). - Sinkron pasien (`internal/worker/patient/migrator.go`) โ€” kode tersedia tapi tidak dipanggil dari `main.go` / `worker.go`; dianggap **deprecated** atau tugas migrasi *one-shot* manual. - 13 resource FHIR yang masih stub (lihat ยง3.1.A) โ€” diaktifkan setelah 6 resource utama stabil. - Integrasi BPJS aktif sebagai worker โ€” disediakan sebagai SDK, *use case* belum dikonfirmasi. --- ## 8. Risiko & Mitigasi | Risiko | Dampak | Mitigasi | | :--- | :--- | :--- | | Tracker `.txt` korup / race antara replica | Duplikasi atau data lost saat pengiriman | Pindah ke DB row-locked atau Redis `INCR` (Devplan Phase 2). | | Token SatuSehat expired di tengah burst | Worker stuck 401 | Sudah ditangani: refresh dengan mutex + fallback re-login ([auth.go](internal/worker/auth.go)). | | Rate limit Kemenkes berubah | Throughput turun | Setiap worker punya konstanta `rateLimitSleep` per resource โ€” bisa di-tune via config (rekomendasi). | | Password hardcoded di [patient/migrator.go:23-26](internal/worker/patient/migrator.go#L23-L26) | Keamanan jika diaktifkan | Hapus default atau wajibkan env var sebelum digunakan. | | Bug `nil` SatuSehat client di main | Panic jika worker yang membutuhkan diaktifkan | Inisialisasi via `satusehat.NewClient(cfg)` sebelum `worker.NewManager`. | --- *Disusun ulang: Tim Engineering GoPrint, 2026-05-13.*