170 lines
10 KiB
Markdown
170 lines
10 KiB
Markdown
# 📑 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.*
|