Files
satusehat-worker/docs/PRD.md
T
2026-07-29 06:38:42 +00:00

170 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📑 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.*