Medication Update
This commit is contained in:
@@ -3,13 +3,16 @@ package imagingstudy
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"service/internal/infrastructure/database"
|
||||
"service/pkg/logger"
|
||||
"service/pkg/utils/query"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
GetNextPendingRequest(ctx context.Context) (*ImagingStudyData, error)
|
||||
UpdateSyncStatus(ctx context.Context, reqID string, status string, responsePayload string) error
|
||||
GetNextImagingStudy(ctx context.Context, lastCreatedAt time.Time) (*ImagingStudyDB, error)
|
||||
SaveSatuSehatID(ctx context.Context, sourceID string, fhirID string) error
|
||||
SaveSyncLog(ctx context.Context, logData SyncLog) error
|
||||
}
|
||||
|
||||
type repository struct {
|
||||
@@ -20,68 +23,173 @@ func NewRepository(dbManager database.Service) Repository {
|
||||
return &repository{dbManager: dbManager}
|
||||
}
|
||||
|
||||
// GetNextPendingRequest mengambil 1 data service request radiologi yang belum terkirim
|
||||
func (r *repository) GetNextPendingRequest(ctx context.Context) (*ImagingStudyData, error) {
|
||||
var data ImagingStudyData
|
||||
// GetNextImagingStudy mengambil data imaging study berikutnya untuk diproses.
|
||||
// Tracker `lastID` adalah primary key integer (`id`) dari tabel `data_service_requests`.
|
||||
// Data akan diambil mulai dari tanggal 2026-04-15 dan melompati `lastCreatedAt` yang sudah diproses.
|
||||
func (r *repository) GetNextImagingStudy(ctx context.Context, lastCreatedAt time.Time) (*ImagingStudyDB, error) {
|
||||
var data ImagingStudyDB
|
||||
|
||||
// 1. Ambil koneksi Postgres (Satu Data) - Sesuaikan nama dengan konfigurasi Anda
|
||||
pgDB, err := r.dbManager.GetDB("satudata")
|
||||
// 1. Inisialisasi Koneksi ke Database yang diperlukan
|
||||
satudataDB, err := r.dbManager.GetSQLXDB("satudata")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
risDB, err := r.dbManager.GetSQLXDB("ris")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ambil dari Postgres
|
||||
queryPG := `
|
||||
SELECT
|
||||
sr.id, sr."Entity_Id", sr."Encounter_Id", sr."No_register",
|
||||
kp."Nomor_satusehat_pasien", kp."Nama_lengkap"
|
||||
FROM public.data_service_requests sr
|
||||
JOIN public.data_kunjungan_pasien kp ON sr."Encounter_Id" = kp."IDXDAFTAR_satusehat"
|
||||
WHERE LOWER(sr."Order_from") = 'radiologi'
|
||||
AND (sr."Send_status" IS NULL OR sr."Send_status" = 'FAILED')
|
||||
ORDER BY sr."Created_at" ASC
|
||||
LIMIT 1
|
||||
`
|
||||
err = pgDB.QueryRowContext(ctx, queryPG).Scan(
|
||||
&data.RequestID, &data.ServiceRequestID, &data.EncounterID, &data.NoRegister,
|
||||
&data.PatientID, &data.PatientName,
|
||||
)
|
||||
if err != nil { // Bisa berupa sql.ErrNoRows jika data habis
|
||||
return nil, err
|
||||
// --- Langkah 1: Ambil data pemicu dari `satudata` ---
|
||||
qbSatuData := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
|
||||
|
||||
// Struct penampung sementara untuk mendapat foreign key ke db lain
|
||||
type BaseSatuData struct {
|
||||
ID string `db:"id"` // Perbaikan: Ubah ke string untuk menampung UUID
|
||||
ServiceRequestID sql.NullString `db:"service_request_id"`
|
||||
EncounterIHS sql.NullString `db:"encounter_ihs"`
|
||||
PatientIHS sql.NullString `db:"patient_ihs"`
|
||||
NoRegister sql.NullString `db:"No_register"`
|
||||
TanggalOrder sql.NullTime `db:"Tanggal_order"`
|
||||
NoMR sql.NullString `db:"nomr"`
|
||||
CreatedAt time.Time `db:"Created_at"`
|
||||
PractitionerIHS sql.NullString `db:"practitioner_ihs"`
|
||||
StartedDate sql.NullTime `db:"started_date"`
|
||||
}
|
||||
var baseData BaseSatuData
|
||||
|
||||
// Hardcode tanggal mulai
|
||||
startDate, _ := time.Parse("2006-01-02", "2026-01-01")
|
||||
|
||||
qSatuData := query.DynamicQuery{
|
||||
From: "public.data_service_requests",
|
||||
Aliases: "dsr",
|
||||
Fields: []query.SelectField{
|
||||
{Expression: `dsr.id`},
|
||||
{Expression: `dsr."Entity_Id"`, Alias: "service_request_id"},
|
||||
{Expression: `dsr."Encounter_Id"`, Alias: "encounter_ihs"},
|
||||
{Expression: `dsr."No_register"`},
|
||||
{Expression: `dsr."Tanggal_order"`},
|
||||
{Expression: `dsr."Occurrence_date_time"`, Alias: "started_date"},
|
||||
{Expression: `dsr."Requester_reference"`, Alias: "practitioner_ihs"},
|
||||
{Expression: `dsr."Created_at"`},
|
||||
{Expression: `dkp."Nomor_satusehat_pasien"`, Alias: "patient_ihs"},
|
||||
{Expression: `dkp."NOMR"`, Alias: "nomr"},
|
||||
},
|
||||
Joins: []query.Join{
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "public.data_kunjungan_pasien",
|
||||
Alias: "dkp",
|
||||
OnConditions: query.CreateAndFilterGroup([]query.DynamicFilter{
|
||||
query.CreateFilter(`dkp."IDXDAFTAR_satusehat"`, query.OpEqual, `dsr."Encounter_Id"`),
|
||||
}),
|
||||
},
|
||||
},
|
||||
Filters: []query.FilterGroup{
|
||||
{Filters: []query.DynamicFilter{
|
||||
query.CreateFilter(`dsr."Created_at"`, query.OpGreaterThan, lastCreatedAt),
|
||||
query.CreateFilter(`dsr."Tanggal_order"`, query.OpGreaterThanEqual, startDate),
|
||||
query.CreateFilter(`dsr."Entity_Id"`, query.OpNotNull, nil),
|
||||
}},
|
||||
},
|
||||
Sort: []query.SortField{query.CreateAscSort(`dsr."Created_at"`)},
|
||||
Limit: 1,
|
||||
}
|
||||
|
||||
// 2. Ambil koneksi MySQL (RIS) - Sesuaikan nama dengan konfigurasi Anda
|
||||
mySQLDB, err := r.dbManager.GetDB("simrs")
|
||||
if err == nil && data.NoRegister.Valid {
|
||||
queryMySQL := `SELECT modality, foto FROM periksa WHERE noregister = ? LIMIT 1`
|
||||
err = qbSatuData.ExecuteQueryRow(ctx, satudataDB, qSatuData, &baseData)
|
||||
if err != nil {
|
||||
return nil, err // Akan melempar sql.ErrNoRows jika tidak ada data baru
|
||||
}
|
||||
|
||||
var modality sql.NullString
|
||||
var foto sql.NullTime
|
||||
// Map data dari `satudata` ke struct utama
|
||||
// SourceID (UUID) digunakan untuk tracking, sedangkan ID (int64) adalah dari tabel log,
|
||||
// yang akan diisi saat menyimpan log.
|
||||
data.SourceID = baseData.ID
|
||||
data.CreatedAt = baseData.CreatedAt
|
||||
data.ID = 0 // ID dari tabel log akan diisi nanti, bukan dari query ini.
|
||||
data.ServiceRequestID = baseData.ServiceRequestID
|
||||
data.EncounterIHS = baseData.EncounterIHS
|
||||
data.PatientIHS = baseData.PatientIHS
|
||||
data.NoMR = baseData.NoMR
|
||||
data.PractitionerIHS = baseData.PractitionerIHS
|
||||
data.StartedDate = baseData.StartedDate
|
||||
|
||||
// Gunakan placeholder '?' jika MySQL, atau '$1' jika PostgreSQL
|
||||
err = mySQLDB.QueryRowContext(ctx, queryMySQL, data.NoRegister.String).Scan(&modality, &foto)
|
||||
if err == nil {
|
||||
data.Modality = modality
|
||||
data.StartedDate = foto
|
||||
// --- Langkah 2: Ambil Accession Number dan Modality dari `ris` (MySQL) ---
|
||||
if baseData.NoRegister.Valid {
|
||||
qbRis := query.NewSQLQueryBuilder(query.DBTypeMySQL).SetSecurityOptions(false, 0)
|
||||
// PERBAIKAN: Secara eksplisit mengizinkan kolom yang akan digunakan dalam filter query RIS.
|
||||
// Ini untuk mengatasi masalah validasi pada query builder yang menganggap kolom 'noregister' tidak valid.
|
||||
qbRis.SetAllowedColumns([]string{
|
||||
"noregister", "DATE(daftar)",
|
||||
})
|
||||
|
||||
risFilters := []query.DynamicFilter{
|
||||
query.CreateFilter("noregister", query.OpEqual, baseData.NoRegister.String),
|
||||
}
|
||||
// Filter berdasarkan tanggal order jika ada, untuk mempersempit pencarian
|
||||
if baseData.TanggalOrder.Valid {
|
||||
risFilters = append(risFilters, query.CreateFilter("DATE(daftar)", query.OpEqual, baseData.TanggalOrder.Time.Format("2006-01-02")))
|
||||
}
|
||||
|
||||
qRis := query.DynamicQuery{
|
||||
From: "periksa",
|
||||
Fields: []query.SelectField{
|
||||
{Expression: "nofoto", Alias: "accession_number"},
|
||||
{Expression: "modality", Alias: "modality"},
|
||||
},
|
||||
Filters: []query.FilterGroup{
|
||||
{Filters: risFilters},
|
||||
},
|
||||
Limit: 1,
|
||||
}
|
||||
|
||||
// Mengeluarkan Log hasil SQL query yang di-generate
|
||||
sqlStr, args, errBuild := qbRis.BuildQuery(qRis)
|
||||
if errBuild == nil {
|
||||
logger.Default().Debug("[IMAGING STUDY REPO] RIS Query Generated", logger.String("query", sqlStr), logger.Any("args", args))
|
||||
} else {
|
||||
logger.Default().Error("[IMAGING STUDY REPO] Failed to build RIS Query", logger.ErrorField(errBuild))
|
||||
}
|
||||
|
||||
// PERBAIKAN: Jangan abaikan semua error. Log error jika bukan 'sql.ErrNoRows'.
|
||||
// Ini penting untuk mendeteksi masalah koneksi atau query ke database RIS.
|
||||
if errRis := qbRis.ExecuteQueryRow(ctx, risDB, qRis, &data); errRis != nil && errRis != sql.ErrNoRows {
|
||||
logger.Default().Warn("[IMAGING STUDY REPO] Gagal mengambil data dari RIS, melanjutkan tanpa data RIS.",
|
||||
logger.String("query", sqlStr), logger.Any("args", args), logger.ErrorField(errRis))
|
||||
}
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
// UpdateSyncStatus memperbarui status pengiriman di PostgreSQL
|
||||
func (r *repository) UpdateSyncStatus(ctx context.Context, reqID string, status string, responsePayload string) error {
|
||||
pgDB, err := r.dbManager.GetDB("satudata")
|
||||
func (r *repository) SaveSatuSehatID(ctx context.Context, sourceID string, fhirID string) error {
|
||||
// Perhatian: Karena ID dari data_service_requests adalah UUID,
|
||||
// kita tidak bisa lagi menggunakan `id` (int64) dari tabel log untuk melakukan UPDATE.
|
||||
// Fungsi ini perlu penyesuaian lebih lanjut jika Anda perlu menyimpan fhir_id kembali ke tabel `data_service_requests`.
|
||||
// Untuk sementara, fungsi ini tidak akan melakukan apa-apa untuk menghindari error.
|
||||
// return nil
|
||||
|
||||
// satudataDB, err := r.dbManager.GetSQLXDB("satudata")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
|
||||
// _, err = qb.ExecuteUpdate(ctx, satudataDB, "public.data_service_requests", query.UpdateData{Columns: []string{"fhir_id"}, Values: []interface{}{fhirID}}, []query.FilterGroup{query.CreateAndFilterGroup([]query.DynamicFilter{query.CreateEqualFilter("id", sourceID)})})
|
||||
// return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) SaveSyncLog(ctx context.Context, logData SyncLog) error {
|
||||
satudataDB, err := r.dbManager.GetSQLXDB("satudata")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `
|
||||
UPDATE public.data_service_requests
|
||||
SET "Send_status" = $1, "Send_response" = $2, "Updated_at" = NOW()
|
||||
WHERE id = $3
|
||||
`
|
||||
|
||||
_, err = pgDB.ExecContext(ctx, query, status, responsePayload, reqID)
|
||||
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
|
||||
insertData := query.InsertData{
|
||||
Columns: []string{"imaging_id", "fhir_id", "request_payload", "response_payload", "status", "error_message"},
|
||||
Values: []interface{}{logData.ImagingStudyID, logData.SatuSehatID, logData.RequestPayload, logData.ResponsePayload, logData.Status, logData.ErrorMessage},
|
||||
}
|
||||
_, err = qb.ExecuteUpsert(ctx, satudataDB, "public.log_satusehat_imagingstudy", insertData, []string{"imaging_id"}, []string{"fhir_id", "request_payload", "response_payload", "status", "error_message"})
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user