# 📑 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.*
