Pengambilan Data KFA Pengiriman data medication dan service request

This commit is contained in:
meninjar
2026-05-04 03:24:01 +00:00
parent 24d25aa14a
commit e8a0642291
80 changed files with 1718354 additions and 263 deletions

No files matched your search

@@ -1 +1 @@
3384484
3578722
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
+49 -12
View File
@@ -18,7 +18,8 @@ import (
)
const trackerFile = "internal/worker/satusehat/medication/last_medication_id.txt"
const mappingFile = "internal/worker/satusehat/medication/medication_mapping.txt"
const mappingBaseDir = "internal/worker/satusehat/medication/mapperdata"
const rateLimitSleep = 60 * time.Second
type Config struct {
DBManager database.Service
@@ -98,7 +99,17 @@ func (w *worker) Run(ctx context.Context) {
internalPayload := MapToInternalAPI(med, w.cfg.OrganizationID, kfaDisplay, formCode, formDisplay)
fhirID, respBody, status, errMsg, shouldRetry := w.sendToInternalAPI(ctx, internalPayload, med.IdxPesanObat)
fhirID, respBody, status, errMsg, shouldRetry, rateLimited := w.sendToInternalAPI(ctx, internalPayload, med.IdxPesanObat)
if rateLimited {
logger.Default().Warn("[MEDICATION WORKER] ⏸️ Rate limit dari Satu Sehat - jeda dan akan retry dokumen yang sama",
logger.Int64("idxpesanobat", med.IdxPesanObat),
logger.String("response", truncate(string(respBody), 300)),
logger.String("sleep", rateLimitSleep.String()),
)
time.Sleep(rateLimitSleep)
continue
}
// Menyimpan payload request dan response ke dalam log
logger.Default().Info("[MEDICATION WORKER] Detail Transaksi",
@@ -132,7 +143,7 @@ func (w *worker) Run(ctx context.Context) {
}
}
func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]interface{}, idxPesanObat int64) (fhirID string, respBody []byte, status string, errMsg string, shouldRetry bool) {
func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]interface{}, idxPesanObat int64) (fhirID string, respBody []byte, status string, errMsg string, shouldRetry bool, rateLimited bool) {
reqURL := fmt.Sprintf("%s/satusehat/medication", strings.TrimRight(w.cfg.InternalBaseURL, "/"))
respBody, statusCode, err := w.apiClient.PostJSON(ctx, reqURL, w.tokenManager.GetAccessToken(), payload)
@@ -142,7 +153,7 @@ func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]inter
logger.Any("payload_sent", payload),
logger.ErrorField(err),
)
return "", nil, "FAILED", err.Error(), true
return "", nil, "FAILED", err.Error(), true, false
}
if statusCode == http.StatusUnauthorized {
@@ -151,7 +162,7 @@ func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]inter
newToken, refreshErr := w.tokenManager.ForceRefreshAndGetToken(ctx)
if refreshErr != nil {
logger.Default().Error("[MEDICATION WORKER] Gagal refresh token, akan mencoba lagi pada iterasi berikutnya.", logger.Int64("idxpesanobat", idxPesanObat), logger.ErrorField(refreshErr))
return "", respBody, "FAILED", refreshErr.Error(), true
return "", respBody, "FAILED", refreshErr.Error(), true, false
}
logger.Default().Info("[MEDICATION WORKER] Token berhasil di-refresh, mencoba ulang request...", logger.Int64("idxpesanobat", idxPesanObat))
@@ -161,10 +172,15 @@ func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]inter
logger.Int64("idxpesanobat", idxPesanObat),
logger.ErrorField(err),
)
return "", nil, "FAILED", err.Error(), true
return "", nil, "FAILED", err.Error(), true, false
}
}
if isRateLimitResponse(statusCode, respBody) {
errMsg = fmt.Sprintf("HTTP %d - rate limit Satu Sehat", statusCode)
return "", respBody, "RATE_LIMITED", errMsg, true, true
}
if statusCode != http.StatusOK && statusCode != http.StatusCreated {
errMsg = fmt.Sprintf("HTTP %d", statusCode)
logger.Default().Error("[MEDICATION WORKER] ❌ Respons Error dari API Part 3",
@@ -173,10 +189,10 @@ func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]inter
logger.String("response", truncate(string(respBody), 200)),
)
if statusCode >= 500 || statusCode == http.StatusTooManyRequests {
return "", respBody, "FAILED", errMsg, true
if statusCode >= 500 {
return "", respBody, "FAILED", errMsg, true, false
}
return "", respBody, "FAILED", errMsg, false
return "", respBody, "FAILED", errMsg, false, false
}
var result map[string]interface{}
@@ -194,7 +210,21 @@ func (w *worker) sendToInternalAPI(ctx context.Context, payload map[string]inter
logger.String("fhir_id", fhirID),
)
return fhirID, respBody, "SUCCESS", "", false
return fhirID, respBody, "SUCCESS", "", false, false
}
// isRateLimitResponse mendeteksi respons rate-limit dari Satu Sehat
// (HTTP 429 atau pesan body yang mengindikasikan limit pengiriman terlampaui).
func isRateLimitResponse(statusCode int, respBody []byte) bool {
if statusCode == http.StatusTooManyRequests {
return true
}
body := strings.ToLower(string(respBody))
return strings.Contains(body, "too many requests") ||
strings.Contains(body, "rate limit") ||
strings.Contains(body, "limit exceeded") ||
strings.Contains(body, "limit pengiriman") ||
strings.Contains(body, "quota exceeded")
}
func (w *worker) fetchKFADetails(ctx context.Context, kfaCode string) (kfaDisplay, formCode, formDisplay string) {
@@ -301,8 +331,15 @@ func (w *worker) ensureTrackerFileExists() {
}
func (w *worker) saveMapping(idxPesanObat int64, fhirID string) {
os.MkdirAll("internal/worker/satusehat/medication", 0755)
f, err := os.OpenFile(mappingFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
now := logger.LocalNow()
monthDir := filepath.Join(mappingBaseDir, now.Format("2006-01"))
if err := os.MkdirAll(monthDir, 0755); err != nil {
logger.Default().Error("[MEDICATION WORKER] Gagal membuat direktori mapping bulanan", logger.ErrorField(err))
return
}
mappingPath := filepath.Join(monthDir, now.Format("2006-01-02")+".txt")
f, err := os.OpenFile(mappingPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
logger.Default().Error("[MEDICATION WORKER] Gagal membuka file mapping", logger.ErrorField(err))
return