From 7871998c98c687ab77d96d7bd44915e71230d042 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 15 Oct 2025 15:49:05 +0700 Subject: [PATCH] feat (reference): done --- .vscode/launch.json | 7 + cmd/bpjs-api/main.go | 2 +- .../domain/bpjs-entities/reference/dto.go | 22 ++++ .../domain/bpjs-entities/reference/entity.go | 23 ++++ internal/infra/bpjs/bpjs.go | 5 + .../interface/bpjs-handler/bpjs-handler.go | 13 ++ .../bpjs-handler/reference/handler.go | 123 ++++++++++++++++++ .../bpjs-handler/vclaim-sep/handler.go | 4 +- .../use-case/bpjs-plugin/reference/helper.go | 35 +++++ .../use-case/bpjs-plugin/reference/plugin.go | 37 ++++++ .../use-case/bpjs-use-case/reference/case.go | 56 ++++++++ .../bpjs-use-case/reference/helper.go | 22 ++++ .../use-case/bpjs-use-case/reference/lib.go | 1 + .../reference/middleware-runner.go | 42 ++++++ .../bpjs-use-case/reference/middleware.go | 12 ++ .../bpjs-use-case/reference/tycovar.go | 23 ++++ 16 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 internal/domain/bpjs-entities/reference/dto.go create mode 100644 internal/domain/bpjs-entities/reference/entity.go create mode 100644 internal/interface/bpjs-handler/reference/handler.go create mode 100644 internal/use-case/bpjs-plugin/reference/helper.go create mode 100644 internal/use-case/bpjs-plugin/reference/plugin.go create mode 100644 internal/use-case/bpjs-use-case/reference/case.go create mode 100644 internal/use-case/bpjs-use-case/reference/helper.go create mode 100644 internal/use-case/bpjs-use-case/reference/lib.go create mode 100644 internal/use-case/bpjs-use-case/reference/middleware-runner.go create mode 100644 internal/use-case/bpjs-use-case/reference/middleware.go create mode 100644 internal/use-case/bpjs-use-case/reference/tycovar.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 7098c42c..fddb0d29 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,6 +7,13 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/main-api" }, + { + "name": "Launch Package bpjs API", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/bpjs-api" + }, { "name": "Launch Package migratioon", "type": "go", diff --git a/cmd/bpjs-api/main.go b/cmd/bpjs-api/main.go index 74be3afd..e1cb2468 100644 --- a/cmd/bpjs-api/main.go +++ b/cmd/bpjs-api/main.go @@ -6,7 +6,7 @@ import ( l "github.com/karincake/apem/logger-zerolog" m "github.com/karincake/apem/ms-redis" - h "simrs-vx/internal/interface/main-handler" + h "simrs-vx/internal/interface/bpjs-handler" ) func main() { diff --git a/internal/domain/bpjs-entities/reference/dto.go b/internal/domain/bpjs-entities/reference/dto.go new file mode 100644 index 00000000..9be2ab5b --- /dev/null +++ b/internal/domain/bpjs-entities/reference/dto.go @@ -0,0 +1,22 @@ +package reference + +type ReadListDto struct { + ReferenceType ReferenceType `json:"-"` + PathValue1 string `json:"-"` + PathValue2 string `json:"-"` + PathValue3 string `json:"-"` +} + +type ReferenceType string + +const ( + RTProvince ReferenceType = "province" + RTDistrict ReferenceType = "district" + RTCities ReferenceType = "cities" + RTDiagnose ReferenceType = "diagnose" + RTDiagnosePrb ReferenceType = "diagnose-prb" + RTMedicinePrb ReferenceType = "medicine-prb" + RTUnit ReferenceType = "unit" + RTHealthcare ReferenceType = "healthcare" + RTDoctor ReferenceType = "doctor" +) diff --git a/internal/domain/bpjs-entities/reference/entity.go b/internal/domain/bpjs-entities/reference/entity.go new file mode 100644 index 00000000..bfcbb66e --- /dev/null +++ b/internal/domain/bpjs-entities/reference/entity.go @@ -0,0 +1,23 @@ +package reference + +type Response struct { + MetaData MetaData `json:"metaData"` + Response *CodeNameList `json:"response"` // pointer to handle possible null +} + +type MetaData struct { + Code string `json:"code"` + Message string `json:"message"` +} + +type CodeNameList struct { + List []CodeName `json:"list,omitempty"` + Diagnosa []CodeName `json:"diagnosa,omitempty"` + Poli []CodeName `json:"poli,omitempty"` + Faskes []CodeName `json:"faskes,omitempty"` +} + +type CodeName struct { + Kode string `json:"kode"` + Nama string `json:"nama"` +} diff --git a/internal/infra/bpjs/bpjs.go b/internal/infra/bpjs/bpjs.go index 68d1b877..36935c74 100644 --- a/internal/infra/bpjs/bpjs.go +++ b/internal/infra/bpjs/bpjs.go @@ -2,8 +2,13 @@ package bpjs import ( a "github.com/karincake/apem" + lo "github.com/karincake/apem/loggero" ) func SetConfig() { a.ParseSingleCfg(&O) + if O.BaseUrl == "" { + panic("config bpjs base url empty") + } + lo.I.Println("Bpjs config loaded, status: DONE!!") } diff --git a/internal/interface/bpjs-handler/bpjs-handler.go b/internal/interface/bpjs-handler/bpjs-handler.go index 7f34b7cc..5d5673df 100644 --- a/internal/interface/bpjs-handler/bpjs-handler.go +++ b/internal/interface/bpjs-handler/bpjs-handler.go @@ -4,6 +4,7 @@ import ( "net/http" /******************** main / transaction ********************/ + reference "simrs-vx/internal/interface/bpjs-handler/reference" vclaimsep "simrs-vx/internal/interface/bpjs-handler/vclaim-sep" vclaimsephist "simrs-vx/internal/interface/bpjs-handler/vclaim-sep-hist" vclaimsepprint "simrs-vx/internal/interface/bpjs-handler/vclaim-sep-print" @@ -58,6 +59,18 @@ func SetRoutes() http.Handler { "POST /": vclaimsepprint.O.Create, }) + hk.GroupRoutes("/v1/reference", r, hk.MapHandlerFunc{ + "GET /province": reference.GetListProvince, + "GET /regency/{provinceCode}": reference.GetListCities, + "GET /district/{regencyCode}": reference.GetListDistrict, + "GET /diagnose/{keyword}": reference.GetListDiagnose, + "GET /diagnose-prb": reference.GetListDiagnosePrb, + "GET /medicine-prb/{keyword}": reference.GetListMedicinePrb, + "GET /unit/{unitCode}": reference.GetListUnit, + "GET /healthcare/{healthcare}/{healthcareType}": reference.GetListHealthcare, + "GET /responsible-doctor/{serviceType}/{serviceDate}/{specialistCode}": reference.GetListDoctor, + }) + /******************** actor ********************/ /******************** sources ********************/ diff --git a/internal/interface/bpjs-handler/reference/handler.go b/internal/interface/bpjs-handler/reference/handler.go new file mode 100644 index 00000000..d452ebc4 --- /dev/null +++ b/internal/interface/bpjs-handler/reference/handler.go @@ -0,0 +1,123 @@ +package reference + +import ( + "net/http" + e "simrs-vx/internal/domain/bpjs-entities/reference" + u "simrs-vx/internal/use-case/bpjs-use-case/reference" + + d "github.com/karincake/dodol" + rw "github.com/karincake/risoles" +) + +func GetListProvince(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + dto.ReferenceType = e.RTProvince + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListCities(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue := rw.ValidateString(w, "provinceCode", r.PathValue("provinceCode")) + if pValue == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "provinceCode is required"}, nil) + } + dto.ReferenceType = e.RTCities + dto.PathValue1 = pValue + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListDistrict(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue := rw.ValidateString(w, "regencyCode", r.PathValue("regencyCode")) + if pValue == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "regencyCode is required"}, nil) + } + dto.ReferenceType = e.RTDistrict + dto.PathValue1 = pValue + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListDiagnose(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue := rw.ValidateString(w, "keyword", r.PathValue("keyword")) + if pValue == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "keyword is required"}, nil) + } + dto.ReferenceType = e.RTDiagnose + dto.PathValue1 = pValue + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListDiagnosePrb(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + dto.ReferenceType = e.RTDiagnosePrb + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListMedicinePrb(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue := rw.ValidateString(w, "keyword", r.PathValue("keyword")) + if pValue == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "keyword is required"}, nil) + } + dto.ReferenceType = e.RTMedicinePrb + dto.PathValue1 = pValue + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListUnit(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue := rw.ValidateString(w, "unitCode", r.PathValue("unitCode")) + if pValue == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "unitCode is required"}, nil) + } + dto.ReferenceType = e.RTUnit + dto.PathValue1 = pValue + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListHealthcare(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue1 := rw.ValidateString(w, "healthcare", r.PathValue("healthcare")) + if pValue1 == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "healthcare is required"}, nil) + } + pValue2 := rw.ValidateString(w, "healthcareType", r.PathValue("healthcareType")) + if pValue2 == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "healthcareType is required"}, nil) + } + dto.ReferenceType = e.RTHealthcare + dto.PathValue1 = pValue1 + dto.PathValue2 = pValue2 + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func GetListDoctor(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + pValue1 := rw.ValidateString(w, "serviceType", r.PathValue("serviceType")) + if pValue1 == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "serviceType is required"}, nil) + } + pValue2 := rw.ValidateString(w, "serviceDate", r.PathValue("serviceDate")) + if pValue2 == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "serviceDate is required"}, nil) + } + pValue3 := rw.ValidateString(w, "specialistCode", r.PathValue("specialistCode")) + if pValue3 == "" { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": "specialistCode is required"}, nil) + } + dto.PathValue1 = pValue1 + dto.PathValue2 = pValue2 + dto.PathValue3 = pValue3 + dto.ReferenceType = e.RTDoctor + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/bpjs-handler/vclaim-sep/handler.go b/internal/interface/bpjs-handler/vclaim-sep/handler.go index c90cf9fc..d40c76e9 100644 --- a/internal/interface/bpjs-handler/vclaim-sep/handler.go +++ b/internal/interface/bpjs-handler/vclaim-sep/handler.go @@ -48,7 +48,7 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { number := rw.ValidateString(w, "number", r.PathValue("number")) - if number <= "" { + if number != "" { return } @@ -63,7 +63,7 @@ func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { number := rw.ValidateString(w, "number", r.PathValue("number")) - if number <= "" { + if number != "" { return } dto := e.DeleteDto{} diff --git a/internal/use-case/bpjs-plugin/reference/helper.go b/internal/use-case/bpjs-plugin/reference/helper.go new file mode 100644 index 00000000..834ba4e5 --- /dev/null +++ b/internal/use-case/bpjs-plugin/reference/helper.go @@ -0,0 +1,35 @@ +package reference + +import ( + "fmt" + e "simrs-vx/internal/domain/bpjs-entities/reference" + ibpjs "simrs-vx/internal/infra/bpjs" +) + +func endpointMapper(input *e.ReadListDto) string { + switch input.ReferenceType { + case e.RTProvince: + return ibpjs.O.BaseUrl + "provinces" + case e.RTCities: + return ibpjs.O.BaseUrl + "cities/" + input.PathValue1 + case e.RTDistrict: + return ibpjs.O.BaseUrl + "districts/" + input.PathValue1 + case e.RTDiagnose: + return ibpjs.O.BaseUrl + "diagnosa/" + input.PathValue1 + case e.RTDiagnosePrb: + return ibpjs.O.BaseUrl + "diagnosaprb" + case e.RTMedicinePrb: + return ibpjs.O.BaseUrl + "obatprb/" + input.PathValue1 + case e.RTUnit: + if input.QParam == "" { + return ibpjs.O.BaseUrl + "referensi/poli" + } + return ibpjs.O.BaseUrl + "referensi/poli/" + input.QParam + case e.RTHealthcare: + return fmt.Sprintf("%sreferensi/faskes?faskes=%s&jenis-faskes=%s", ibpjs.O.BaseUrl, input.PathValue1, input.PathValue2) + case e.RTDoctor: + return fmt.Sprintf("%sreferensi/dokter-dpjp?jenis-pelayanan=%s&tgl-pelayanan=%s&kode-spesialis=%s", ibpjs.O.BaseUrl, input.PathValue1, input.PathValue2, input.PathValue3) + default: + return "" + } +} diff --git a/internal/use-case/bpjs-plugin/reference/plugin.go b/internal/use-case/bpjs-plugin/reference/plugin.go new file mode 100644 index 00000000..f4d4098e --- /dev/null +++ b/internal/use-case/bpjs-plugin/reference/plugin.go @@ -0,0 +1,37 @@ +package reference + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + e "simrs-vx/internal/domain/bpjs-entities/reference" + + "gorm.io/gorm" +) + +func ReadList(input *e.ReadListDto, data *e.Response, tx *gorm.DB) error { + endpoint := endpointMapper(input) + req, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if err := json.Unmarshal(body, &data); err != nil { + return fmt.Errorf("failed to parse response JSON: %w", err) + } + + return nil +} diff --git a/internal/use-case/bpjs-use-case/reference/case.go b/internal/use-case/bpjs-use-case/reference/case.go new file mode 100644 index 00000000..d753a955 --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/case.go @@ -0,0 +1,56 @@ +package reference + +import ( + e "simrs-vx/internal/domain/bpjs-entities/reference" + // evsh "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-hist" + + dg "github.com/karincake/apem/db-gorm-pg" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "reference" + +func ReadList(input e.ReadListDto) (*e.Response, error) { + var data e.Response + // var dataList []e.Response + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, &data); err != nil { + return err + } + + // if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + // return err + // } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, &data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &data, nil +} diff --git a/internal/use-case/bpjs-use-case/reference/helper.go b/internal/use-case/bpjs-use-case/reference/helper.go new file mode 100644 index 00000000..5f664320 --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package reference + +// import ( +// e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep" +// ) + +// func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.VclaimSep) { +// var inputSrc *e.CreateDto +// if inputT, ok := any(input).(*e.CreateDto); ok { +// inputSrc = inputT +// } else { +// inputTemp := any(input).(*e.UpdateDto) +// inputSrc = &inputTemp.CreateDto +// } + +// data.Encounter_Id = inputSrc.Encounter_Id +// data.Number = inputSrc.Number +// } diff --git a/internal/use-case/bpjs-use-case/reference/lib.go b/internal/use-case/bpjs-use-case/reference/lib.go new file mode 100644 index 00000000..bccaf5df --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/lib.go @@ -0,0 +1 @@ +package reference diff --git a/internal/use-case/bpjs-use-case/reference/middleware-runner.go b/internal/use-case/bpjs-use-case/reference/middleware-runner.go new file mode 100644 index 00000000..0d13c083 --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/middleware-runner.go @@ -0,0 +1,42 @@ +package reference + +import ( + e "simrs-vx/internal/domain/bpjs-entities/reference" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Response) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/bpjs-use-case/reference/middleware.go b/internal/use-case/bpjs-use-case/reference/middleware.go new file mode 100644 index 00000000..f7680ffd --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/middleware.go @@ -0,0 +1,12 @@ +package reference + +import ( + pr "simrs-vx/internal/use-case/bpjs-plugin/reference" +) + +func init() { + readListPreMw = append(readListPreMw, + readListMw{Name: "readList-reference", Func: pr.ReadList}, + ) + +} diff --git a/internal/use-case/bpjs-use-case/reference/tycovar.go b/internal/use-case/bpjs-use-case/reference/tycovar.go new file mode 100644 index 00000000..51df4160 --- /dev/null +++ b/internal/use-case/bpjs-use-case/reference/tycovar.go @@ -0,0 +1,23 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package reference + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/bpjs-entities/reference" +) + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Response, tx *gorm.DB) error +} + +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // ..