update All feture and service
This commit is contained in:
No files matched your search
@@ -2,21 +2,53 @@ package questionnaireresponse
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"service/internal/satusehat/common"
|
||||
)
|
||||
|
||||
type QuestionnaireResponseRequest struct {
|
||||
OrganizationID string `json:"organization_id,omitempty"`
|
||||
QuestionnaireResponseID string `json:"questionnaire_response_id,omitempty"`
|
||||
QuestionnaireURL string `json:"questionnaire_url" binding:"required"` // e.g., "https://fhir.kemkes.go.id/Questionnaire/Q0007"
|
||||
Status string `json:"status" binding:"required,oneof=in-progress completed amended entered-in-error stopped"`
|
||||
Subject *common.ReferenceDTO `json:"subject" binding:"required"` // Patient
|
||||
Encounter *common.ReferenceDTO `json:"encounter,omitempty"`
|
||||
Authored *time.Time `json:"authored" binding:"required"`
|
||||
Author *common.ReferenceDTO `json:"author,omitempty"`
|
||||
Source *common.ReferenceDTO `json:"source,omitempty"`
|
||||
Items []map[string]interface{} `json:"item" binding:"required,min=1"` // Raw FHIR formatted items
|
||||
OrganizationID string `json:"organization_id,omitempty"`
|
||||
QuestionnaireResponseID string `json:"questionnaire_response_id,omitempty"`
|
||||
|
||||
QuestionnaireURL string `json:"questionnaire_url" binding:"required"` // e.g. "https://fhir.kemkes.go.id/Questionnaire/Q0007"
|
||||
Status string `json:"status" binding:"required,oneof=in-progress completed amended entered-in-error stopped"`
|
||||
|
||||
PatientID string `json:"patient_id" binding:"required"`
|
||||
PatientName string `json:"patient_name,omitempty"`
|
||||
|
||||
EncounterID string `json:"encounter_id,omitempty"`
|
||||
EncounterDisplay string `json:"encounter_display,omitempty"`
|
||||
|
||||
AuthorID string `json:"author_id,omitempty"`
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
AuthorType string `json:"author_type,omitempty"` // default: "Practitioner"
|
||||
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
SourceName string `json:"source_name,omitempty"`
|
||||
SourceType string `json:"source_type,omitempty"` // default: "Patient"
|
||||
|
||||
Authored *time.Time `json:"authored" binding:"required"`
|
||||
|
||||
Items []ItemDTO `json:"items" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
// ItemDTO represents one questionnaire answer in flat form.
|
||||
// Provide exactly one of the answer_* fields per item.
|
||||
type ItemDTO struct {
|
||||
LinkID string `json:"link_id" binding:"required"`
|
||||
Text string `json:"text,omitempty"`
|
||||
|
||||
AnswerBoolean *bool `json:"answer_boolean,omitempty"`
|
||||
AnswerString string `json:"answer_string,omitempty"`
|
||||
AnswerInteger *int `json:"answer_integer,omitempty"`
|
||||
AnswerDecimal *float64 `json:"answer_decimal,omitempty"`
|
||||
AnswerDate *time.Time `json:"answer_date,omitempty"`
|
||||
AnswerDateTime *time.Time `json:"answer_datetime,omitempty"`
|
||||
AnswerQuantityValue *float64 `json:"answer_quantity_value,omitempty"`
|
||||
AnswerQuantityUnit string `json:"answer_quantity_unit,omitempty"`
|
||||
AnswerQuantitySystem string `json:"answer_quantity_system,omitempty"`
|
||||
AnswerQuantityCode string `json:"answer_quantity_code,omitempty"`
|
||||
AnswerCodingSystem string `json:"answer_coding_system,omitempty"`
|
||||
AnswerCodingCode string `json:"answer_coding_code,omitempty"`
|
||||
AnswerCodingDisplay string `json:"answer_coding_display,omitempty"`
|
||||
}
|
||||
|
||||
type QuestionnaireResponsePatchRequest []map[string]interface{}
|
||||
@@ -1,7 +1,6 @@
|
||||
package questionnaireresponse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"service/internal/interfaces/satusehat"
|
||||
@@ -10,15 +9,9 @@ import (
|
||||
func MapRequestToFHIR(req QuestionnaireResponseRequest) satusehat.FHIRPayload {
|
||||
payload := satusehat.NewFHIRPayload("QuestionnaireResponse")
|
||||
|
||||
orgID := os.Getenv("SATUSEHAT_ORG_ID")
|
||||
if orgID == "" {
|
||||
orgID = req.OrganizationID
|
||||
}
|
||||
|
||||
if orgID != "" && req.QuestionnaireResponseID != "" {
|
||||
// Catatan: Identifier QuestionnaireResponse adalah single object, bukan array
|
||||
if req.OrganizationID != "" && req.QuestionnaireResponseID != "" {
|
||||
payload.Set("identifier", map[string]interface{}{
|
||||
"system": "http://sys-ids.kemkes.go.id/QuestionnaireResponse/" + orgID,
|
||||
"system": "http://sys-ids.kemkes.go.id/QuestionnaireResponse/" + req.OrganizationID,
|
||||
"value": req.QuestionnaireResponseID,
|
||||
})
|
||||
}
|
||||
@@ -30,18 +23,18 @@ func MapRequestToFHIR(req QuestionnaireResponseRequest) satusehat.FHIRPayload {
|
||||
payload.Set("status", req.Status)
|
||||
}
|
||||
|
||||
if req.Subject != nil {
|
||||
subject := map[string]interface{}{"reference": req.Subject.Reference}
|
||||
if req.Subject.Display != "" {
|
||||
subject["display"] = req.Subject.Display
|
||||
if req.PatientID != "" {
|
||||
subject := map[string]interface{}{"reference": "Patient/" + req.PatientID}
|
||||
if req.PatientName != "" {
|
||||
subject["display"] = req.PatientName
|
||||
}
|
||||
payload.Set("subject", subject)
|
||||
}
|
||||
|
||||
if req.Encounter != nil {
|
||||
encounter := map[string]interface{}{"reference": req.Encounter.Reference}
|
||||
if req.Encounter.Display != "" {
|
||||
encounter["display"] = req.Encounter.Display
|
||||
if req.EncounterID != "" {
|
||||
encounter := map[string]interface{}{"reference": "Encounter/" + req.EncounterID}
|
||||
if req.EncounterDisplay != "" {
|
||||
encounter["display"] = req.EncounterDisplay
|
||||
}
|
||||
payload.Set("encounter", encounter)
|
||||
}
|
||||
@@ -50,25 +43,90 @@ func MapRequestToFHIR(req QuestionnaireResponseRequest) satusehat.FHIRPayload {
|
||||
payload.Set("authored", req.Authored.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if req.Author != nil {
|
||||
author := map[string]interface{}{"reference": req.Author.Reference}
|
||||
if req.Author.Display != "" {
|
||||
author["display"] = req.Author.Display
|
||||
if req.AuthorID != "" {
|
||||
authorType := req.AuthorType
|
||||
if authorType == "" {
|
||||
authorType = "Practitioner"
|
||||
}
|
||||
author := map[string]interface{}{"reference": authorType + "/" + req.AuthorID}
|
||||
if req.AuthorName != "" {
|
||||
author["display"] = req.AuthorName
|
||||
}
|
||||
payload.Set("author", author)
|
||||
}
|
||||
|
||||
if req.Source != nil {
|
||||
source := map[string]interface{}{"reference": req.Source.Reference}
|
||||
if req.Source.Display != "" {
|
||||
source["display"] = req.Source.Display
|
||||
if req.SourceID != "" {
|
||||
sourceType := req.SourceType
|
||||
if sourceType == "" {
|
||||
sourceType = "Patient"
|
||||
}
|
||||
source := map[string]interface{}{"reference": sourceType + "/" + req.SourceID}
|
||||
if req.SourceName != "" {
|
||||
source["display"] = req.SourceName
|
||||
}
|
||||
payload.Set("source", source)
|
||||
}
|
||||
|
||||
if len(req.Items) > 0 {
|
||||
payload.Set("item", req.Items)
|
||||
items := make([]map[string]interface{}, 0, len(req.Items))
|
||||
for _, it := range req.Items {
|
||||
items = append(items, mapItem(it))
|
||||
}
|
||||
payload.Set("item", items)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func mapItem(it ItemDTO) map[string]interface{} {
|
||||
out := map[string]interface{}{"linkId": it.LinkID}
|
||||
if it.Text != "" {
|
||||
out["text"] = it.Text
|
||||
}
|
||||
if answer := mapAnswer(it); answer != nil {
|
||||
out["answer"] = []map[string]interface{}{answer}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mapAnswer(it ItemDTO) map[string]interface{} {
|
||||
switch {
|
||||
case it.AnswerBoolean != nil:
|
||||
return map[string]interface{}{"valueBoolean": *it.AnswerBoolean}
|
||||
case it.AnswerString != "":
|
||||
return map[string]interface{}{"valueString": it.AnswerString}
|
||||
case it.AnswerInteger != nil:
|
||||
return map[string]interface{}{"valueInteger": *it.AnswerInteger}
|
||||
case it.AnswerDecimal != nil:
|
||||
return map[string]interface{}{"valueDecimal": *it.AnswerDecimal}
|
||||
case it.AnswerDate != nil && !it.AnswerDate.IsZero():
|
||||
return map[string]interface{}{"valueDate": it.AnswerDate.Format("2006-01-02")}
|
||||
case it.AnswerDateTime != nil && !it.AnswerDateTime.IsZero():
|
||||
return map[string]interface{}{"valueDateTime": it.AnswerDateTime.Format(time.RFC3339)}
|
||||
case it.AnswerQuantityValue != nil:
|
||||
q := map[string]interface{}{"value": *it.AnswerQuantityValue}
|
||||
if it.AnswerQuantityUnit != "" {
|
||||
q["unit"] = it.AnswerQuantityUnit
|
||||
}
|
||||
if it.AnswerQuantitySystem != "" {
|
||||
q["system"] = it.AnswerQuantitySystem
|
||||
}
|
||||
if it.AnswerQuantityCode != "" {
|
||||
q["code"] = it.AnswerQuantityCode
|
||||
}
|
||||
return map[string]interface{}{"valueQuantity": q}
|
||||
case it.AnswerCodingCode != "" || it.AnswerCodingDisplay != "":
|
||||
coding := map[string]interface{}{}
|
||||
if it.AnswerCodingSystem != "" {
|
||||
coding["system"] = it.AnswerCodingSystem
|
||||
}
|
||||
if it.AnswerCodingCode != "" {
|
||||
coding["code"] = it.AnswerCodingCode
|
||||
}
|
||||
if it.AnswerCodingDisplay != "" {
|
||||
coding["display"] = it.AnswerCodingDisplay
|
||||
}
|
||||
return map[string]interface{}{"valueCoding": coding}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -26,12 +26,8 @@ func NewRepository(client satusehat.SatuSehatClient) Repository {
|
||||
return &repository{client: client}
|
||||
}
|
||||
|
||||
type hiddenCtx struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (r *repository) executeRequest(ctx context.Context, method, endpoint string, req interface{}) (*satusehat.FHIRResponse, error) {
|
||||
resp, err := r.client.DoRequest(hiddenCtx{ctx}, method, endpoint, req)
|
||||
resp, err := r.client.DoRequest(ctx, method, endpoint, req)
|
||||
if err != nil {
|
||||
return nil, errors.InternalError().Message("Failed to execute request to SatuSehat").Cause(err).Build()
|
||||
}
|
||||
|
||||
@@ -17,24 +17,33 @@ type Service interface {
|
||||
}
|
||||
|
||||
type service struct {
|
||||
repo Repository
|
||||
repo Repository
|
||||
orgID string
|
||||
}
|
||||
|
||||
func NewService(repo Repository) Service {
|
||||
return &service{repo: repo}
|
||||
func NewService(repo Repository, orgID string) Service {
|
||||
return &service{repo: repo, orgID: orgID}
|
||||
}
|
||||
|
||||
func (s *service) Create(ctx context.Context, req QuestionnaireResponseRequest) (*satusehat.FHIRResponse, error) {
|
||||
if req.OrganizationID == "" {
|
||||
req.OrganizationID = s.orgID
|
||||
}
|
||||
return s.repo.Create(ctx, MapRequestToFHIR(req))
|
||||
}
|
||||
|
||||
func (s *service) Update(ctx context.Context, id string, req QuestionnaireResponseRequest) (*satusehat.FHIRResponse, error) {
|
||||
if id == "" {
|
||||
return nil, errors.NewValidationError().Message("QuestionnaireResponse ID is required").Build()
|
||||
}
|
||||
if req.OrganizationID == "" {
|
||||
req.OrganizationID = s.orgID
|
||||
}
|
||||
payload := MapRequestToFHIR(req)
|
||||
payload.Set("id", id)
|
||||
return s.repo.Update(ctx, id, payload)
|
||||
}
|
||||
|
||||
func (s *service) Patch(ctx context.Context, id string, req QuestionnaireResponsePatchRequest) (*satusehat.FHIRResponse, error) {
|
||||
if id == "" {
|
||||
return nil, errors.NewValidationError().Message("QuestionnaireResponse ID is required").Build()
|
||||
@@ -44,12 +53,14 @@ func (s *service) Patch(ctx context.Context, id string, req QuestionnaireRespons
|
||||
}
|
||||
return s.repo.Patch(ctx, id, req)
|
||||
}
|
||||
|
||||
func (s *service) GetByID(ctx context.Context, id string) (*satusehat.FHIRResponse, error) {
|
||||
if id == "" {
|
||||
return nil, errors.NewValidationError().Message("QuestionnaireResponse ID is required").Build()
|
||||
}
|
||||
return s.repo.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *service) Search(ctx context.Context, queryParams url.Values) (*satusehat.FHIRResponse, error) {
|
||||
return s.repo.Search(ctx, queryParams)
|
||||
}
|
||||
Reference in New Issue
Block a user