From 4f6a781341af81e32e131966daf61d6e9d947fe7 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Thu, 4 Sep 2025 11:32:11 +0700 Subject: [PATCH] feat (patient): add search --- internal/domain/main-entities/patient/dto.go | 4 +++ .../interface/main-handler/main-handler.go | 10 +++++- .../interface/main-handler/patient/handler.go | 7 ++++ .../use-case/main-use-case/patient/case.go | 25 ++++++++++++++ .../use-case/main-use-case/patient/lib.go | 34 +++++++++++++++++++ pkg/use-case-helper/use-case-helper.go | 25 ++++++++++++++ 6 files changed, 104 insertions(+), 1 deletion(-) diff --git a/internal/domain/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index c0eee96a..e3f5a622 100644 --- a/internal/domain/main-entities/patient/dto.go +++ b/internal/domain/main-entities/patient/dto.go @@ -55,6 +55,10 @@ type DeleteDto struct { Id uint `json:"id"` } +type SearchDto struct { + Search string `json:"search"` +} + type MetaDto struct { PageNumber int `json:"page_number"` PageSize int `json:"page_size"` diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 3b6c0c47..80aa4df4 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -105,6 +105,15 @@ func SetRoutes() http.Handler { "PATCH /{id}/block": user.O.Block, "PATCH /{id}/active": user.O.Active, }) + hk.GroupRoutes("/v1/patient", r, hk.MapHandlerFunc{ + "GET /": patient.O.GetList, + "GET /{id}": patient.O.GetDetail, + "POST /": patient.O.Create, + "PATCH /{id}": patient.O.Update, + "DELETE /{id}": patient.O.Delete, + "GET /by-identifier": patient.O.Search, + }) + hc.RegCrud(r, "/v1/person", person.O) hc.RegCrud(r, "/v1/person-address", personaddress.O) hc.RegCrud(r, "/v1/person-contact", personcontact.O) @@ -113,7 +122,6 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/nurse", nurse.O) hc.RegCrud(r, "/v1/nutritionist", nutritionist.O) hc.RegCrud(r, "/v1/pharmacist", pharmacist.O) - hc.RegCrud(r, "/v1/patient", patient.O) /******************** sources ********************/ hc.RegCrud(r, "/v1/division", division.O) diff --git a/internal/interface/main-handler/patient/handler.go b/internal/interface/main-handler/patient/handler.go index 57771b56..c392ee4c 100644 --- a/internal/interface/main-handler/patient/handler.go +++ b/internal/interface/main-handler/patient/handler.go @@ -69,3 +69,10 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { res, err := u.Delete(dto) rw.DataResponse(w, res, err) } + +func (obj myBase) Search(w http.ResponseWriter, r *http.Request) { + dto := e.SearchDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.Search(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/patient/case.go b/internal/use-case/main-use-case/patient/case.go index e942536b..b17c756a 100644 --- a/internal/use-case/main-use-case/patient/case.go +++ b/internal/use-case/main-use-case/patient/case.go @@ -334,3 +334,28 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func Search(input e.SearchDto) (*d.Data, error) { + var data *e.Patient + var err error + + event := pl.Event{ + Feature: "Search", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "search") + if data, err = SearchData(input, &event); err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} diff --git a/internal/use-case/main-use-case/patient/lib.go b/internal/use-case/main-use-case/patient/lib.go index 2a5d70e6..539f6843 100644 --- a/internal/use-case/main-use-case/patient/lib.go +++ b/internal/use-case/main-use-case/patient/lib.go @@ -163,3 +163,37 @@ func DeleteData(data *e.Patient, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func SearchData(input e.SearchDto, event *pl.Event, dbx ...*gorm.DB) (*e.Patient, error) { + pl.SetLogInfo(event, input, "started", "DBSearch") + + var patient e.Patient + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + // Preload associations for complete data + tx = tx.Preload(clause.Associations) + tx = tx.Preload("Person.Addresses") + tx = tx.Preload("Person.Contacts") + tx = tx.Preload("Person.Relatives") + + // Search by patient number OR person's resident identity number (exact match) + err := tx.Joins("JOIN \"Person\" ON \"Patient\".\"Person_Id\" = \"Person\".\"Id\""). + Where("\"Patient\".\"Number\" = ? OR \"Person\".\"ResidentIdentityNumber\" = ?", + input.Search, input.Search). + First(&patient).Error + + if err != nil { + if processedErr := pu.HandleSearchError(err, event, source, input.Search, patient); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &patient, nil +} diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go index beb369c1..a16a9ba2 100644 --- a/pkg/use-case-helper/use-case-helper.go +++ b/pkg/use-case-helper/use-case-helper.go @@ -52,6 +52,31 @@ func HandleReadError(err error, event *pl.Event, itemType string, id interface{} return pl.SetLogError(event, nil) } +func HandleSearchError(err error, event *pl.Event, itemType string, query interface{}, data any) error { + if err == nil { + pl.SetLogInfo(event, data, "complete") + return nil + } + + if errors.Is(err, gorm.ErrRecordNotFound) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-notFound", + Detail: fmt.Sprintf("%s with query '%v' not found", itemType, query), + Raw: err, + } + } else { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-search-fail", + Detail: fmt.Sprintf("%s search failed", itemType), + Raw: err, + } + } + + return pl.SetLogError(event, nil) +} + func GetMiddlewareErrorCode(mwType MWType) string { if strings.Contains(string(mwType), "Pre") { return "MW_PRE_FAILED"