From 46407cdf572a9ff40160bf580d1fe23b3773d481 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 14 Nov 2025 14:18:10 +0700 Subject: [PATCH 01/23] sync unit on going --- .../simgos-sync-use-case/unit/case.go | 255 ++++++++++++++++++ .../simgos-sync-use-case/unit/helper.go | 60 +++++ .../use-case/simgos-sync-use-case/unit/lib.go | 184 +++++++++++++ .../unit/middleware-runner.go | 104 +++++++ .../simgos-sync-use-case/unit/middleware.go | 9 + .../simgos-sync-use-case/unit/tycovar.go | 44 +++ 6 files changed, 656 insertions(+) create mode 100644 internal/use-case/simgos-sync-use-case/unit/case.go create mode 100644 internal/use-case/simgos-sync-use-case/unit/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/unit/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/unit/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/unit/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/unit/tycovar.go diff --git a/internal/use-case/simgos-sync-use-case/unit/case.go b/internal/use-case/simgos-sync-use-case/unit/case.go new file mode 100644 index 00000000..bc14a72d --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/case.go @@ -0,0 +1,255 @@ +package unit + +import ( + "errors" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/log" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" +) + +const source = "installation" + +func Create(input e.CreateDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + ) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // STEP 1: Insert to simgos + sgData, err := CreateSimgosData(input, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 2: Insert to Link + if err = CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 3: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() + return nil, err + } + + if err = txSync.Commit().Error; err != nil { + // STEP 4: Rollback Partial + go func() { + _ = DeleteSimgosData(sgData, &event) + }() + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + // InsertSimxLog + if err := CreateLogData(input, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MInstalasi + err error + ) + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + // STEP 1: Get Installation Link + syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) + if errGetLink != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + if errGetLink != nil && errors.Is(errGetLink, gorm.ErrRecordNotFound) { + // STEP 2.1: Insert to simgos + sgData, err = CreateSimgosData(input.CreateDto, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 2.2: Insert to Link + simxId := uint(*input.Id) + if err = CreateLinkData(simxId, sgData.No_Instalasi, &event, txSync); err != nil { + txSimgos.Rollback() + return nil, err + } + } else { + // Step 3.1: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err + } + } + + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() + return nil, err + } + + if errGetLink != nil { + if err = txSync.Commit().Error; err != nil { + // STEP 5.1: Rollback Partial + go func() { + _ = DeleteSimgosData(sgData, &event) + }() + return nil, err + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + }, nil +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MInstalasi + err error + ) + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + // STEP 1: Get Installation Link + syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) + if errGetLink != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + if errGetLink == nil { + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + // STEP 3: Delete M_Instalation Simgos + err = DeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() + return nil, err + } + + if err = txSync.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + inputRollback := e.UpdateDto{ + CreateDto: e.CreateDto{ + Name: sgData.Nama_Instalasi, + }, + } + _ = UpdateSimgosData(inputRollback, syncLink, &event) + }() + return nil, err + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/unit/helper.go b/internal/use-case/simgos-sync-use-case/unit/helper.go new file mode 100644 index 00000000..53c518e2 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/helper.go @@ -0,0 +1,60 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package unit + +import ( + "encoding/json" + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/installation" + + esimgos "simrs-vx/internal/domain/simgos-entities/unit" + esync "simrs-vx/internal/domain/sync-entities/installation" + esyncLog "simrs-vx/internal/domain/sync-entities/log" +) + +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Nama_Instalasi = inputSrc.Name + data.Status_Rawat_Inap = 0 + data.St_Aktif = 1 + + return +} + +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.InstallationSimxLog) { + // encode to JSON + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") + jsonString := string(jsonData) + + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + + data.Value = &jsonString + data.Date = &now + data.Status = status + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.InstallationLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return +} diff --git a/internal/use-case/simgos-sync-use-case/unit/lib.go b/internal/use-case/simgos-sync-use-case/unit/lib.go new file mode 100644 index 00000000..091082f1 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/lib.go @@ -0,0 +1,184 @@ +package unit + +import ( + esynclog "simrs-vx/internal/domain/sync-entities/log" + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/installation" +) + +var now = time.Now() + +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MInstalasi, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := setDataSimgos(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MInstalasi, error) { + pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") + data := esimgos.MInstalasi{} + + var tx = dg.IS["simrs"] + + if err := tx. + Where("\"no_instalasi\" = ?", simgosId). + First(&data).Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.No_Instalasi = dataSimgos.Simgos_Id + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + data.St_Aktif = 0 + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.InstallationLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.InstallationLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/unit/middleware-runner.go b/internal/use-case/simgos-sync-use-case/unit/middleware-runner.go new file mode 100644 index 00000000..64426eb9 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/middleware-runner.go @@ -0,0 +1,104 @@ +package unit + +import ( + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/unit" +) + +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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Unit) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Unit) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) 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/simgos-sync-use-case/unit/middleware.go b/internal/use-case/simgos-sync-use-case/unit/middleware.go new file mode 100644 index 00000000..bac48f4d --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/middleware.go @@ -0,0 +1,9 @@ +package unit + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/unit/tycovar.go b/internal/use-case/simgos-sync-use-case/unit/tycovar.go new file mode 100644 index 00000000..e1a7c69f --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/tycovar.go @@ -0,0 +1,44 @@ +/* +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 unit + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/unit" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Unit, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Unit, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Unit, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From ebb3500d34e359ea45e28efc4586eb2c0bca6a0e Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 17 Nov 2025 11:05:21 +0700 Subject: [PATCH 02/23] unit finish --- internal/domain/main-entities/unit/dto.go | 1 + .../simgos-sync-handler.go | 8 + .../simgos-sync-handler/unit/handler.go | 66 ++++++++ .../main-use-case/installation/middleware.go | 6 +- internal/use-case/main-use-case/unit/case.go | 120 +++++++------- .../main-use-case/unit/middleware-runner.go | 38 +++-- .../use-case/main-use-case/unit/middleware.go | 23 ++- .../use-case/main-use-case/unit/tycovar.go | 31 +++- .../simgos-sync-plugin/unit/plugin.go | 153 ++++++++++++++++++ .../simgos-sync-use-case/installation/case.go | 127 +++++---------- .../simgos-sync-use-case/installation/lib.go | 42 ++++- .../simgos-sync-use-case/unit/case.go | 141 ++++++---------- .../simgos-sync-use-case/unit/helper.go | 15 +- .../use-case/simgos-sync-use-case/unit/lib.go | 72 +++++---- 14 files changed, 528 insertions(+), 315 deletions(-) create mode 100644 internal/interface/simgos-sync-handler/unit/handler.go create mode 100644 internal/use-case/simgos-sync-plugin/unit/plugin.go diff --git a/internal/domain/main-entities/unit/dto.go b/internal/domain/main-entities/unit/dto.go index f272ecec..7b071a84 100644 --- a/internal/domain/main-entities/unit/dto.go +++ b/internal/domain/main-entities/unit/dto.go @@ -7,6 +7,7 @@ import ( ) type CreateDto struct { + Id *uint `json:"id"` Installation_Code *string `json:"installation_code"` Code string `json:"code" validate:"maxLength=10"` Name string `json:"name" validate:"maxLength=50"` diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index aebcc9bb..3b1908a9 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -19,6 +19,7 @@ import ( /******************** internal ********************/ "simrs-vx/internal/interface/main-handler/home" installation "simrs-vx/internal/interface/simgos-sync-handler/installation" + unit "simrs-vx/internal/interface/simgos-sync-handler/unit" ) func SetRoutes() http.Handler { @@ -42,5 +43,12 @@ func SetRoutes() http.Handler { "DELETE /{id}": installation.O.Delete, }) + hk.GroupRoutes(prefix+"/v1/unit", r, hk.MapHandlerFunc{ + "POST /": unit.O.Create, + "POST /log": unit.O.CreateLog, + "PATCH /{id}": unit.O.Update, + "DELETE /{id}": unit.O.Delete, + }) + return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/interface/simgos-sync-handler/unit/handler.go b/internal/interface/simgos-sync-handler/unit/handler.go new file mode 100644 index 00000000..b1cac1c5 --- /dev/null +++ b/internal/interface/simgos-sync-handler/unit/handler.go @@ -0,0 +1,66 @@ +package unit + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/unit" + esync "simrs-vx/internal/domain/sync-entities/log" + u "simrs-vx/internal/use-case/simgos-sync-use-case/unit" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.CreateSimxLog(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + val := uint16(id) + dto.Id = &val + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + val := uint16(id) + dto.Id = &val + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index dd4b0b42..b8dfa4c8 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -7,12 +7,10 @@ import ( // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "create-installation", Func: plugin.Create}, - ) + createMw{Name: "create-installation", Func: plugin.Create}) createSimxLogMw = append(createSimxLogMw, - createLogMw{Name: "create-log", Func: plugin.CreateLog}, - ) + createLogMw{Name: "create-log", Func: plugin.CreateLog}) updatePreMw = append(updatePreMw, updateMw{Name: "update-installation", Func: plugin.Update}) diff --git a/internal/use-case/main-use-case/unit/case.go b/internal/use-case/main-use-case/unit/case.go index 650c4511..75b1c4ab 100644 --- a/internal/use-case/main-use-case/unit/case.go +++ b/internal/use-case/main-use-case/unit/case.go @@ -2,6 +2,8 @@ package unit import ( e "simrs-vx/internal/domain/main-entities/unit" + erc "simrs-vx/internal/domain/references/common" + esync "simrs-vx/internal/domain/sync-entities/log" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -25,36 +27,32 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData + id := uint(data.Id) + input.Id = &id } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -66,7 +64,6 @@ func Create(input e.CreateDto) (*d.Data, error) { } func ReadList(input e.ReadListDto) (*d.Data, error) { - var data *e.Unit var dataList []e.Unit var metaList *e.MetaDto var err error @@ -80,23 +77,9 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { 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 }) @@ -131,23 +114,9 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { - return err - } - if data, err = ReadDetailData(input, &event, tx); err != nil { return err } - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { - return err - } - return nil }) @@ -177,6 +146,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -184,32 +154,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := UpdateData(input, data, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(updatePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -233,6 +197,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -240,30 +205,26 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := DeleteData(data, &event, tx); err != nil { return err } mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -274,3 +235,32 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/unit/middleware-runner.go b/internal/use-case/main-use-case/unit/middleware-runner.go index fdce9d02..3c7fa241 100644 --- a/internal/use-case/main-use-case/unit/middleware-runner.go +++ b/internal/use-case/main-use-case/unit/middleware-runner.go @@ -2,6 +2,7 @@ package unit import ( e "simrs-vx/internal/domain/main-entities/unit" + esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -15,21 +16,36 @@ type middlewareRunner struct { } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ Event: event, - Tx: tx, } } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Unit) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -68,13 +84,13 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -83,13 +99,13 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []DeleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/unit/middleware.go b/internal/use-case/main-use-case/unit/middleware.go index bac48f4d..e3144448 100644 --- a/internal/use-case/main-use-case/unit/middleware.go +++ b/internal/use-case/main-use-case/unit/middleware.go @@ -1,9 +1,20 @@ package unit +import ( + plugin "simrs-vx/internal/use-case/simgos-sync-plugin/unit" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPreMw = append(createPreMw, + createMw{Name: "create-unit", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "update-unit", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "delete-unit", Func: plugin.Delete}) +} diff --git a/internal/use-case/main-use-case/unit/tycovar.go b/internal/use-case/main-use-case/unit/tycovar.go index e1a7c69f..bf717333 100644 --- a/internal/use-case/main-use-case/unit/tycovar.go +++ b/internal/use-case/main-use-case/unit/tycovar.go @@ -12,11 +12,27 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/unit" + elog "simrs-vx/internal/domain/sync-entities/log" ) type createMw struct { Name string - Func func(input *e.CreateDto, data *e.Unit, tx *gorm.DB) error + Func func(input *e.CreateDto) error +} + +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + +type updateMw struct { + Name string + Func func(input *e.UpdateDto) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error } type readListMw struct { @@ -29,16 +45,17 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Unit, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw diff --git a/internal/use-case/simgos-sync-plugin/unit/plugin.go b/internal/use-case/simgos-sync-plugin/unit/plugin.go new file mode 100644 index 00000000..8462f18d --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/unit/plugin.go @@ -0,0 +1,153 @@ +package unit + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/unit" + elog "simrs-vx/internal/domain/sync-entities/log" +) + +func Create(input *e.CreateDto) error { + endpoint := getPrefixEndpoint() + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Update(input *e.UpdateDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/unit", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index 6c3a79bd..755c65d3 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,7 +1,6 @@ package installation import ( - "errors" esimgos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/log" @@ -38,22 +37,23 @@ func Create(input e.CreateDto) (*d.Data, error) { } // STEP 2: Insert to Link - if err = CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { + syncLink, err := CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync) + if err != nil { txSimgos.Rollback() return nil, err } // STEP 3: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() + if err = txSync.Commit().Error; err != nil { txSync.Rollback() + txSimgos.Rollback() return nil, err } - if err = txSync.Commit().Error; err != nil { + if err = txSimgos.Commit().Error; err != nil { // STEP 4: Rollback Partial go func() { - _ = DeleteSimgosData(sgData, &event) + _ = DeleteLinkData(syncLink, &event) }() return nil, err } @@ -106,9 +106,7 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { func Update(input e.UpdateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MInstalasi err error ) @@ -121,51 +119,23 @@ func Update(input e.UpdateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "update") // STEP 1: Get Installation Link - syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) - if errGetLink != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err } - if errGetLink != nil && errors.Is(errGetLink, gorm.ErrRecordNotFound) { - // STEP 2.1: Insert to simgos - sgData, err = CreateSimgosData(input.CreateDto, &event, txSimgos) - if err != nil { - return nil, err - } - - // STEP 2.2: Insert to Link - simxId := uint(*input.Id) - if err = CreateLinkData(simxId, sgData.No_Instalasi, &event, txSync); err != nil { - txSimgos.Rollback() - return nil, err - } - } else { - // Step 3.1: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err } // STEP 4: Commit two trx if err = txSimgos.Commit().Error; err != nil { txSimgos.Rollback() - txSync.Rollback() return nil, err } - if errGetLink != nil { - if err = txSync.Commit().Error; err != nil { - // STEP 5.1: Rollback Partial - go func() { - _ = DeleteSimgosData(sgData, &event) - }() - return nil, err - } - } - pl.SetLogInfo(&event, nil, "complete") return &d.Data{ @@ -194,52 +164,43 @@ func Delete(input e.DeleteDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "delete") // STEP 1: Get Installation Link - syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) - if errGetLink != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err } - if errGetLink == nil { - // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) - if err != nil { - return nil, err - } + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } - // STEP 3: Delete M_Instalation Simgos - err = DeleteSimgosData(sgData, &event, txSimgos) - if err != nil { - return nil, err - } + // STEP 3: Delete M_Instalation Simgos + err = SoftDeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - txSync.Rollback() - return nil, err - } + // STEP 4: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } - if err = txSync.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - inputRollback := e.UpdateDto{ - CreateDto: e.CreateDto{ - Name: sgData.Nama_Instalasi, - }, - } - _ = UpdateSimgosData(inputRollback, syncLink, &event) - }() - return nil, err - } + if err = txSimgos.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.No_Instalasi, &event) + }() + return nil, err } pl.SetLogInfo(&event, nil, "complete") diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index 3e7ca854..77140553 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -4,6 +4,7 @@ import ( esynclog "simrs-vx/internal/domain/sync-entities/log" plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "time" dg "github.com/karincake/apem/db-gorm-pg" @@ -45,7 +46,9 @@ func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MInstalasi if err := tx. Where("\"no_instalasi\" = ?", simgosId). First(&data).Error; err != nil { - return nil, err + if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { + return nil, processedErr + } } pl.SetLogInfo(event, nil, "complete") @@ -79,7 +82,32 @@ func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.InstallationLink, eve return nil } -func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB) error { +func HardDeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx. + Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func SoftDeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBDelete") var tx *gorm.DB @@ -105,7 +133,7 @@ func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB return nil } -func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.InstallationLink, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") data := setDataSimxLink(simxId, simgosId) @@ -117,11 +145,11 @@ func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) err } if err := tx.Create(&data).Error; err != nil { - return plh.HandleCreateError(data, event, err) + return nil, plh.HandleCreateError(data, event, err) } pl.SetLogInfo(event, nil, "complete") - return nil + return &data, nil } func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.InstallationLink, error) { @@ -133,7 +161,9 @@ func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.InstallationLink if err := tx. Where("\"Simx_Id\" = ?", simxId). First(&data).Error; err != nil { - return nil, err + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } } pl.SetLogInfo(event, nil, "complete") diff --git a/internal/use-case/simgos-sync-use-case/unit/case.go b/internal/use-case/simgos-sync-use-case/unit/case.go index bc14a72d..bb87f1c6 100644 --- a/internal/use-case/simgos-sync-use-case/unit/case.go +++ b/internal/use-case/simgos-sync-use-case/unit/case.go @@ -1,21 +1,18 @@ package unit import ( - "errors" - esimgos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/log" + pl "simrs-vx/pkg/logger" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" - - pl "simrs-vx/pkg/logger" - "gorm.io/gorm" - e "simrs-vx/internal/domain/main-entities/installation" + e "simrs-vx/internal/domain/main-entities/unit" + esimgos "simrs-vx/internal/domain/simgos-entities/unit" + esync "simrs-vx/internal/domain/sync-entities/log" ) -const source = "installation" +const source = "unit" func Create(input e.CreateDto) (*d.Data, error) { var ( @@ -38,22 +35,23 @@ func Create(input e.CreateDto) (*d.Data, error) { } // STEP 2: Insert to Link - if err = CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { + syncLink, err := CreateLinkData(*input.Id, sgData.Kode, &event, txSync) + if err != nil { txSimgos.Rollback() return nil, err } // STEP 3: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() + if err = txSync.Commit().Error; err != nil { txSync.Rollback() + txSimgos.Rollback() return nil, err } - if err = txSync.Commit().Error; err != nil { + if err = txSimgos.Commit().Error; err != nil { // STEP 4: Rollback Partial go func() { - _ = DeleteSimgosData(sgData, &event) + _ = DeleteLinkData(syncLink, &event) }() return nil, err } @@ -106,9 +104,7 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { func Update(input e.UpdateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MInstalasi err error ) @@ -121,51 +117,23 @@ func Update(input e.UpdateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "update") // STEP 1: Get Installation Link - syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) - if errGetLink != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err } - if errGetLink != nil && errors.Is(errGetLink, gorm.ErrRecordNotFound) { - // STEP 2.1: Insert to simgos - sgData, err = CreateSimgosData(input.CreateDto, &event, txSimgos) - if err != nil { - return nil, err - } - - // STEP 2.2: Insert to Link - simxId := uint(*input.Id) - if err = CreateLinkData(simxId, sgData.No_Instalasi, &event, txSync); err != nil { - txSimgos.Rollback() - return nil, err - } - } else { - // Step 3.1: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err } // STEP 4: Commit two trx if err = txSimgos.Commit().Error; err != nil { txSimgos.Rollback() - txSync.Rollback() return nil, err } - if errGetLink != nil { - if err = txSync.Commit().Error; err != nil { - // STEP 5.1: Rollback Partial - go func() { - _ = DeleteSimgosData(sgData, &event) - }() - return nil, err - } - } - pl.SetLogInfo(&event, nil, "complete") return &d.Data{ @@ -181,7 +149,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { var ( txSync = dg.I.Begin() txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MInstalasi + sgData *esimgos.MPloy err error ) @@ -194,52 +162,43 @@ func Delete(input e.DeleteDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "delete") // STEP 1: Get Installation Link - syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) - if errGetLink != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err } - if errGetLink == nil { - // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) - if err != nil { - return nil, err - } + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } - // STEP 3: Delete M_Instalation Simgos - err = DeleteSimgosData(sgData, &event, txSimgos) - if err != nil { - return nil, err - } + // STEP 3: Delete M_Poly Simgos + err = HardDeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - txSync.Rollback() - return nil, err - } + // STEP 4: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } - if err = txSync.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - inputRollback := e.UpdateDto{ - CreateDto: e.CreateDto{ - Name: sgData.Nama_Instalasi, - }, - } - _ = UpdateSimgosData(inputRollback, syncLink, &event) - }() - return nil, err - } + if err = txSimgos.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.Kode, &event) + }() + return nil, err } pl.SetLogInfo(&event, nil, "complete") diff --git a/internal/use-case/simgos-sync-use-case/unit/helper.go b/internal/use-case/simgos-sync-use-case/unit/helper.go index 53c518e2..a499958c 100644 --- a/internal/use-case/simgos-sync-use-case/unit/helper.go +++ b/internal/use-case/simgos-sync-use-case/unit/helper.go @@ -8,14 +8,14 @@ import ( "encoding/json" erc "simrs-vx/internal/domain/references/common" - e "simrs-vx/internal/domain/main-entities/installation" + e "simrs-vx/internal/domain/main-entities/unit" esimgos "simrs-vx/internal/domain/simgos-entities/unit" - esync "simrs-vx/internal/domain/sync-entities/installation" esyncLog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/unit" ) -func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.) { +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MPloy) { var inputSrc *e.CreateDto if inputT, ok := any(input).(*e.CreateDto); ok { inputSrc = inputT @@ -24,14 +24,13 @@ func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.) { inputSrc = &inputTemp.CreateDto } - data.Nama_Instalasi = inputSrc.Name - data.Status_Rawat_Inap = 0 - data.St_Aktif = 1 + data.Nama = inputSrc.Name + data.Jenispoly = 0 return } -func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.InstallationSimxLog) { +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.UnitSimxLog) { // encode to JSON jsonData, _ := json.MarshalIndent(input.Payload, "", " ") jsonString := string(jsonData) @@ -53,7 +52,7 @@ func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.InstallationSimxLog) return } -func setDataSimxLink(simxId, simgosId uint) (data esync.InstallationLink) { +func setDataSimxLink(simxId, simgosId uint) (data esync.UnitLink) { data.Simx_Id = simxId data.Simgos_Id = simgosId return diff --git a/internal/use-case/simgos-sync-use-case/unit/lib.go b/internal/use-case/simgos-sync-use-case/unit/lib.go index 091082f1..720bd413 100644 --- a/internal/use-case/simgos-sync-use-case/unit/lib.go +++ b/internal/use-case/simgos-sync-use-case/unit/lib.go @@ -1,22 +1,23 @@ package unit import ( - esynclog "simrs-vx/internal/domain/sync-entities/log" plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "time" dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" - e "simrs-vx/internal/domain/main-entities/installation" - esimgos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/installation" + e "simrs-vx/internal/domain/main-entities/unit" + esimgos "simrs-vx/internal/domain/simgos-entities/unit" + esynclog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/unit" ) var now = time.Now() -func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MInstalasi, error) { +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPloy, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") data := setDataSimgos(&input) @@ -36,27 +37,29 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi return &data, nil } -func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MInstalasi, error) { +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPloy, error) { pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") - data := esimgos.MInstalasi{} + data := esimgos.MPloy{} var tx = dg.IS["simrs"] if err := tx. - Where("\"no_instalasi\" = ?", simgosId). + Where("\"kode\" = ?", simgosId). First(&data).Error; err != nil { - return nil, err + if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { + return nil, processedErr + } } pl.SetLogInfo(event, nil, "complete") return &data, nil } -func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { +func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.UnitLink, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, input, "started", "DBUpdate") data := setDataSimgos(&input) - data.No_Instalasi = dataSimgos.Simgos_Id + data.Kode = dataSimgos.Simgos_Id var tx *gorm.DB if len(dbx) > 0 { @@ -79,7 +82,7 @@ func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.InstallationLink, eve return nil } -func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB) error { +func HardDeleteSimgosData(data *esimgos.MPloy, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBDelete") var tx *gorm.DB @@ -89,9 +92,8 @@ func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB tx = dg.IS["simrs"] } - data.St_Aktif = 0 - - if err := tx.Save(&data).Error; err != nil { + if err := tx. + Delete(&data).Error; err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-delete-fail", @@ -105,7 +107,7 @@ func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event, dbx ...*gorm.DB return nil } -func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.UnitLink, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") data := setDataSimxLink(simxId, simgosId) @@ -117,30 +119,32 @@ func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) err } if err := tx.Create(&data).Error; err != nil { - return plh.HandleCreateError(data, event, err) - } - - pl.SetLogInfo(event, nil, "complete") - return nil -} - -func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.InstallationLink, error) { - pl.SetLogInfo(event, simxId, "started", "DBReadDetail") - data := esync.InstallationLink{} - - var tx = dg.I - - if err := tx. - Where("\"Simx_Id\" = ?", simxId). - First(&data).Error; err != nil { - return nil, err + return nil, plh.HandleCreateError(data, event, err) } pl.SetLogInfo(event, nil, "complete") return &data, nil } -func DeleteLinkData(data *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.UnitLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.UnitLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.UnitLink, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBDelete") var tx *gorm.DB From 34b79e6472a12dc31d8b8cce61849bdfea6043ab Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 17 Nov 2025 12:06:09 +0700 Subject: [PATCH 03/23] on going division --- .../simgos-sync-handler/division/handler.go | 66 ++++++ .../simgos-sync-handler.go | 20 +- .../simgos-sync-plugin/division/plugin.go | 153 +++++++++++++ .../simgos-sync-use-case/division/case.go | 214 ++++++++++++++++++ .../simgos-sync-use-case/division/helper.go | 58 +++++ .../simgos-sync-use-case/division/lib.go | 188 +++++++++++++++ .../division/middleware-runner.go | 104 +++++++++ .../division/middleware.go | 9 + .../simgos-sync-use-case/division/tycovar.go | 44 ++++ .../simgos-sync-use-case/installation/case.go | 5 +- .../installation/helper.go | 4 +- .../simgos-sync-use-case/installation/lib.go | 2 +- .../handler-crud-helper.go | 33 +++ pkg/handler-crud-helper/types.go | 7 + 14 files changed, 886 insertions(+), 21 deletions(-) create mode 100644 internal/interface/simgos-sync-handler/division/handler.go create mode 100644 internal/use-case/simgos-sync-plugin/division/plugin.go create mode 100644 internal/use-case/simgos-sync-use-case/division/case.go create mode 100644 internal/use-case/simgos-sync-use-case/division/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/division/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/division/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/division/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/division/tycovar.go diff --git a/internal/interface/simgos-sync-handler/division/handler.go b/internal/interface/simgos-sync-handler/division/handler.go new file mode 100644 index 00000000..0f87ca8a --- /dev/null +++ b/internal/interface/simgos-sync-handler/division/handler.go @@ -0,0 +1,66 @@ +package division + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/log" + u "simrs-vx/internal/use-case/simgos-sync-use-case/division" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.CreateSimxLog(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + val := uint16(id) + dto.Id = &val + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + val := uint16(id) + dto.Id = &val + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index 3b1908a9..590d7b9d 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -2,6 +2,7 @@ package simgossynchandler import ( "net/http" + hc "simrs-vx/pkg/handler-crud-helper" /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" @@ -14,10 +15,9 @@ import ( /******************** external ********************/ a "github.com/karincake/apem" - hk "github.com/karincake/hongkue" - /******************** internal ********************/ "simrs-vx/internal/interface/main-handler/home" + division "simrs-vx/internal/interface/simgos-sync-handler/division" installation "simrs-vx/internal/interface/simgos-sync-handler/installation" unit "simrs-vx/internal/interface/simgos-sync-handler/unit" ) @@ -36,19 +36,9 @@ func SetRoutes() http.Handler { /******************** Source ******************/ prefix := "/new-to-old" - hk.GroupRoutes(prefix+"/v1/installation", r, hk.MapHandlerFunc{ - "POST /": installation.O.Create, - "POST /log": installation.O.CreateLog, - "PATCH /{id}": installation.O.Update, - "DELETE /{id}": installation.O.Delete, - }) - - hk.GroupRoutes(prefix+"/v1/unit", r, hk.MapHandlerFunc{ - "POST /": unit.O.Create, - "POST /log": unit.O.CreateLog, - "PATCH /{id}": unit.O.Update, - "DELETE /{id}": unit.O.Delete, - }) + hc.SyncCrud(r, prefix+"/v1/installation", installation.O) + hc.SyncCrud(r, prefix+"/v1/unit", unit.O) + hc.SyncCrud(r, prefix+"/v1/division", division.O) return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/use-case/simgos-sync-plugin/division/plugin.go b/internal/use-case/simgos-sync-plugin/division/plugin.go new file mode 100644 index 00000000..06e54e31 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/division/plugin.go @@ -0,0 +1,153 @@ +package division + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/unit" + elog "simrs-vx/internal/domain/sync-entities/log" +) + +func Create(input *e.CreateDto) error { + endpoint := getPrefixEndpoint() + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Update(input *e.UpdateDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + 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() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/division", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-use-case/division/case.go b/internal/use-case/simgos-sync-use-case/division/case.go new file mode 100644 index 00000000..074bf2a7 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/case.go @@ -0,0 +1,214 @@ +package division + +import ( + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/log" + //esimgos "simrs-vx/internal/domain/simgos-entities/installation" +) + +const source = "division" + +func Create(input e.CreateDto) (*d.Data, error) { + //var ( + // txSync = dg.I.Begin() + // txSimgos = dg.IS["simrs"].Begin() + //) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // STEP 1: Insert to simgos + //sgData, err := CreateSimgosData(input, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 2: Insert to Link + //syncLink, err := CreateLinkData(*input.Id, sgData.Kode, &event, txSync) + //if err != nil { + // txSimgos.Rollback() + // return nil, err + //} + // + //// STEP 3: Commit two trx + //if err = txSync.Commit().Error; err != nil { + // txSync.Rollback() + // txSimgos.Rollback() + // return nil, err + //} + // + //if err = txSimgos.Commit().Error; err != nil { + // // STEP 4: Rollback Partial + // go func() { + // _ = DeleteLinkData(syncLink, &event) + // }() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + // InsertSimxLog + if err := CreateLogData(input, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + //var ( + // txSimgos = dg.IS["simrs"].Begin() + // err error + //) + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + //// STEP 1: Get Installation Link + //syncLink, err := ReadDetailLinkData(*input.Id, &event) + //if err != nil { + // return nil, err + //} + // + //// Step 2: Update Simgos + //err = UpdateSimgosData(input, syncLink, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 4: Commit two trx + //if err = txSimgos.Commit().Error; err != nil { + // txSimgos.Rollback() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + }, nil +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + //var ( + // txSync = dg.I.Begin() + // txSimgos = dg.IS["simrs"].Begin() + // sgData *esimgos.MPloy + // err error + //) + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + //// STEP 1: Get Installation Link + //syncLink, err := ReadDetailLinkData(*input.Id, &event) + //if err != nil { + // return nil, err + //} + // + //// STEP 2: Get Simgos + //sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + //if err != nil { + // return nil, err + //} + // + //// STEP 3: Delete M_Poly Simgos + //err = HardDeleteSimgosData(sgData, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 4: Delete Installation Link + //err = DeleteLinkData(syncLink, &event, txSync) + //if err != nil { + // txSimgos.Rollback() + // return nil, err + //} + // + //// STEP 4: Commit two trx + //if err = txSync.Commit().Error; err != nil { + // txSync.Rollback() + // txSimgos.Rollback() + // return nil, err + //} + // + //if err = txSimgos.Commit().Error; err != nil { + // // STEP 5: Rollback Partial + // go func() { + // _, _ = CreateLinkData(uint(*input.Id), sgData.Kode, &event) + // }() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/division/helper.go b/internal/use-case/simgos-sync-use-case/division/helper.go new file mode 100644 index 00000000..210d6e7c --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/helper.go @@ -0,0 +1,58 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package division + +import ( + "encoding/json" + erc "simrs-vx/internal/domain/references/common" + + //e "simrs-vx/internal/domain/main-entities/division" + // esimgos "simrs-vx/internal/domain/simgos-entities/divison" TBC + esync "simrs-vx/internal/domain/sync-entities/division" + esyncLog "simrs-vx/internal/domain/sync-entities/log" +) + +//func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MPloy) { +// var inputSrc *e.CreateDto +// if inputT, ok := any(input).(*e.CreateDto); ok { +// inputSrc = inputT +// } else { +// inputTemp := any(input).(*e.UpdateDto) +// inputSrc = &inputTemp.CreateDto +// } +// +// data.Nama = inputSrc.Name +// data.Jenispoly = 0 +// +// return +//} + +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.DivisionSimxLog) { + // encode to JSON + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") + jsonString := string(jsonData) + + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + + data.Value = &jsonString + data.Date = &now + data.Status = status + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.DivisionLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return +} diff --git a/internal/use-case/simgos-sync-use-case/division/lib.go b/internal/use-case/simgos-sync-use-case/division/lib.go new file mode 100644 index 00000000..ce13068d --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/lib.go @@ -0,0 +1,188 @@ +package division + +import ( + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + //e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/division" + //esimgos "simrs-vx/internal/domain/simgos-entities/unit" + esynclog "simrs-vx/internal/domain/sync-entities/log" +) + +var now = time.Now() + +//func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPloy, error) { +// pl.SetLogInfo(event, nil, "started", "DBCreate") +// +// data := setDataSimgos(&input) +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx.Create(&data).Error; err != nil { +// return nil, plh.HandleCreateError(input, event, err) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return &data, nil +//} + +//func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPloy, error) { +// pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") +// data := esimgos.MPloy{} +// +// var tx = dg.IS["simrs"] +// +// if err := tx. +// Where("\"kode\" = ?", simgosId). +// First(&data).Error; err != nil { +// if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { +// return nil, processedErr +// } +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return &data, nil +//} + +//func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.DivisionLink, event *pl.Event, dbx ...*gorm.DB) error { +// pl.SetLogInfo(event, input, "started", "DBUpdate") +// +// data := setDataSimgos(&input) +// data.Kode = dataSimgos.Simgos_Id +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx.Save(&data).Error; err != nil { +// event.Status = "failed" +// event.ErrInfo = pl.ErrorInfo{ +// Code: "data-update-fail", +// Detail: "Database update failed", +// Raw: err, +// } +// return pl.SetLogError(event, input) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return nil +//} + +//func HardDeleteSimgosData(data *esimgos.MPloy, event *pl.Event, dbx ...*gorm.DB) error { +// pl.SetLogInfo(event, data, "started", "DBDelete") +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx. +// Delete(&data).Error; err != nil { +// event.Status = "failed" +// event.ErrInfo = pl.ErrorInfo{ +// Code: "data-delete-fail", +// Detail: "Database delete failed", +// Raw: err, +// } +// return pl.SetLogError(event, data) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return nil +//} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.DivisionLink, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.DivisionLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.DivisionLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.DivisionLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/division/middleware-runner.go b/internal/use-case/simgos-sync-use-case/division/middleware-runner.go new file mode 100644 index 00000000..dad68114 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/middleware-runner.go @@ -0,0 +1,104 @@ +package division + +import ( + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/division" +) + +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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Division) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Division) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) 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/simgos-sync-use-case/division/middleware.go b/internal/use-case/simgos-sync-use-case/division/middleware.go new file mode 100644 index 00000000..f066ffb8 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/middleware.go @@ -0,0 +1,9 @@ +package division + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/division/tycovar.go b/internal/use-case/simgos-sync-use-case/division/tycovar.go new file mode 100644 index 00000000..c7733117 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/tycovar.go @@ -0,0 +1,44 @@ +/* +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 division + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/division" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Division, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Division, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Division, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index 755c65d3..c920fca1 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,9 +1,6 @@ package installation import ( - esimgos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/log" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -12,6 +9,8 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/log" ) const source = "installation" diff --git a/internal/use-case/simgos-sync-use-case/installation/helper.go b/internal/use-case/simgos-sync-use-case/installation/helper.go index 51316d79..aa33ef4b 100644 --- a/internal/use-case/simgos-sync-use-case/installation/helper.go +++ b/internal/use-case/simgos-sync-use-case/installation/helper.go @@ -10,7 +10,7 @@ import ( erc "simrs-vx/internal/domain/references/common" esimgos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/installation" - "simrs-vx/internal/domain/sync-entities/log" + esyncLog "simrs-vx/internal/domain/sync-entities/log" ) func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MInstalasi) { @@ -29,7 +29,7 @@ func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MInstal return } -func setDataSimxLog(input *log.SimxLogDto) (data esync.InstallationSimxLog) { +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.InstallationSimxLog) { // encode to JSON jsonData, _ := json.MarshalIndent(input.Payload, "", " ") jsonString := string(jsonData) diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index 77140553..9b057ece 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -1,7 +1,6 @@ package installation import ( - esynclog "simrs-vx/internal/domain/sync-entities/log" plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -13,6 +12,7 @@ import ( e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/installation" + esynclog "simrs-vx/internal/domain/sync-entities/log" ) var now = time.Now() diff --git a/pkg/handler-crud-helper/handler-crud-helper.go b/pkg/handler-crud-helper/handler-crud-helper.go index df475094..3003e812 100644 --- a/pkg/handler-crud-helper/handler-crud-helper.go +++ b/pkg/handler-crud-helper/handler-crud-helper.go @@ -74,3 +74,36 @@ func RegCrudByCode(r *http.ServeMux, path string, mwAndRouter ...any) { "DELETE /{code}": c.Delete, }) } + +func SyncCrud(r *http.ServeMux, path string, mwAndRouter ...any) { + sLength := len(mwAndRouter) + + mwCandidates := mwAndRouter[:sLength-1] + mwList := []hk.HandlerMw{} + for i := range mwCandidates { + // have to do it manually, since casting directly results unexpected result + myType := reflect.TypeOf(mwCandidates[i]) + if myType.String() != "func(http.Handler) http.Handler" { + panic("non middleware included as middleware") + } + mwList = append(mwList, mwCandidates[i].(func(http.Handler) http.Handler)) + + // if g, okHandler := mwCandidates[i].(func(http.Handler) http.Handler); !okHandler { + // panic("non middleware included") + // } else { + // mwList = append(mwList, g) + // } + } + + c, ok := mwAndRouter[sLength-1].(SyncCrudBase) + if !ok { + panic("non CrudBase used in the last paramter") + } + + hk.GroupRoutes(path, r, mwList, hk.MapHandlerFunc{ + "POST /": c.Create, + "POST /log": c.CreateLog, + "PATCH /{id}": c.Update, + "DELETE /{id}": c.Delete, + }) +} diff --git a/pkg/handler-crud-helper/types.go b/pkg/handler-crud-helper/types.go index e85f22fc..891af38e 100644 --- a/pkg/handler-crud-helper/types.go +++ b/pkg/handler-crud-helper/types.go @@ -9,3 +9,10 @@ type CrudBase interface { Update(w http.ResponseWriter, r *http.Request) Delete(w http.ResponseWriter, r *http.Request) } + +type SyncCrudBase interface { + Create(w http.ResponseWriter, r *http.Request) + CreateLog(w http.ResponseWriter, r *http.Request) + Update(w http.ResponseWriter, r *http.Request) + Delete(w http.ResponseWriter, r *http.Request) +} From 9cf28432491b7089b91a96584b20ccd71eae132e Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 14:33:49 +0700 Subject: [PATCH 04/23] add general consent template --- assets/docs/general-consent.html | 273 +++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 assets/docs/general-consent.html diff --git a/assets/docs/general-consent.html b/assets/docs/general-consent.html new file mode 100644 index 00000000..e311cb54 --- /dev/null +++ b/assets/docs/general-consent.html @@ -0,0 +1,273 @@ + + + + General Consent + + + + + + + + +
+ logo + +
+ PEMERINTAH PROVINSI JAWA TIMUR +
+
+ RUMAH SAKIT UMUM DAERAH Dr. SAIFUL ANWAR +
+
+ TERAKREDITASI KARS VERSI 2012 TINGKAT PARIPURNA +
+
+ Jl. Jaksa Agung Suprapto No. 2 MALANG 65111 +
+
Telp. (0341) 362101, Fax. (0341) 362110
+
Email: rsu-drsaifulanwar@jatimprov.go.id
+
Website: www.rsudsaifulanwar.jatimprov.go.id
+
+ logo +
+ +
+ +
+ FORMULIR PEMBERIAN INFORMASI DAN PERSETUJUAN UMUM +
+
+ (GENERAL CONSENT) +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1. + Hak dan Kewajiban sebagai pasien : + Dengan menandatangani dokumen ini saya mengakui bahwa pada proses + pendaftaran untuk mendapatkan perawatan di RSUD Dr. Saiful Anwar telah + mendapatkan informasi tentang hak dan kewajiban saya sebagai pasien + (melalui leaflet/banner dan atau petugas). Saya berhak mendapatkan + pelayanan kesehatan sesuai standar, mendapatkan informasi yang cukup + tentang keadaan kesehatan, rencana tindakan, manfaat, risiko, + alternatif tindakan, serta biaya yang akan timbul. Saya berkewajiban + memberikan informasi kesehatan yang jujur dan lengkap kepada tenaga + kesehatan, mematuhi aturan rumah sakit, serta memenuhi kewajiban + pembayaran sesuai ketentuan yang berlaku. +
2. + Persetujuan Pelayanan : + Saya menyetujui dan memberikan persetujuan untuk dirawat di RSUD Dr. + Saiful Anwar dan dengan ini saya meminta dan memberikan kuasa kepada + RSUD Dr. Saiful Anwar, dokter dan perawat serta tenaga kesehatan + lainnya untuk memberikan asuhan keperawatan, pemeriksaan fisik yang + dilakukan oleh dokter dan perawat dan melakukan prosedur diagnostik + radiologi dan/atau terapi dan tata laksana sesuai pertimbangan dokter + yang diperlukan atau disarankan pada perawatan saya. Hal ini mencakup + seluruh pemeriksaan dan prosedur diagnostik rutin termasuk X-ray, + pemberian dan/atau tindakan medis serta penyuntikan (intramuskular, + intravena dan prosedur invasif lainnya), produk farmasi dan + obat-obatan, pemasangan alat kesehatan (kecuali yang membutuhkan + persetujuan khusus/tertulis) dan pengambilan darah untuk pemeriksaan + laboratorium atau pemeriksaan patologi. +
3. + Akses Informasi Kesehatan : + Saya memberi kuasa kepada setiap dan seluruh orang yang merawat saya + untuk memeriksa dan/atau memberitahukan informasi kesehatan saya + kepada pemberi kesehatan lain yang turut merawat saya selama di rumah + sakit ini, sesuai kebutuhan pelayanan. +
4. + Rahasia Kedokteran : + Saya setuju RSUD Dr. Saiful Anwar Malang wajib menjamin kerahasiaan + informasi medis saya baik untuk kepentingan perawatan dan pengobatan, + pendidikan maupun penelitian, sesuai ketentuan yang berlaku. +
5. + Membuka Rahasia Kedokteran : + Saya setuju untuk membuka rahasia kedokteran terkait dengan kondisi + kesehatan, asuhan dan pengobatan yang saya terima kepada: +
a) Dokter dan tenaga kesehatan lain yang turut merawat/memberikan + asuhan kepada saya; +
b) Perusahaan asuransi kesehatan atau perusahaan lainnya atau pihak + lain yang menjamin pembiayaan saya; +
c) Anggota keluarga saya : + .......................................... +
Saya memahami bahwa pembukaan rahasia ini hanya sejauh yang + diperlukan untuk tujuan perawatan, pembiayaan atau administrasi yang + terkait. +
6. + Privasi : + Saya memberi kuasa kepada RSUD Dr. Saiful Anwar Malang untuk menjaga + privasi dan kerahasiaan penyakit saya selama dalam perawatan, serta + membatasi akses terhadap informasi yang tidak berkepentingan. +
7. + Barang Pribadi : + Saya setuju untuk tidak membawa barang-barang berharga yang tidak + diperlukan (seperti perhiasan, elektronik, dll) selama dalam perawatan + di RSUD Dr. Saiful Anwar. Jika saya tetap membawa dan terjadi + kehilangan, kerusakan atau pencurian, maka RSUD Dr. Saiful Anwar tidak + bertanggung jawab atas hal tersebut, kecuali bila ada perjanjian + tertulis yang menyatakan lain. +
8. + Pengajuan Keluhan : + Saya menyatakan bahwa saya telah menerima informasi tentang adanya + tata cara mengajukan dan mengatasi keluhan terkait pelayanan medik + yang diberikan terhadap diri saya. Saya setuju untuk mengikuti tata + cara pengajuan keluhan sesuai prosedur yang ada di rumah sakit. +
9. + Kewajiban Pembayaran : + Saya menyatakan setuju, baik sebagai wali ataupun sebagai pasien, + bahwa sesuai pertimbangan pelayanan yang diberikan kepada pasien, maka + saya wajib untuk membayar total biaya pelayanan sesuai acuan biaya dan + ketentuan RSUD Dr. Saiful Anwar Malang dengan jaminan atau pribadi. + Apabila asuransi kesehatan swasta atau program pemerintah menanggung + pembiayaan saya, saya memberi wewenang kepada rumah sakit untuk + memberi tagihan dari semua pelayanan dan tindakan medis yang + diberikan. Tanggungan Asuransi saya mungkin menyatakan bahwa sebagian + pembayaran tetap menjadi tanggung jawab pribadi saya atau tidak + ditanggung oleh asuransi, maka rumah sakit berwenang memberi tagihan + untuk biaya yang tidak ditanggung oleh asuransi dan saya bertanggung + jawab untuk membayarnya. Apabila saya tidak memberikan persetujuan, + atau dikemudian hari mencabut persetujuan saya untuk melepaskan + rahasia kedokteran saya kepada perusahaan asuransi yang saya tentukan, + maka saya pribadi bertanggung jawab untuk membayar semua pelayanan dan + tindakan medis dari RSUD Dr. Saiful Anwar Malang. +
10. + Rumah Sakit Pendidikan : + Saya mengetahui bahwa RSUD Dr. Saiful Anwar merupakan rumah sakit + pendidikan yang menjadi tempat praktik klinik bagi mahasiswa + kedokteran dan profesi-profesi kesehatan lainnya, karena itu mereka + mungkin berpartisipasi dan atau terlibat dalam perawatan saya dan saya + menyetujui bahwa mereka berpartisipasi dalam perawatan saya sepanjang + di bawah supervisi dokter penanggung jawab pasien (DPJP). +
11. + Selama Dalam Perawatan : + Selama dalam perawatan saya dan keluarga saya akan mematuhi ketentuan + untuk tidak mengambil, menyimpan, mengedarkan gambar/video dokumen dan + aktivitas pelayanan selama di RS tanpa seizin rumah sakit. +
12. + Penegasan Kepercayaan : + Melalui dokumen ini, saya menegaskan kembali bahwa saya mempercayakan + kepada semua tenaga kesehatan rumah sakit untuk memberikan perawatan, + diagnostik dan terapi kepada saya sebagai pasien rawat inap atau rawat + jalan atau Instalasi Gawat Darurat (IGD), termasuk semua pemeriksaan + penunjang yang dibutuhkan untuk pengobatan dan tindakan yang + diperlukan. +
+ +

+ +
+ Saya menyetujui setiap pernyataan dalam formulir ini dan menandatangani + tanpa paksaan. +
+ + + + + +
+ Malang, ............................................................ +
+ + + + + + + + +
+ Pasien/Keluarga


.......................................
Nama + terang +
+ Pemberi Informasi


.......................................
Nama + terang +
+ Saksi I


.......................................
Nama + terang +
+ Saksi II


.......................................
Nama + terang +
+ + From 05f54e4e341b285fcf3c1416ea73f4fdeb1c7c84 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 15:20:24 +0700 Subject: [PATCH 05/23] refactor (upload-file) --- .../main-entities/encounter-document/dto.go | 8 +- .../encounter-document/entity.go | 4 +- internal/domain/main-entities/patient/dto.go | 4 +- .../domain/references/encounter/encounter.go | 47 ++++++++ internal/domain/references/upload/upload.go | 53 --------- .../interface/main-handler/main-handler.go | 4 +- .../{upload => upload-file}/handler.go | 13 +-- .../validate-request.go | 6 +- .../main-use-case/encounter-document/case.go | 4 +- .../main-use-case/upload-file/case.go | 76 +++++++++++++ .../{upload => upload-file}/helper.go | 21 ++-- .../main-use-case/upload-file/tycovar.go} | 59 +++------- .../use-case/main-use-case/upload/case.go | 90 --------------- .../main-use-case/upload/middleware-runner.go | 104 ------------------ .../main-use-case/upload/middleware.go | 9 -- .../use-case/main-use-case/upload/tycovar.go | 44 -------- pkg/upload-helper/upload-helper.go | 2 +- 17 files changed, 169 insertions(+), 379 deletions(-) delete mode 100644 internal/domain/references/upload/upload.go rename internal/interface/main-handler/{upload => upload-file}/handler.go (84%) rename internal/interface/main-handler/{upload => upload-file}/validate-request.go (64%) create mode 100644 internal/use-case/main-use-case/upload-file/case.go rename internal/use-case/main-use-case/{upload => upload-file}/helper.go (89%) rename internal/{domain/main-entities/upload/dto.go => use-case/main-use-case/upload-file/tycovar.go} (53%) delete mode 100644 internal/use-case/main-use-case/upload/case.go delete mode 100644 internal/use-case/main-use-case/upload/middleware-runner.go delete mode 100644 internal/use-case/main-use-case/upload/middleware.go delete mode 100644 internal/use-case/main-use-case/upload/tycovar.go diff --git a/internal/domain/main-entities/encounter-document/dto.go b/internal/domain/main-entities/encounter-document/dto.go index 4bc76994..6381413b 100644 --- a/internal/domain/main-entities/encounter-document/dto.go +++ b/internal/domain/main-entities/encounter-document/dto.go @@ -1,7 +1,7 @@ package encounter_document import ( - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" // internal - domain - base-entities ecore "simrs-vx/internal/domain/base-entities/core" @@ -10,7 +10,7 @@ import ( type CreateDto struct { Encounter_Id *uint `json:"encounter_id"` - Type_Code eru.DocTypeCode `json:"type_code"` + Type_Code ere.DocTypeCode `json:"type_code"` Name string `json:"name"` FilePath string `json:"filePath"` Filename string `json:"-"` @@ -25,7 +25,7 @@ type ReadListDto struct { type FilterDto struct { Encounter_Id *uint `json:"encounter-id"` - Type_Code eru.DocTypeCode `json:"type-code"` + Type_Code ere.DocTypeCode `json:"type-code"` Upload_Employee_Id *string `json:"encounter-document-employee-id"` } @@ -52,7 +52,7 @@ type MetaDto struct { type ResponseDto struct { ecore.Main Encounter_Id *uint `json:"encounter_id"` - Type_Code eru.DocTypeCode `json:"type_code"` + Type_Code ere.DocTypeCode `json:"type_code"` Name string `json:"name"` FilePath *string `json:"filePath"` FileName *string `json:"fileName"` diff --git a/internal/domain/main-entities/encounter-document/entity.go b/internal/domain/main-entities/encounter-document/entity.go index b0b040fc..61152eb4 100644 --- a/internal/domain/main-entities/encounter-document/entity.go +++ b/internal/domain/main-entities/encounter-document/entity.go @@ -3,13 +3,13 @@ package encounter_document import ( ecore "simrs-vx/internal/domain/base-entities/core" ee "simrs-vx/internal/domain/main-entities/employee" - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" ) type EncounterDocument struct { ecore.Main Encounter_Id *uint `json:"encounter_id"` - Type_Code eru.DocTypeCode `json:"type_code"` + Type_Code ere.DocTypeCode `json:"type_code"` Name string `json:"name"` FilePath *string `json:"filePath"` FileName *string `json:"fileName"` diff --git a/internal/domain/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index ec270abf..ae387895 100644 --- a/internal/domain/main-entities/patient/dto.go +++ b/internal/domain/main-entities/patient/dto.go @@ -12,7 +12,7 @@ import ( epr "simrs-vx/internal/domain/main-entities/person-relative" erc "simrs-vx/internal/domain/references/common" - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" pa "simrs-vx/internal/lib/auth" ) @@ -68,7 +68,7 @@ type SearchDto struct { type UploadDto struct { Id uint `json:"-"` - Code eru.DocTypeCode `json:"-"` + Code ere.DocTypeCode `json:"-"` File multipart.File `json:"-"` FileHeader *multipart.FileHeader `json:"-"` Filename string `json:"-"` diff --git a/internal/domain/references/encounter/encounter.go b/internal/domain/references/encounter/encounter.go index dc3dd09e..532e1625 100644 --- a/internal/domain/references/encounter/encounter.go +++ b/internal/domain/references/encounter/encounter.go @@ -1,5 +1,7 @@ package encounter +import "fmt" + type ( EncounterClassCode string QueueStatusCode string @@ -18,6 +20,8 @@ type ( SEPRefTypeCode string VisitModeCode string PolySwitchCode string + DocTypeCode string + EntityTypeCode string ) const ( @@ -104,6 +108,22 @@ const ( PSCConsulPoly PolySwitchCode = "consul-poly" // Konsultasi Poliklinik Lain PSCConsulExecutive PolySwitchCode = "consul-executive" // Konsultasi Antar Dokter Eksekutif + + DTCPRN DocTypeCode = "person-resident-number" // Person Resident Number + DTCPDL DocTypeCode = "person-driver-license" // Person Driver License + DTCPP DocTypeCode = "person-passport" // Person Passport + DTCPFC DocTypeCode = "person-family-card" // Person Family Card + DTCMIR DocTypeCode = "mcu-item-result" // Mcu Item Result + DTCEnPatient DocTypeCode = "encounter-patient" + DTCEnSupport DocTypeCode = "encounter-support" + DTCEnOther DocTypeCode = "encounter-other" + DTCSEP DocTypeCode = "vclaim-sep" // SEP + DTCSIPP DocTypeCode = "vclaim-sipp" // SIPP + DTCGC DocTypeCode = "general-consent" + + ETCPerson EntityTypeCode = "person" + ETCEncounter EntityTypeCode = "encounter" + ETCMCU EntityTypeCode = "mcu" ) func (ec EncounterClassCode) Code() string { @@ -118,3 +138,30 @@ func (ec EncounterClassCode) Code() string { return "UNKNOWN" } } + +var validUploadCodesByEntity = map[EntityTypeCode][]DocTypeCode{ + ETCPerson: { + DTCPRN, DTCPDL, DTCPP, DTCPFC, + }, + ETCEncounter: { + DTCSEP, DTCSIPP, DTCEnPatient, DTCEnSupport, DTCGC, DTCEnOther, + }, + ETCMCU: { + DTCMIR, + }, +} + +func IsValidUploadCode(entity EntityTypeCode, code DocTypeCode) (bool, string) { + allowedCodes, ok := validUploadCodesByEntity[entity] + if !ok { + return false, fmt.Sprintf("unknown entityType_code: %s", entity) + } + + for _, c := range allowedCodes { + if c == code { + return true, "" + } + } + + return false, fmt.Sprintf("invalid doctype_code '%s' for entityType_code '%s'", code, entity) +} diff --git a/internal/domain/references/upload/upload.go b/internal/domain/references/upload/upload.go deleted file mode 100644 index 0817e23c..00000000 --- a/internal/domain/references/upload/upload.go +++ /dev/null @@ -1,53 +0,0 @@ -package upload - -import "fmt" - -type ( - DocTypeCode string - EntityTypeCode string -) - -const ( - DTCPRN DocTypeCode = "person-resident-number" // Person Resident Number - DTCPDL DocTypeCode = "person-driver-license" // Person Driver License - DTCPP DocTypeCode = "person-passport" // Person Passport - DTCPFC DocTypeCode = "person-family-card" // Person Family Card - DTCMIR DocTypeCode = "mcu-item-result" // Mcu Item Result - DTCEnPatient DocTypeCode = "encounter-patient" - DTCEnSupport DocTypeCode = "encounter-support" - DTCEnOther DocTypeCode = "encounter-other" - DTCSEP DocTypeCode = "vclaim-sep" // SEP - DTCSIPP DocTypeCode = "vclaim-sipp" // SIPP - DTCGC DocTypeCode = "general-consent" - - ETCPerson EntityTypeCode = "person" - ETCEncounter EntityTypeCode = "encounter" - ETCMCU EntityTypeCode = "mcu" -) - -var validUploadCodesByEntity = map[EntityTypeCode][]DocTypeCode{ - ETCPerson: { - DTCPRN, DTCPDL, DTCPP, DTCPFC, - }, - ETCEncounter: { - DTCSEP, DTCSIPP, DTCEnPatient, DTCEnSupport, DTCEnOther, - }, - ETCMCU: { - DTCMIR, - }, -} - -func IsValidUploadCode(entity EntityTypeCode, code DocTypeCode) (bool, string) { - allowedCodes, ok := validUploadCodesByEntity[entity] - if !ok { - return false, fmt.Sprintf("unknown entityType_code: %s", entity) - } - - for _, c := range allowedCodes { - if c == code { - return true, "" - } - } - - return false, fmt.Sprintf("invalid doctype_code '%s' for entityType_code '%s'", code, entity) -} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index aa6f76fb..0027fbc7 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -34,7 +34,7 @@ import ( responsibledoctorhist "simrs-vx/internal/interface/main-handler/responsible-doctor-hist" sbar "simrs-vx/internal/interface/main-handler/sbar" soapi "simrs-vx/internal/interface/main-handler/soapi" - upload "simrs-vx/internal/interface/main-handler/upload" + uploadfile "simrs-vx/internal/interface/main-handler/upload-file" /******************** actor ********************/ authpartner "simrs-vx/internal/interface/main-handler/auth-partner" @@ -274,7 +274,7 @@ func SetRoutes() http.Handler { "PATCH /{id}/reject": therapyprotocol.O.Reject, }) hc.RegCrud(r, "/v1/chemo-protocol", chemoprotocol.O) - hc.RegCrud(r, "/v1/upload", upload.O) + hc.RegCrud(r, "/v1/upload-file", uploadfile.O) hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O) /******************** actor ********************/ diff --git a/internal/interface/main-handler/upload/handler.go b/internal/interface/main-handler/upload-file/handler.go similarity index 84% rename from internal/interface/main-handler/upload/handler.go rename to internal/interface/main-handler/upload-file/handler.go index 8a09c9ae..ac09fb65 100644 --- a/internal/interface/main-handler/upload/handler.go +++ b/internal/interface/main-handler/upload-file/handler.go @@ -1,4 +1,4 @@ -package upload +package uploadfile import ( "net/http" @@ -7,10 +7,9 @@ import ( d "github.com/karincake/dodol" rw "github.com/karincake/risoles" - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" - e "simrs-vx/internal/domain/main-entities/upload" - u "simrs-vx/internal/use-case/main-use-case/upload" + u "simrs-vx/internal/use-case/main-use-case/upload-file" ) type myBase struct{} @@ -49,10 +48,10 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { } } - dto := e.CreateDto{ - EntityType_Code: eru.EntityTypeCode(r.FormValue("entityType_code")), + dto := u.CreateDto{ + EntityType_Code: ere.EntityTypeCode(r.FormValue("entityType_code")), Ref_Id: refID, - Type_Code: eru.DocTypeCode(r.FormValue("type_code")), + Type_Code: ere.DocTypeCode(r.FormValue("type_code")), Name: r.FormValue("name"), Upload_Employee_Id: employeeId, File: file, diff --git a/internal/interface/main-handler/upload/validate-request.go b/internal/interface/main-handler/upload-file/validate-request.go similarity index 64% rename from internal/interface/main-handler/upload/validate-request.go rename to internal/interface/main-handler/upload-file/validate-request.go index 38ae492c..72161e81 100644 --- a/internal/interface/main-handler/upload/validate-request.go +++ b/internal/interface/main-handler/upload-file/validate-request.go @@ -1,10 +1,10 @@ -package upload +package uploadfile import ( - e "simrs-vx/internal/domain/main-entities/upload" + u "simrs-vx/internal/use-case/main-use-case/upload-file" ) -func validateCreate(dto e.CreateDto) string { +func validateCreate(dto u.CreateDto) string { switch { case dto.EntityType_Code == "": diff --git a/internal/use-case/main-use-case/encounter-document/case.go b/internal/use-case/main-use-case/encounter-document/case.go index 88592db7..979c6611 100644 --- a/internal/use-case/main-use-case/encounter-document/case.go +++ b/internal/use-case/main-use-case/encounter-document/case.go @@ -10,7 +10,7 @@ import ( d "github.com/karincake/dodol" "gorm.io/gorm" - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" e "simrs-vx/internal/domain/main-entities/encounter-document" ) @@ -244,7 +244,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - if err := removeUploadedFile(string(eru.ETCEncounter), *data.FilePath, &event); err != nil { + if err := removeUploadedFile(string(ere.ETCEncounter), *data.FilePath, &event); err != nil { return err } diff --git a/internal/use-case/main-use-case/upload-file/case.go b/internal/use-case/main-use-case/upload-file/case.go new file mode 100644 index 00000000..20d000dc --- /dev/null +++ b/internal/use-case/main-use-case/upload-file/case.go @@ -0,0 +1,76 @@ +package uploadfile + +import ( + "errors" + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + ere "simrs-vx/internal/domain/references/encounter" +) + +const source = "upload-file" + +func Upload(input CreateDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Upload", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + var ( + err error + ) + err = dg.I.Transaction(func(tx *gorm.DB) error { + // validate entityType_Code and Type_Code + valid, msg := ere.IsValidUploadCode(input.EntityType_Code, input.Type_Code) + if !valid { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "invalid-code", + Detail: msg, + Raw: errors.New(msg), + } + return pl.SetLogError(&event, input) + } + + // upload file + input.FilePath, err = uploadAndGenerateFileUrl(input, &event) + if err != nil { + return err + } + + if input.EntityType_Code == ere.ETCEncounter { + _, err = setEncounterDocument(input, &event, tx) + if err != nil { + return err + } + + } else if input.EntityType_Code == ere.ETCPerson { + _, err = setPersonAttachment(input, &event, tx) + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: d.IS{ + "fileUrl": input.FilePath, + }, + }, nil +} diff --git a/internal/use-case/main-use-case/upload/helper.go b/internal/use-case/main-use-case/upload-file/helper.go similarity index 89% rename from internal/use-case/main-use-case/upload/helper.go rename to internal/use-case/main-use-case/upload-file/helper.go index 1880676f..176de93f 100644 --- a/internal/use-case/main-use-case/upload/helper.go +++ b/internal/use-case/main-use-case/upload-file/helper.go @@ -2,13 +2,13 @@ DESCRIPTION: Any functions that are used internally by the use-case */ -package upload +package uploadfile import ( "fmt" "path/filepath" ecore "simrs-vx/internal/domain/base-entities/core" - eru "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" "strings" "time" @@ -21,13 +21,12 @@ import ( ee "simrs-vx/internal/domain/main-entities/encounter-document" ep "simrs-vx/internal/domain/main-entities/person" - e "simrs-vx/internal/domain/main-entities/upload" ue "simrs-vx/internal/use-case/main-use-case/encounter-document" up "simrs-vx/internal/use-case/main-use-case/person" ) -func uploadAndGenerateFileUrl(input e.CreateDto, event *pl.Event) (string, error) { +func uploadAndGenerateFileUrl(input CreateDto, event *pl.Event) (string, error) { pl.SetLogInfo(event, input, "started", "uploadAndGenerateFileUrl") bucket := string(input.EntityType_Code) ext := strings.ToLower(filepath.Ext(input.Filename)) @@ -97,7 +96,7 @@ func updatePersonAttachment(dataPerson *ep.Person, event *pl.Event, dbx ...*gorm return nil } -func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.Person, error) { +func setPersonAttachment(input CreateDto, event *pl.Event, tx *gorm.DB) (*ep.Person, error) { // get person dataPerson, err := up.ReadDetailData(ep.ReadDetailDto{Id: *input.Ref_Id}, event, tx) if err != nil { @@ -106,22 +105,22 @@ func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.P var removeUrl string switch input.Type_Code { - case eru.DTCPRN: + case ere.DTCPRN: if dataPerson.ResidentIdentityFileUrl != nil { removeUrl = *dataPerson.ResidentIdentityFileUrl } dataPerson.ResidentIdentityFileUrl = &input.FilePath - case eru.DTCPDL: + case ere.DTCPDL: if dataPerson.DrivingLicenseFileUrl != nil { removeUrl = *dataPerson.DrivingLicenseFileUrl } dataPerson.DrivingLicenseFileUrl = &input.FilePath - case eru.DTCPP: + case ere.DTCPP: if dataPerson.PassportFileUrl != nil { removeUrl = *dataPerson.PassportFileUrl } dataPerson.PassportFileUrl = &input.FilePath - case eru.DTCPFC: + case ere.DTCPFC: if dataPerson.FamilyIdentityFileUrl != nil { removeUrl = *dataPerson.FamilyIdentityFileUrl } @@ -141,7 +140,7 @@ func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.P return dataPerson, nil } -func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee.EncounterDocument, error) { +func setEncounterDocument(input CreateDto, event *pl.Event, tx *gorm.DB) (*ee.EncounterDocument, error) { data := ee.EncounterDocument{} // get EncounterDocument @@ -164,7 +163,7 @@ func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee. Upload_Employee_Id: input.Upload_Employee_Id, } - if input.Type_Code == eru.DTCSEP || input.Type_Code == eru.DTCSIPP { + if input.Type_Code == ere.DTCSEP || input.Type_Code == ere.DTCSIPP { if len(dataUpload) > 0 { data = dataUpload[0] diff --git a/internal/domain/main-entities/upload/dto.go b/internal/use-case/main-use-case/upload-file/tycovar.go similarity index 53% rename from internal/domain/main-entities/upload/dto.go rename to internal/use-case/main-use-case/upload-file/tycovar.go index 445b1402..a5d63454 100644 --- a/internal/domain/main-entities/upload/dto.go +++ b/internal/use-case/main-use-case/upload-file/tycovar.go @@ -1,17 +1,22 @@ -package upload +/* +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 uploadfile import ( "mime/multipart" - eru "simrs-vx/internal/domain/references/upload" - - // internal - domain - base-entities - ecore "simrs-vx/internal/domain/base-entities/core" + ere "simrs-vx/internal/domain/references/encounter" ) type CreateDto struct { - EntityType_Code eru.EntityTypeCode `form:"entityType_code"` + EntityType_Code ere.EntityTypeCode `form:"entityType_code"` Ref_Id *uint `form:"ref_id"` - Type_Code eru.DocTypeCode `form:"type_code"` + Type_Code ere.DocTypeCode `form:"type_code"` Name string `form:"name"` Upload_Employee_Id *uint `form:"upload_employee_id"` FilePath string `json:"-"` @@ -23,46 +28,10 @@ type CreateDto struct { MimeType string `json:"-"` } -type ReadListDto struct { - FilterDto - Includes string `json:"includes"` - Pagination ecore.Pagination -} - -type FilterDto struct { - EntityType_Code eru.EntityTypeCode `json:"entityType-code"` - Ref_Id *uint `json:"ref-id"` - Type_Code eru.DocTypeCode `json:"type-code"` - Name string `json:"name"` - FilePath *string `json:"filePath"` - FileName *string `json:"fileName"` - Upload_Employee_Id *string `json:"encounter-document-employee-id"` -} - -type ReadDetailDto struct { - Id uint16 `json:"id"` - Includes string `json:"includes"` -} - -type UpdateDto struct { - Id uint16 `json:"id"` - CreateDto -} - -type DeleteDto struct { - Id uint16 `json:"id"` -} - -type MetaDto struct { - PageNumber int `json:"page_number"` - PageSize int `json:"page_size"` - Count int `json:"count"` -} - type ResponseDto struct { - EntityType_Code eru.EntityTypeCode `json:"entityType_code"` + EntityType_Code ere.EntityTypeCode `json:"entityType_code"` Ref_Id *uint `json:"ref_id"` - Type_Code eru.DocTypeCode `json:"type_code"` + Type_Code ere.DocTypeCode `json:"type_code"` Name string `json:"name"` Upload_Employee_Id *uint `json:"upload_employee_id"` FilePath string `json:"filePath"` diff --git a/internal/use-case/main-use-case/upload/case.go b/internal/use-case/main-use-case/upload/case.go deleted file mode 100644 index 40ff74f6..00000000 --- a/internal/use-case/main-use-case/upload/case.go +++ /dev/null @@ -1,90 +0,0 @@ -package upload - -import ( - "errors" - pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" - - dg "github.com/karincake/apem/db-gorm-pg" - d "github.com/karincake/dodol" - "gorm.io/gorm" - - eru "simrs-vx/internal/domain/references/upload" - - e "simrs-vx/internal/domain/main-entities/upload" -) - -const source = "upload" - -func Upload(input e.CreateDto) (*d.Data, error) { - event := pl.Event{ - Feature: "Upload", - Source: source, - } - - // Start log - pl.SetLogInfo(&event, input, "started", "create") - - var data interface{} - err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - - // Run pre-middleware - err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data) - if err != nil { - return err - } - - // validate entityType_Code and Type_Code - valid, msg := eru.IsValidUploadCode(input.EntityType_Code, input.Type_Code) - if !valid { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "invalid-code", - Detail: msg, - Raw: errors.New(msg), - } - return pl.SetLogError(&event, input) - } - - // upload file - input.FilePath, err = uploadAndGenerateFileUrl(input, &event) - if err != nil { - return err - } - - if input.EntityType_Code == eru.ETCEncounter { - data, err = setEncounterDocument(input, &event, tx) - if err != nil { - return err - } - - } else if input.EntityType_Code == eru.ETCPerson { - data, err = setPersonAttachment(input, &event, tx) - } - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { - return err - } - - pl.SetLogInfo(&event, nil, "complete") - - return nil - }) - - if err != nil { - return nil, err - } - - return &d.Data{ - Meta: d.II{ - "source": source, - "structure": "single-data", - "status": "created", - }, - Data: input.ToResponse(), - }, nil -} diff --git a/internal/use-case/main-use-case/upload/middleware-runner.go b/internal/use-case/main-use-case/upload/middleware-runner.go deleted file mode 100644 index 88e3e637..00000000 --- a/internal/use-case/main-use-case/upload/middleware-runner.go +++ /dev/null @@ -1,104 +0,0 @@ -package upload - -import ( - pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" - - "gorm.io/gorm" - - e "simrs-vx/internal/domain/main-entities/upload" -) - -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, - } -} - -// ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data interface{}) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data interface{}) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) 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/main-use-case/upload/middleware.go b/internal/use-case/main-use-case/upload/middleware.go deleted file mode 100644 index 63beb606..00000000 --- a/internal/use-case/main-use-case/upload/middleware.go +++ /dev/null @@ -1,9 +0,0 @@ -package upload - -// example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } diff --git a/internal/use-case/main-use-case/upload/tycovar.go b/internal/use-case/main-use-case/upload/tycovar.go deleted file mode 100644 index 4849e215..00000000 --- a/internal/use-case/main-use-case/upload/tycovar.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -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 upload - -import ( - "gorm.io/gorm" - - e "simrs-vx/internal/domain/main-entities/upload" -) - -type createMw struct { - Name string - Func func(input *e.CreateDto, data interface{}, tx *gorm.DB) error -} - -type readListMw struct { - Name string - Func func(input *e.ReadListDto, data interface{}, tx *gorm.DB) error -} - -type readDetailMw struct { - Name string - Func func(input *e.ReadDetailDto, data interface{}, tx *gorm.DB) error -} - -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw - -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware -var readListPreMw []readListMw // .. -var readListPostMw []readListMw // .. -var readDetailPreMw []readDetailMw -var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw -var updatePostMw []readDetailMw -var deletePreMw []readDetailMw -var deletePostMw []readDetailMw diff --git a/pkg/upload-helper/upload-helper.go b/pkg/upload-helper/upload-helper.go index 85f5448f..0dce8fd7 100644 --- a/pkg/upload-helper/upload-helper.go +++ b/pkg/upload-helper/upload-helper.go @@ -5,7 +5,7 @@ import ( "path/filepath" "strings" - ere "simrs-vx/internal/domain/references/upload" + ere "simrs-vx/internal/domain/references/encounter" ) func getBucketForType(docType string) (string, error) { From e78ad5aa836787c807790ca743f69a80c25af35c Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 15:29:46 +0700 Subject: [PATCH 06/23] add docs config parser --- internal/infra/docs-cfg/docs-cfg.go | 17 +++++++++++++++++ internal/interface/main-handler/main-handler.go | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 internal/infra/docs-cfg/docs-cfg.go diff --git a/internal/infra/docs-cfg/docs-cfg.go b/internal/infra/docs-cfg/docs-cfg.go new file mode 100644 index 00000000..e6d32c92 --- /dev/null +++ b/internal/infra/docs-cfg/docs-cfg.go @@ -0,0 +1,17 @@ +package docscfg + +import a "github.com/karincake/apem" + +type DocsCfg struct { + Path string +} + +var O DocsCfg = DocsCfg{} + +func ParseCfg() { + a.ParseSingleCfg(&O) +} + +func (c DocsCfg) GetPath() string { + return c.Path +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 0027fbc7..649c59cc 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -57,6 +57,7 @@ import ( /******************** infra ********************/ ibpjs "simrs-vx/internal/infra/bpjs" + docscfg "simrs-vx/internal/infra/docs-cfg" gs "simrs-vx/internal/infra/gorm-setting" minio "simrs-vx/internal/infra/minio" ssdb "simrs-vx/internal/infra/ss-db" @@ -131,6 +132,7 @@ func SetRoutes() http.Handler { a.RegisterExtCall(ibpjs.SetConfig) a.RegisterExtCall(validation.RegisterValidation) a.RegisterExtCall(simgossync.SetConfig) + a.RegisterExtCall(docscfg.ParseCfg) r := http.NewServeMux() From 8911bb678c411b16245a5a0d3ef763c60c67700b Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 17 Nov 2025 16:17:26 +0700 Subject: [PATCH 07/23] add divisi --- internal/domain/main-entities/division/dto.go | 1 + .../use-case/main-use-case/division/case.go | 97 +++++---- .../division/middleware-runner.go | 38 +++- .../main-use-case/division/middleware.go | 23 ++- .../main-use-case/division/tycovar.go | 31 ++- .../main-use-case/unit/middleware-runner.go | 4 +- .../simgos-sync-plugin/division/plugin.go | 56 +++-- .../simgos-sync-plugin/installation/plugin.go | 54 +++-- .../simgos-sync-plugin/unit/plugin.go | 54 +++-- .../simgos-sync-use-case/division/case.go | 194 +++++++++--------- .../simgos-sync-use-case/division/helper.go | 30 ++- .../simgos-sync-use-case/division/lib.go | 176 ++++++++-------- 12 files changed, 431 insertions(+), 327 deletions(-) diff --git a/internal/domain/main-entities/division/dto.go b/internal/domain/main-entities/division/dto.go index 30f0b238..d74b54f7 100644 --- a/internal/domain/main-entities/division/dto.go +++ b/internal/domain/main-entities/division/dto.go @@ -6,6 +6,7 @@ import ( ) type CreateDto struct { + Id *uint `json:"id"` Code string `json:"code" validate:"maxLength=10"` Name string `json:"name" validate:"maxLength=50"` Parent_Code *string `json:"parent_code"` diff --git a/internal/use-case/main-use-case/division/case.go b/internal/use-case/main-use-case/division/case.go index c955aed7..4be7ce82 100644 --- a/internal/use-case/main-use-case/division/case.go +++ b/internal/use-case/main-use-case/division/case.go @@ -2,6 +2,8 @@ package division import ( e "simrs-vx/internal/domain/main-entities/division" + erc "simrs-vx/internal/domain/references/common" + esync "simrs-vx/internal/domain/sync-entities/log" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -25,36 +27,32 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData + id := uint(data.Id) + input.Id = &id } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -80,7 +78,7 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { @@ -131,7 +129,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { @@ -177,6 +175,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -184,32 +183,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := UpdateData(input, data, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(updatePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -233,6 +226,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -240,30 +234,26 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := DeleteData(data, &event, tx); err != nil { return err } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -274,3 +264,32 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/division/middleware-runner.go b/internal/use-case/main-use-case/division/middleware-runner.go index 915cce28..b39e3176 100644 --- a/internal/use-case/main-use-case/division/middleware-runner.go +++ b/internal/use-case/main-use-case/division/middleware-runner.go @@ -2,6 +2,7 @@ package division import ( e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -15,21 +16,36 @@ type middlewareRunner struct { } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ Event: event, - Tx: tx, } } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Division) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// ExecuteCreateMiddleware executes createlog middleware +func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -68,13 +84,13 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -83,13 +99,13 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/division/middleware.go b/internal/use-case/main-use-case/division/middleware.go index f066ffb8..317b137a 100644 --- a/internal/use-case/main-use-case/division/middleware.go +++ b/internal/use-case/main-use-case/division/middleware.go @@ -1,9 +1,20 @@ package division +import ( + plugin "simrs-vx/internal/use-case/simgos-sync-plugin/division" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPreMw = append(createPreMw, + createMw{Name: "create-unit", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "update-unit", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "delete-unit", Func: plugin.Delete}) +} diff --git a/internal/use-case/main-use-case/division/tycovar.go b/internal/use-case/main-use-case/division/tycovar.go index c7733117..cef873ee 100644 --- a/internal/use-case/main-use-case/division/tycovar.go +++ b/internal/use-case/main-use-case/division/tycovar.go @@ -12,11 +12,27 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/division" + elog "simrs-vx/internal/domain/sync-entities/log" ) type createMw struct { Name string - Func func(input *e.CreateDto, data *e.Division, tx *gorm.DB) error + Func func(input *e.CreateDto) error +} + +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + +type updateMw struct { + Name string + Func func(input *e.UpdateDto) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error } type readListMw struct { @@ -29,16 +45,17 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Division, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/unit/middleware-runner.go b/internal/use-case/main-use-case/unit/middleware-runner.go index 3c7fa241..f45fe208 100644 --- a/internal/use-case/main-use-case/unit/middleware-runner.go +++ b/internal/use-case/main-use-case/unit/middleware-runner.go @@ -84,7 +84,7 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e.UpdateDto) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -99,7 +99,7 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []DeleteMw, input *e.DeleteDto) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) diff --git a/internal/use-case/simgos-sync-plugin/division/plugin.go b/internal/use-case/simgos-sync-plugin/division/plugin.go index 06e54e31..54c1403c 100644 --- a/internal/use-case/simgos-sync-plugin/division/plugin.go +++ b/internal/use-case/simgos-sync-plugin/division/plugin.go @@ -9,8 +9,10 @@ import ( sync "simrs-vx/internal/infra/sync-cfg" - e "simrs-vx/internal/domain/main-entities/unit" + e "simrs-vx/internal/domain/main-entities/division" elog "simrs-vx/internal/domain/sync-entities/log" + + d "github.com/karincake/dodol" ) func Create(input *e.CreateDto) error { @@ -34,15 +36,18 @@ func Create(input *e.CreateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -68,15 +73,18 @@ func CreateLog(input *elog.SimxLogDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -102,15 +110,18 @@ func Update(input *e.UpdateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -136,15 +147,18 @@ func Delete(input *e.DeleteDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } diff --git a/internal/use-case/simgos-sync-plugin/installation/plugin.go b/internal/use-case/simgos-sync-plugin/installation/plugin.go index 06c9bebe..f2495139 100644 --- a/internal/use-case/simgos-sync-plugin/installation/plugin.go +++ b/internal/use-case/simgos-sync-plugin/installation/plugin.go @@ -11,6 +11,8 @@ import ( e "simrs-vx/internal/domain/main-entities/installation" elog "simrs-vx/internal/domain/sync-entities/log" + + d "github.com/karincake/dodol" ) func Create(input *e.CreateDto) error { @@ -34,15 +36,18 @@ func Create(input *e.CreateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -68,15 +73,18 @@ func CreateLog(input *elog.SimxLogDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -102,15 +110,18 @@ func Update(input *e.UpdateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -136,15 +147,18 @@ func Delete(input *e.DeleteDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } diff --git a/internal/use-case/simgos-sync-plugin/unit/plugin.go b/internal/use-case/simgos-sync-plugin/unit/plugin.go index 8462f18d..e854d898 100644 --- a/internal/use-case/simgos-sync-plugin/unit/plugin.go +++ b/internal/use-case/simgos-sync-plugin/unit/plugin.go @@ -11,6 +11,8 @@ import ( e "simrs-vx/internal/domain/main-entities/unit" elog "simrs-vx/internal/domain/sync-entities/log" + + d "github.com/karincake/dodol" ) func Create(input *e.CreateDto) error { @@ -34,15 +36,18 @@ func Create(input *e.CreateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -68,15 +73,18 @@ func CreateLog(input *elog.SimxLogDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -102,15 +110,18 @@ func Update(input *e.UpdateDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } @@ -136,15 +147,18 @@ func Delete(input *e.DeleteDto) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - _, err = io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + return nil } diff --git a/internal/use-case/simgos-sync-use-case/division/case.go b/internal/use-case/simgos-sync-use-case/division/case.go index 074bf2a7..14dd0886 100644 --- a/internal/use-case/simgos-sync-use-case/division/case.go +++ b/internal/use-case/simgos-sync-use-case/division/case.go @@ -8,17 +8,17 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/division" + esimgos "simrs-vx/internal/domain/simgos-entities/division" esync "simrs-vx/internal/domain/sync-entities/log" - //esimgos "simrs-vx/internal/domain/simgos-entities/installation" ) const source = "division" func Create(input e.CreateDto) (*d.Data, error) { - //var ( - // txSync = dg.I.Begin() - // txSimgos = dg.IS["simrs"].Begin() - //) + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + ) event := pl.Event{ Feature: "Create", @@ -29,32 +29,32 @@ func Create(input e.CreateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "create") // STEP 1: Insert to simgos - //sgData, err := CreateSimgosData(input, &event, txSimgos) - //if err != nil { - // return nil, err - //} - // - //// STEP 2: Insert to Link - //syncLink, err := CreateLinkData(*input.Id, sgData.Kode, &event, txSync) - //if err != nil { - // txSimgos.Rollback() - // return nil, err - //} - // - //// STEP 3: Commit two trx - //if err = txSync.Commit().Error; err != nil { - // txSync.Rollback() - // txSimgos.Rollback() - // return nil, err - //} - // - //if err = txSimgos.Commit().Error; err != nil { - // // STEP 4: Rollback Partial - // go func() { - // _ = DeleteLinkData(syncLink, &event) - // }() - // return nil, err - //} + sgData, err := CreateSimgosData(input, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 2: Insert to Link + syncLink, err := CreateLinkData(*input.Id, sgData.KodeUnit, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 3: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } + + if err = txSimgos.Commit().Error; err != nil { + // STEP 4: Rollback Partial + go func() { + _ = DeleteLinkData(syncLink, &event) + }() + return nil, err + } pl.SetLogInfo(&event, nil, "complete") @@ -103,10 +103,10 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - //var ( - // txSimgos = dg.IS["simrs"].Begin() - // err error - //) + var ( + txSimgos = dg.IS["simrs"].Begin() + err error + ) event := pl.Event{ Feature: "Update", @@ -116,23 +116,23 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") - //// STEP 1: Get Installation Link - //syncLink, err := ReadDetailLinkData(*input.Id, &event) - //if err != nil { - // return nil, err - //} - // - //// Step 2: Update Simgos - //err = UpdateSimgosData(input, syncLink, &event, txSimgos) - //if err != nil { - // return nil, err - //} - // - //// STEP 4: Commit two trx - //if err = txSimgos.Commit().Error; err != nil { - // txSimgos.Rollback() - // return nil, err - //} + // STEP 1: Get Installation Link + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err + } + + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + return nil, err + } pl.SetLogInfo(&event, nil, "complete") @@ -146,12 +146,12 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - //var ( - // txSync = dg.I.Begin() - // txSimgos = dg.IS["simrs"].Begin() - // sgData *esimgos.MPloy - // err error - //) + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MUnit + err error + ) event := pl.Event{ Feature: "Delete", @@ -161,45 +161,45 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") - //// STEP 1: Get Installation Link - //syncLink, err := ReadDetailLinkData(*input.Id, &event) - //if err != nil { - // return nil, err - //} - // - //// STEP 2: Get Simgos - //sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) - //if err != nil { - // return nil, err - //} - // - //// STEP 3: Delete M_Poly Simgos - //err = HardDeleteSimgosData(sgData, &event, txSimgos) - //if err != nil { - // return nil, err - //} - // - //// STEP 4: Delete Installation Link - //err = DeleteLinkData(syncLink, &event, txSync) - //if err != nil { - // txSimgos.Rollback() - // return nil, err - //} - // - //// STEP 4: Commit two trx - //if err = txSync.Commit().Error; err != nil { - // txSync.Rollback() - // txSimgos.Rollback() - // return nil, err - //} - // - //if err = txSimgos.Commit().Error; err != nil { - // // STEP 5: Rollback Partial - // go func() { - // _, _ = CreateLinkData(uint(*input.Id), sgData.Kode, &event) - // }() - // return nil, err - //} + // STEP 1: Get Installation Link + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err + } + + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + // STEP 3: Delete M_Unit Simgos + err = HardDeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 4: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } + + if err = txSimgos.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.KodeUnit, &event) + }() + return nil, err + } pl.SetLogInfo(&event, nil, "complete") diff --git a/internal/use-case/simgos-sync-use-case/division/helper.go b/internal/use-case/simgos-sync-use-case/division/helper.go index 210d6e7c..e180d771 100644 --- a/internal/use-case/simgos-sync-use-case/division/helper.go +++ b/internal/use-case/simgos-sync-use-case/division/helper.go @@ -8,26 +8,24 @@ import ( "encoding/json" erc "simrs-vx/internal/domain/references/common" - //e "simrs-vx/internal/domain/main-entities/division" - // esimgos "simrs-vx/internal/domain/simgos-entities/divison" TBC + e "simrs-vx/internal/domain/main-entities/division" + esimgos "simrs-vx/internal/domain/simgos-entities/division" esync "simrs-vx/internal/domain/sync-entities/division" esyncLog "simrs-vx/internal/domain/sync-entities/log" ) -//func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MPloy) { -// var inputSrc *e.CreateDto -// if inputT, ok := any(input).(*e.CreateDto); ok { -// inputSrc = inputT -// } else { -// inputTemp := any(input).(*e.UpdateDto) -// inputSrc = &inputTemp.CreateDto -// } -// -// data.Nama = inputSrc.Name -// data.Jenispoly = 0 -// -// return -//} +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MUnit) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.NamaUnit = inputSrc.Name + return +} func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.DivisionSimxLog) { // encode to JSON diff --git a/internal/use-case/simgos-sync-use-case/division/lib.go b/internal/use-case/simgos-sync-use-case/division/lib.go index ce13068d..681be276 100644 --- a/internal/use-case/simgos-sync-use-case/division/lib.go +++ b/internal/use-case/simgos-sync-use-case/division/lib.go @@ -9,103 +9,103 @@ import ( dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" - //e "simrs-vx/internal/domain/main-entities/division" + e "simrs-vx/internal/domain/main-entities/division" + esimgos "simrs-vx/internal/domain/simgos-entities/division" esync "simrs-vx/internal/domain/sync-entities/division" - //esimgos "simrs-vx/internal/domain/simgos-entities/unit" esynclog "simrs-vx/internal/domain/sync-entities/log" ) var now = time.Now() -//func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPloy, error) { -// pl.SetLogInfo(event, nil, "started", "DBCreate") -// -// data := setDataSimgos(&input) -// -// var tx *gorm.DB -// if len(dbx) > 0 { -// tx = dbx[0] -// } else { -// tx = dg.IS["simrs"] -// } -// -// if err := tx.Create(&data).Error; err != nil { -// return nil, plh.HandleCreateError(input, event, err) -// } -// -// pl.SetLogInfo(event, nil, "complete") -// return &data, nil -//} +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MUnit, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") -//func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPloy, error) { -// pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") -// data := esimgos.MPloy{} -// -// var tx = dg.IS["simrs"] -// -// if err := tx. -// Where("\"kode\" = ?", simgosId). -// First(&data).Error; err != nil { -// if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { -// return nil, processedErr -// } -// } -// -// pl.SetLogInfo(event, nil, "complete") -// return &data, nil -//} + data := setDataSimgos(&input) -//func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.DivisionLink, event *pl.Event, dbx ...*gorm.DB) error { -// pl.SetLogInfo(event, input, "started", "DBUpdate") -// -// data := setDataSimgos(&input) -// data.Kode = dataSimgos.Simgos_Id -// -// var tx *gorm.DB -// if len(dbx) > 0 { -// tx = dbx[0] -// } else { -// tx = dg.IS["simrs"] -// } -// -// if err := tx.Save(&data).Error; err != nil { -// event.Status = "failed" -// event.ErrInfo = pl.ErrorInfo{ -// Code: "data-update-fail", -// Detail: "Database update failed", -// Raw: err, -// } -// return pl.SetLogError(event, input) -// } -// -// pl.SetLogInfo(event, nil, "complete") -// return nil -//} + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } -//func HardDeleteSimgosData(data *esimgos.MPloy, event *pl.Event, dbx ...*gorm.DB) error { -// pl.SetLogInfo(event, data, "started", "DBDelete") -// -// var tx *gorm.DB -// if len(dbx) > 0 { -// tx = dbx[0] -// } else { -// tx = dg.IS["simrs"] -// } -// -// if err := tx. -// Delete(&data).Error; err != nil { -// event.Status = "failed" -// event.ErrInfo = pl.ErrorInfo{ -// Code: "data-delete-fail", -// Detail: "Database delete failed", -// Raw: err, -// } -// return pl.SetLogError(event, data) -// } -// -// pl.SetLogInfo(event, nil, "complete") -// return nil -//} + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MUnit, error) { + pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") + data := esimgos.MUnit{} + + var tx = dg.IS["simrs"] + + if err := tx. + Where("\"kode_unit\" = ?", simgosId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.DivisionLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.KodeUnit = dataSimgos.Simgos_Id + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func HardDeleteSimgosData(data *esimgos.MUnit, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx. + Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.DivisionLink, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") From 8073236d06ec563669b84fda0a1f210529fca79a Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 16:20:14 +0700 Subject: [PATCH 08/23] feat (general-consent): add crud --- cmd/main-api/config.yml-example | 5 +- .../main-entities/general-consent/dto.go | 64 ++++ .../main-entities/general-consent/entity.go | 2 +- internal/domain/references/common/common.go | 6 + .../main-handler/general-consent/handler.go | 72 +++++ .../interface/main-handler/main-handler.go | 2 + .../main-use-case/general-consent/case.go | 282 ++++++++++++++++++ .../main-use-case/general-consent/helper.go | 22 ++ .../main-use-case/general-consent/lib.go | 140 +++++++++ .../general-consent/middleware-runner.go | 103 +++++++ .../general-consent/middleware.go | 9 + .../main-use-case/general-consent/tycovar.go | 44 +++ .../main-use-case/generate-file/case.go | 45 +++ .../main-use-case/generate-file/tycovar.go | 25 ++ 14 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 internal/domain/main-entities/general-consent/dto.go create mode 100644 internal/interface/main-handler/general-consent/handler.go create mode 100644 internal/use-case/main-use-case/general-consent/case.go create mode 100644 internal/use-case/main-use-case/general-consent/helper.go create mode 100644 internal/use-case/main-use-case/general-consent/lib.go create mode 100644 internal/use-case/main-use-case/general-consent/middleware-runner.go create mode 100644 internal/use-case/main-use-case/general-consent/middleware.go create mode 100644 internal/use-case/main-use-case/general-consent/tycovar.go create mode 100644 internal/use-case/main-use-case/generate-file/case.go create mode 100644 internal/use-case/main-use-case/generate-file/tycovar.go diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index 448cff66..22fdf651 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -70,4 +70,7 @@ bpjsCfg: syncUrlCfg: host: - prefix: new-to-old \ No newline at end of file + prefix: new-to-old + +docsCfg: + path: ../../assets/docs/ \ No newline at end of file diff --git a/internal/domain/main-entities/general-consent/dto.go b/internal/domain/main-entities/general-consent/dto.go new file mode 100644 index 00000000..3c659f2f --- /dev/null +++ b/internal/domain/main-entities/general-consent/dto.go @@ -0,0 +1,64 @@ +package generalconsent + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" +) + +type CreateDto struct { + Encounter_Id *uint `json:"-"` + Value *string `json:"value"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` +} + +type ReadDetailDto struct { + Id uint `json:"id"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Value *string `json:"value"` + FileUrl *string `json:"fileUrl"` +} + +func (d GeneralConsent) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Value: d.Value, + FileUrl: d.FileUrl, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []GeneralConsent) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/general-consent/entity.go b/internal/domain/main-entities/general-consent/entity.go index f5ce6394..bf9381ec 100644 --- a/internal/domain/main-entities/general-consent/entity.go +++ b/internal/domain/main-entities/general-consent/entity.go @@ -1,4 +1,4 @@ -package general_consent +package generalconsent import ( "simrs-vx/internal/domain/base-entities/core" diff --git a/internal/domain/references/common/common.go b/internal/domain/references/common/common.go index 5b3f675b..cc466d7b 100644 --- a/internal/domain/references/common/common.go +++ b/internal/domain/references/common/common.go @@ -17,6 +17,7 @@ type ( CrudCode string DataApprovalCode string ProcessStatusCode string + DocFormatTypeCode string ) const ( @@ -106,6 +107,11 @@ const ( PSCSuccess ProcessStatusCode = "success" PSCFailed ProcessStatusCode = "failed" + + DFTCPDF DocFormatTypeCode = "pdf" + DFTCTXLSX DocFormatTypeCode = "xlsx" + DFTCTCSV DocFormatTypeCode = "csv" + DFTCTXLS DocFormatTypeCode = "xls" ) func GetDayCodes() map[DayCode]string { diff --git a/internal/interface/main-handler/general-consent/handler.go b/internal/interface/main-handler/general-consent/handler.go new file mode 100644 index 00000000..0a2e2f6a --- /dev/null +++ b/internal/interface/main-handler/general-consent/handler.go @@ -0,0 +1,72 @@ +package generalconsent + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/general-consent" + u "simrs-vx/internal/use-case/main-use-case/general-consent" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) + dto.Id = uint(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 649c59cc..8ce9fadf 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -17,6 +17,7 @@ import ( deviceorderitem "simrs-vx/internal/interface/main-handler/device-order-item" encounter "simrs-vx/internal/interface/main-handler/encounter" encounterdocument "simrs-vx/internal/interface/main-handler/encounter-document" + generalconsent "simrs-vx/internal/interface/main-handler/general-consent" internalreference "simrs-vx/internal/interface/main-handler/internal-reference" materialorder "simrs-vx/internal/interface/main-handler/material-order" materialorderitem "simrs-vx/internal/interface/main-handler/material-order-item" @@ -278,6 +279,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/chemo-protocol", chemoprotocol.O) hc.RegCrud(r, "/v1/upload-file", uploadfile.O) hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O) + hc.RegCrud(r, "/v1/general-consent", generalconsent.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/use-case/main-use-case/general-consent/case.go b/internal/use-case/main-use-case/general-consent/case.go new file mode 100644 index 00000000..a8c181ef --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/case.go @@ -0,0 +1,282 @@ +package generalconsent + +import ( + "strconv" + + // main entities + e "simrs-vx/internal/domain/main-entities/general-consent" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" +) + +const source = "general-consent" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.GeneralConsent{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.GeneralConsent + var dataList []e.GeneralConsent + var metaList *e.MetaDto + 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 &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.GeneralConsent + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.GeneralConsent + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + // Get Updated Data + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: uint(input.Id)} + var data *e.GeneralConsent + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/general-consent/helper.go b/internal/use-case/main-use-case/general-consent/helper.go new file mode 100644 index 00000000..015d4716 --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package generalconsent + +import ( + e "simrs-vx/internal/domain/main-entities/general-consent" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.GeneralConsent) { + 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.Value = inputSrc.Value +} diff --git a/internal/use-case/main-use-case/general-consent/lib.go b/internal/use-case/main-use-case/general-consent/lib.go new file mode 100644 index 00000000..e365523d --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/lib.go @@ -0,0 +1,140 @@ +package generalconsent + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/general-consent" + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.GeneralConsent, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.GeneralConsent{} + setData(&input, &data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.GeneralConsent, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.GeneralConsent{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.GeneralConsent{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Order("\"CreatedAt\" DESC") + + if err := tx.Find(&data).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.GeneralConsent, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.GeneralConsent{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.GeneralConsent, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&input, data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func DeleteData(data *e.GeneralConsent, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/general-consent/middleware-runner.go b/internal/use-case/main-use-case/general-consent/middleware-runner.go new file mode 100644 index 00000000..4dfc739a --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/middleware-runner.go @@ -0,0 +1,103 @@ +package generalconsent + +import ( + e "simrs-vx/internal/domain/main-entities/general-consent" + 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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.GeneralConsent) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.GeneralConsent) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) 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/main-use-case/general-consent/middleware.go b/internal/use-case/main-use-case/general-consent/middleware.go new file mode 100644 index 00000000..69311dfc --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/middleware.go @@ -0,0 +1,9 @@ +package generalconsent + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/main-use-case/general-consent/tycovar.go b/internal/use-case/main-use-case/general-consent/tycovar.go new file mode 100644 index 00000000..b91396b9 --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/tycovar.go @@ -0,0 +1,44 @@ +/* +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 generalconsent + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/general-consent" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.GeneralConsent, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.GeneralConsent, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.GeneralConsent, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go new file mode 100644 index 00000000..72182513 --- /dev/null +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -0,0 +1,45 @@ +package generatefile + +import ( + // "encoding/json" + + // egc "simrs-vx/internal/domain/main-entities/general-consent" + // ugc "simrs-vx/internal/use-case/main-use-case/general-consent" + + pl "simrs-vx/pkg/logger" + + d "github.com/karincake/dodol" + + ere "simrs-vx/internal/domain/references/encounter" +) + +const source = "generate-file" + +func Generate(input GenerateDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Generate", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + switch input.Type_Code { + // general-consent + case ere.DTCGC: + // get value from general consent by ref_id + // gc, err := ugc.ReadDetailData(ugc.ReadDetailDto{Ref_Id: input.Ref_Id}, &event, nil) + // if err != nil { + // return nil, err + // } + + // map template data + // gc + // templateData := GeneralConsentPDF{} + // gcUnmarshalled := json.Unmarshal(g) + + return nil, nil + } + + return nil, nil +} diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go new file mode 100644 index 00000000..716e277d --- /dev/null +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -0,0 +1,25 @@ +package generatefile + +import ( + erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" +) + +type GeneralConsentPDF struct { + PatientRelatives_Name []string `json:"patientRelatives_name"` + Responsible_Name string `json:"responsible_name"` + InformationGivenBy_Name string `json:"informationGivenBy_name"` + Witness1_Name string `json:"witness1_name"` + Witness2_Name string `json:"witness2_name"` +} + +type GenerateDto struct { + EntityType_Code ere.EntityTypeCode `form:"entityType_code"` + Ref_Id *uint `form:"ref_id"` + Type_Code ere.DocTypeCode `form:"type_code"` + FormatType erc.DocFormatTypeCode `form:"formatType"` +} + +type ResponseDto struct { + FileUrl string `json:"fileUrl"` +} From 69a13dd37d517d1b5a6ba5073907842a7d2cadc2 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 17:18:22 +0700 Subject: [PATCH 09/23] feat (generate-file): not tested yet --- go.mod | 1 + go.sum | 2 + .../main-handler/generate-file/handler.go | 18 +++ .../interface/main-handler/main-handler.go | 2 + .../main-use-case/generate-file/case.go | 64 +++++++-- .../main-use-case/generate-file/helper.go | 83 +++++++++++ .../main-use-case/generate-file/tycovar.go | 22 ++- pkg/file-helper/file-helper.go | 33 +++++ pkg/pdf-helper/pdf-helper.go | 131 ++++++++++++++++++ 9 files changed, 340 insertions(+), 16 deletions(-) create mode 100644 internal/interface/main-handler/generate-file/handler.go create mode 100644 internal/use-case/main-use-case/generate-file/helper.go create mode 100644 pkg/file-helper/file-helper.go create mode 100644 pkg/pdf-helper/pdf-helper.go diff --git a/go.mod b/go.mod index 9d557637..b44bb011 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( require ( ariga.io/atlas v0.36.2-0.20250806044935-5bb51a0a956e // indirect + github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-redis/redis v6.15.9+incompatible // indirect diff --git a/go.sum b/go.sum index 9681a749..da70ac51 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3 h1:vrA6+R1BMLKMTbos8jAeuBrImHPGtY4gTlcue3OIej8= +github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3/go.mod h1:SQq4xfIdvf6WYKSDxAJc+xOJdolt+/bc1jnQKMtPMvQ= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/interface/main-handler/generate-file/handler.go b/internal/interface/main-handler/generate-file/handler.go new file mode 100644 index 00000000..c6d6c67d --- /dev/null +++ b/internal/interface/main-handler/generate-file/handler.go @@ -0,0 +1,18 @@ +package generatefile + +import ( + "net/http" + + rw "github.com/karincake/risoles" + + u "simrs-vx/internal/use-case/main-use-case/generate-file" +) + +func Generate(w http.ResponseWriter, r *http.Request) { + dto := u.GenerateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Generate(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 8ce9fadf..4a49c022 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -18,6 +18,7 @@ import ( encounter "simrs-vx/internal/interface/main-handler/encounter" encounterdocument "simrs-vx/internal/interface/main-handler/encounter-document" generalconsent "simrs-vx/internal/interface/main-handler/general-consent" + generatefile "simrs-vx/internal/interface/main-handler/generate-file" internalreference "simrs-vx/internal/interface/main-handler/internal-reference" materialorder "simrs-vx/internal/interface/main-handler/material-order" materialorderitem "simrs-vx/internal/interface/main-handler/material-order-item" @@ -280,6 +281,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/upload-file", uploadfile.O) hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O) hc.RegCrud(r, "/v1/general-consent", generalconsent.O) + r.HandleFunc("POST /v1/generate-file", generatefile.Generate) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go index 72182513..c39a7edc 100644 --- a/internal/use-case/main-use-case/generate-file/case.go +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -1,15 +1,18 @@ package generatefile import ( - // "encoding/json" + "encoding/json" + "errors" - // egc "simrs-vx/internal/domain/main-entities/general-consent" - // ugc "simrs-vx/internal/use-case/main-use-case/general-consent" + egc "simrs-vx/internal/domain/main-entities/general-consent" + ugc "simrs-vx/internal/use-case/main-use-case/general-consent" pl "simrs-vx/pkg/logger" + dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" + erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" ) @@ -28,18 +31,55 @@ func Generate(input GenerateDto) (*d.Data, error) { // general-consent case ere.DTCGC: // get value from general consent by ref_id - // gc, err := ugc.ReadDetailData(ugc.ReadDetailDto{Ref_Id: input.Ref_Id}, &event, nil) - // if err != nil { - // return nil, err - // } + gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *input.Ref_Id}, &event, nil) + if err != nil { + return nil, err + } // map template data - // gc - // templateData := GeneralConsentPDF{} - // gcUnmarshalled := json.Unmarshal(g) + templateData := GeneralConsentPDF{} + if gc.Value != nil { + err := json.Unmarshal([]byte(*gc.Value), &templateData) + if err != nil { + event.ErrInfo = pl.ErrorInfo{ + Code: "data-unmarshal-fail", + Detail: err.Error(), + Raw: err, + } + return nil, err + } + } else { + return nil, errors.New("no value in this general consent") + } - return nil, nil + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNGC + + // generate file + urlPub, err := generateFile(input, templateData) + if err != nil { + return nil, err + } + + gc.FileUrl = &urlPub + if err := dg.I.Save(&gc).Error; err != nil { + return nil, err + } + + response := ResponseDto{ + FileUrl: urlPub, + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: response, + }, nil + default: + return nil, errors.New("invalid type code") } - return nil, nil } diff --git a/internal/use-case/main-use-case/generate-file/helper.go b/internal/use-case/main-use-case/generate-file/helper.go new file mode 100644 index 00000000..ec377dc2 --- /dev/null +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -0,0 +1,83 @@ +package generatefile + +import ( + "errors" + "fmt" + "mime" + "path/filepath" + "time" + + erc "simrs-vx/internal/domain/references/common" + docscfg "simrs-vx/internal/infra/docs-cfg" + pf "simrs-vx/pkg/file-helper" + pm "simrs-vx/pkg/minio-helper" + pp "simrs-vx/pkg/pdf-helper" +) + +// generate temporary file, upload to minio, generate public url, delete temporary file +func generateFile(input GenerateDto, templateData any) (string, error) { + newPath, err := pf.PathToSaveFile(fmt.Sprintf("./public/%s/%d/%s", input.EntityType_Code, *input.Ref_Id, input.Type_Code)) + if err != nil { + return "", err + } + + fPath := fmt.Sprintf("%s/%s_%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType) + + templatePath := docscfg.O.GetPath() + string(input.TemplateName) + + switch input.FormatType { + case erc.DFTCPDF: + if err := generatePDF(GeneratePDFdto{ + TemplatePath: templatePath, + TemplateData: templateData, + PdfPath: fPath, + }); err != nil { + return "", err + } + case erc.DFTCTXLSX: + // TODO: generate xlsx + case erc.DFTCTCSV: + // TODO: generate csv + default: + return "", errors.New("invalid format type") + } + + bucketName := input.EntityType_Code + + objectName := fmt.Sprintf("%v/%s-%d", *input.Ref_Id, input.Type_Code, time.Now().UnixNano()) + pdfUpload := pm.UploadPathInput{ + BucketName: string(bucketName), + Name: objectName, + Path: fPath, + ContentType: mime.TypeByExtension(filepath.Ext(fPath)), + } + + // create bucket if not exist, create object in bucket + info, err := pm.I.FPutObject(pdfUpload) + if err != nil { + return "", err + } + + // generate public url + urlPub := pm.I.GenerateUrl(info.Bucket, info.Key) + if err := pf.DeleteFolder(fPath); err != nil { + return "", err + } + + return urlPub, nil +} + +func generatePDF(input GeneratePDFdto) error { + // parse template data into html template + r := pp.NewRequestPdf("") + if err := r.ParseTemplate(input.TemplatePath, input.TemplateData); err == nil { + _, err := r.GenerateByCommand(input.PdfPath, input.TemplatePath) + if err != nil { + return errors.New("generate pdf by command error : " + err.Error()) + } + } else { + return errors.New("parse template error : " + err.Error()) + } + + return nil +} diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index 716e277d..17c5abc1 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -14,12 +14,26 @@ type GeneralConsentPDF struct { } type GenerateDto struct { - EntityType_Code ere.EntityTypeCode `form:"entityType_code"` - Ref_Id *uint `form:"ref_id"` - Type_Code ere.DocTypeCode `form:"type_code"` - FormatType erc.DocFormatTypeCode `form:"formatType"` + EntityType_Code ere.EntityTypeCode `json:"entityType_code"` + Ref_Id *uint `json:"ref_id"` + Type_Code ere.DocTypeCode `json:"type_code"` + FormatType erc.DocFormatTypeCode `json:"formatType"` + TemplateName TemplateDocsName `json:"-"` +} + +type GeneratePDFdto struct { + TemplatePath string + TemplateData any + PdfPath string } type ResponseDto struct { FileUrl string `json:"fileUrl"` } + +type TemplateDocsName string + +// TemplateDocsName is the name of the template file in the assets/docs folder +const ( + TDNGC TemplateDocsName = "general-consent.html" +) diff --git a/pkg/file-helper/file-helper.go b/pkg/file-helper/file-helper.go new file mode 100644 index 00000000..bd0849c7 --- /dev/null +++ b/pkg/file-helper/file-helper.go @@ -0,0 +1,33 @@ +package filehelper + +import ( + "fmt" + "os" + "path/filepath" +) + +// const DEFAULT_EXPIRY_FILES = time.Hour * 24 * 7 + +func PathAgreement(medicalNumber string) (string, error) { + outputPath := fmt.Sprintf("./public/patient/%s", medicalNumber) + err := os.MkdirAll(outputPath, os.ModePerm) + return outputPath, err +} + +func PathToSaveFile(outputPath string) (string, error) { + err := os.MkdirAll(outputPath, os.ModePerm) + return outputPath, err +} + +func RenameFile(srcPath, dstPath string) error { + return os.Rename(srcPath, dstPath) +} + +func DeleteFolder(path string) error { + return os.RemoveAll(path) +} + +func PathToUrl(fileName string) *string { + fileUrl := filepath.ToSlash(fmt.Sprintf("%c%s", os.PathSeparator, fileName)) + return &fileUrl +} diff --git a/pkg/pdf-helper/pdf-helper.go b/pkg/pdf-helper/pdf-helper.go new file mode 100644 index 00000000..597052eb --- /dev/null +++ b/pkg/pdf-helper/pdf-helper.go @@ -0,0 +1,131 @@ +package pdfhelper + +import ( + "bytes" + "html/template" + "log" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/SebastiaanKlippert/go-wkhtmltopdf" +) + +// pdf requestpdf struct +type RequestPdf struct { + body string +} + +// new request to pdf function +func NewRequestPdf(body string) *RequestPdf { + return &RequestPdf{ + body: body, + } +} + +// parsing template function +func (r *RequestPdf) ParseTemplate(templatePath string, data interface{}) error { + f := strings.Split(templatePath, "/") + fileName := f[len(f)-1] + funcs := template.FuncMap{ + "nl2br": func(text string) template.HTML { + return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "
", -1)) + }, + } + t, err := template.New(fileName).Funcs(funcs).ParseFiles(templatePath) + if err != nil { + return err + } + buf := new(bytes.Buffer) + if err = t.Execute(buf, data); err != nil { + return err + } + r.body = buf.String() + + return nil +} + +func (r *RequestPdf) GenerateByCommand(pdfPath string, templatePath string) (bool, error) { + // wkhtmltopdf -L 0 -R 0 -B 0 -s Legal --enable-local-file-access resultAntigen2.html out.pdf + t := time.Now().Unix() + + if _, err := os.Stat("cloneTemplate/"); os.IsNotExist(err) { + errDir := os.Mkdir("cloneTemplate/", 0777) + if errDir != nil { + log.Fatal(errDir) + } + } + htmlName := strconv.FormatInt(int64(t), 10) + ".html" + err := os.WriteFile("cloneTemplate/"+htmlName, []byte(r.body), 0644) + if err != nil { + panic(err) + } + + cmd := exec.Command("wkhtmltopdf", "--enable-local-file-access", "-L", "0", "-R", "0", "-B", "0", "-s", "A4", "cloneTemplate/"+htmlName, pdfPath) + + if err := cmd.Run(); err != nil { + return false, err + } + dir, err := os.Getwd() + if err != nil { + panic(err) + } + defer os.RemoveAll(dir + "/cloneTemplate") + return true, nil +} + +// generate pdf function +func (r *RequestPdf) GeneratePDF(pdfPath string) (bool, error) { + t := time.Now().Unix() + + if _, err := os.Stat("cloneTemplate/"); os.IsNotExist(err) { + errDir := os.Mkdir("cloneTemplate/", 0777) + if errDir != nil { + log.Fatal(errDir) + } + } + err1 := os.WriteFile("cloneTemplate/"+strconv.FormatInt(int64(t), 10)+".html", []byte(r.body), 0644) + if err1 != nil { + panic(err1) + } + + f, err := os.Open("cloneTemplate/" + strconv.FormatInt(int64(t), 10) + ".html") + if f != nil { + defer f.Close() + } + if err != nil { + log.Fatal(err) + } + + pdfg, err := wkhtmltopdf.NewPDFGenerator() + if err != nil { + log.Fatal(err) + } + + pdfg.AddPage(wkhtmltopdf.NewPageReader(f)) + + pdfg.PageSize.Set(wkhtmltopdf.PageSizeA4) + + pdfg.Dpi.Set(300) + + err = pdfg.Create() + if err != nil { + log.Fatal(err) + } + + err = pdfg.WriteFile(pdfPath) + if err != nil { + log.Fatal(err) + } + + dir, err := os.Getwd() + if err != nil { + panic(err) + } + + defer os.RemoveAll(dir + "/cloneTemplate") + + return true, nil +} From c8e17619d8f2860d44b137bf7b76281623251bed Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Mon, 17 Nov 2025 17:34:17 +0700 Subject: [PATCH 10/23] feat (generate-file): add validation --- internal/use-case/main-use-case/generate-file/tycovar.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index 17c5abc1..b5d09ceb 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -14,9 +14,9 @@ type GeneralConsentPDF struct { } type GenerateDto struct { - EntityType_Code ere.EntityTypeCode `json:"entityType_code"` - Ref_Id *uint `json:"ref_id"` - Type_Code ere.DocTypeCode `json:"type_code"` + EntityType_Code ere.EntityTypeCode `json:"entityType_code" validate:"required"` + Ref_Id *uint `json:"ref_id" validate:"required"` + Type_Code ere.DocTypeCode `json:"type_code" validate:"required"` FormatType erc.DocFormatTypeCode `json:"formatType"` TemplateName TemplateDocsName `json:"-"` } From e8fbd042b67f0ff5a37667eac633e9dc333a472d Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 17 Nov 2025 18:59:45 +0700 Subject: [PATCH 11/23] add specialist and subspecialist --- .../domain/main-entities/specialist/dto.go | 1 + .../domain/main-entities/subspecialist/dto.go | 1 + .../simgos-sync-handler.go | 4 + .../simgos-sync-handler/specialist/handler.go | 66 ++++++ .../subspecialist/handler.go | 66 ++++++ .../use-case/main-use-case/division/case.go | 9 +- .../use-case/main-use-case/specialist/case.go | 101 +++++---- .../specialist/middleware-runner.go | 38 +++- .../main-use-case/specialist/middleware.go | 23 +- .../main-use-case/specialist/tycovar.go | 31 ++- .../main-use-case/subspecialist/case.go | 91 +++++--- .../subspecialist/middleware-runner.go | 37 ++- .../main-use-case/subspecialist/tycovar.go | 32 ++- .../simgos-sync-plugin/specialist/plugin.go | 167 ++++++++++++++ .../subspecialist/plugin.go | 167 ++++++++++++++ .../simgos-sync-use-case/specialist/case.go | 214 ++++++++++++++++++ .../simgos-sync-use-case/specialist/helper.go | 60 +++++ .../simgos-sync-use-case/specialist/lib.go | 188 +++++++++++++++ .../specialist/middleware-runner.go | 104 +++++++++ .../specialist/middleware.go | 9 + .../specialist/tycovar.go | 44 ++++ .../subspecialist/case.go | 214 ++++++++++++++++++ .../subspecialist/helper.go | 59 +++++ .../simgos-sync-use-case/subspecialist/lib.go | 187 +++++++++++++++ .../subspecialist/middleware-runner.go | 104 +++++++++ .../subspecialist/middleware.go | 9 + .../subspecialist/tycovar.go | 44 ++++ 27 files changed, 1948 insertions(+), 122 deletions(-) create mode 100644 internal/interface/simgos-sync-handler/specialist/handler.go create mode 100644 internal/interface/simgos-sync-handler/subspecialist/handler.go create mode 100644 internal/use-case/simgos-sync-plugin/specialist/plugin.go create mode 100644 internal/use-case/simgos-sync-plugin/subspecialist/plugin.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/case.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/specialist/tycovar.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/case.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/subspecialist/tycovar.go diff --git a/internal/domain/main-entities/specialist/dto.go b/internal/domain/main-entities/specialist/dto.go index a67a830f..2d656556 100644 --- a/internal/domain/main-entities/specialist/dto.go +++ b/internal/domain/main-entities/specialist/dto.go @@ -8,6 +8,7 @@ import ( ) type CreateDto struct { + Id *uint `json:"id"` Code string `json:"code" validate:"maxLength=10"` Name string `json:"name" validate:"maxLength=50"` Unit_Code *string `json:"unit_code"` diff --git a/internal/domain/main-entities/subspecialist/dto.go b/internal/domain/main-entities/subspecialist/dto.go index 6e10b573..72959023 100644 --- a/internal/domain/main-entities/subspecialist/dto.go +++ b/internal/domain/main-entities/subspecialist/dto.go @@ -7,6 +7,7 @@ import ( ) type CreateDto struct { + Id *uint `json:"id"` Code string `json:"code" validate:"maxLength=10"` Name string `json:"name" validate:"maxLength=50"` Specialist_Code *string `json:"specialist_code"` diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index 590d7b9d..6b63e1d6 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -19,6 +19,8 @@ import ( "simrs-vx/internal/interface/main-handler/home" division "simrs-vx/internal/interface/simgos-sync-handler/division" installation "simrs-vx/internal/interface/simgos-sync-handler/installation" + specialist "simrs-vx/internal/interface/simgos-sync-handler/specialist" + subspecialist "simrs-vx/internal/interface/simgos-sync-handler/subspecialist" unit "simrs-vx/internal/interface/simgos-sync-handler/unit" ) @@ -39,6 +41,8 @@ func SetRoutes() http.Handler { hc.SyncCrud(r, prefix+"/v1/installation", installation.O) hc.SyncCrud(r, prefix+"/v1/unit", unit.O) hc.SyncCrud(r, prefix+"/v1/division", division.O) + hc.SyncCrud(r, prefix+"/v1/specialist", specialist.O) + hc.SyncCrud(r, prefix+"/v1/subspecialist", subspecialist.O) return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/interface/simgos-sync-handler/specialist/handler.go b/internal/interface/simgos-sync-handler/specialist/handler.go new file mode 100644 index 00000000..79289472 --- /dev/null +++ b/internal/interface/simgos-sync-handler/specialist/handler.go @@ -0,0 +1,66 @@ +package specialist + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" + u "simrs-vx/internal/use-case/simgos-sync-use-case/specialist" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.CreateSimxLog(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + val := uint16(id) + dto.Id = &val + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + val := uint16(id) + dto.Id = &val + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/simgos-sync-handler/subspecialist/handler.go b/internal/interface/simgos-sync-handler/subspecialist/handler.go new file mode 100644 index 00000000..fcd03c6b --- /dev/null +++ b/internal/interface/simgos-sync-handler/subspecialist/handler.go @@ -0,0 +1,66 @@ +package subspecialist + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/subspecialist" + esync "simrs-vx/internal/domain/sync-entities/log" + u "simrs-vx/internal/use-case/simgos-sync-use-case/subspecialist" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.CreateSimxLog(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + val := uint16(id) + dto.Id = &val + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + val := uint16(id) + dto.Id = &val + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/division/case.go b/internal/use-case/main-use-case/division/case.go index 4be7ce82..8f2e8488 100644 --- a/internal/use-case/main-use-case/division/case.go +++ b/internal/use-case/main-use-case/division/case.go @@ -1,18 +1,19 @@ package division import ( - e "simrs-vx/internal/domain/main-entities/division" - erc "simrs-vx/internal/domain/references/common" - esync "simrs-vx/internal/domain/sync-entities/log" "strconv" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" + "gorm.io/gorm" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" - "gorm.io/gorm" + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/log" ) const source = "division" diff --git a/internal/use-case/main-use-case/specialist/case.go b/internal/use-case/main-use-case/specialist/case.go index c51a0002..f9f5442b 100644 --- a/internal/use-case/main-use-case/specialist/case.go +++ b/internal/use-case/main-use-case/specialist/case.go @@ -1,7 +1,6 @@ package specialist import ( - e "simrs-vx/internal/domain/main-entities/specialist" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -11,6 +10,11 @@ import ( pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" ) const source = "specialist" @@ -25,36 +29,32 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData + id := uint(data.Id) + input.Id = &id } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -80,7 +80,7 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { @@ -131,7 +131,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { @@ -177,6 +177,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -184,32 +185,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := UpdateData(input, data, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(updatePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -233,6 +228,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -240,30 +236,26 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := DeleteData(data, &event, tx); err != nil { return err } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -274,3 +266,32 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/specialist/middleware-runner.go b/internal/use-case/main-use-case/specialist/middleware-runner.go index 4aa6cdd4..c937d2bc 100644 --- a/internal/use-case/main-use-case/specialist/middleware-runner.go +++ b/internal/use-case/main-use-case/specialist/middleware-runner.go @@ -2,6 +2,7 @@ package specialist import ( e "simrs-vx/internal/domain/main-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -15,21 +16,36 @@ type middlewareRunner struct { } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ Event: event, - Tx: tx, } } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Specialist) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// ExecuteCreateMiddleware executes createlog middleware +func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -68,13 +84,13 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -83,13 +99,13 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/specialist/middleware.go b/internal/use-case/main-use-case/specialist/middleware.go index 298e51a8..5d119b01 100644 --- a/internal/use-case/main-use-case/specialist/middleware.go +++ b/internal/use-case/main-use-case/specialist/middleware.go @@ -1,9 +1,20 @@ package specialist +import ( + plugin "simrs-vx/internal/use-case/simgos-sync-plugin/specialist" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPreMw = append(createPreMw, + createMw{Name: "create-unit", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "update-unit", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "delete-unit", Func: plugin.Delete}) +} diff --git a/internal/use-case/main-use-case/specialist/tycovar.go b/internal/use-case/main-use-case/specialist/tycovar.go index 16fa50b0..d0755799 100644 --- a/internal/use-case/main-use-case/specialist/tycovar.go +++ b/internal/use-case/main-use-case/specialist/tycovar.go @@ -12,11 +12,27 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/specialist" + elog "simrs-vx/internal/domain/sync-entities/log" ) type createMw struct { Name string - Func func(input *e.CreateDto, data *e.Specialist, tx *gorm.DB) error + Func func(input *e.CreateDto) error +} + +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + +type updateMw struct { + Name string + Func func(input *e.UpdateDto) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error } type readListMw struct { @@ -29,16 +45,17 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Specialist, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/subspecialist/case.go b/internal/use-case/main-use-case/subspecialist/case.go index 51509c47..ca99750d 100644 --- a/internal/use-case/main-use-case/subspecialist/case.go +++ b/internal/use-case/main-use-case/subspecialist/case.go @@ -2,6 +2,8 @@ package subspecialist import ( e "simrs-vx/internal/domain/main-entities/subspecialist" + erc "simrs-vx/internal/domain/references/common" + esync "simrs-vx/internal/domain/sync-entities/log" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -25,36 +27,32 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData + id := uint(data.Id) + input.Id = &id } - mwRunner.setMwType(pu.MWTPost) + mwRunner.setMwType(pu.MWTPre) // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + if err := mwRunner.RunCreateMiddleware(createPreMw, &input); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -80,7 +78,7 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { @@ -131,7 +129,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { @@ -177,6 +175,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -184,32 +183,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := UpdateData(input, data, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - - mwRunner.setMwType(pu.MWTPost) + mwRunner.setMwType(pu.MWTPre) // Run post-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + if err := mwRunner.RunUpdateMiddleware(updatePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -233,6 +226,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -240,30 +234,26 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := DeleteData(data, &event, tx); err != nil { return err } - mwRunner.setMwType(pu.MWTPost) + mwRunner.setMwType(pu.MWTPre) // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -274,3 +264,32 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/subspecialist/middleware-runner.go b/internal/use-case/main-use-case/subspecialist/middleware-runner.go index c7e5f285..065e5ecc 100644 --- a/internal/use-case/main-use-case/subspecialist/middleware-runner.go +++ b/internal/use-case/main-use-case/subspecialist/middleware-runner.go @@ -2,6 +2,7 @@ package subspecialist import ( e "simrs-vx/internal/domain/main-entities/subspecialist" + esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -15,21 +16,35 @@ type middlewareRunner struct { } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ Event: event, - Tx: tx, } } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Subspecialist) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); 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) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -68,13 +83,13 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -83,13 +98,13 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/subspecialist/tycovar.go b/internal/use-case/main-use-case/subspecialist/tycovar.go index ed4540b8..cd26e517 100644 --- a/internal/use-case/main-use-case/subspecialist/tycovar.go +++ b/internal/use-case/main-use-case/subspecialist/tycovar.go @@ -9,6 +9,8 @@ functionality. The purpose of this is to make the code more maintainable. package subspecialist import ( + elog "simrs-vx/internal/domain/sync-entities/log" + "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/subspecialist" @@ -16,7 +18,22 @@ import ( type createMw struct { Name string - Func func(input *e.CreateDto, data *e.Subspecialist, tx *gorm.DB) error + Func func(input *e.CreateDto) error +} + +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + +type updateMw struct { + Name string + Func func(input *e.UpdateDto) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error } type readListMw struct { @@ -29,16 +46,17 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Subspecialist, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw diff --git a/internal/use-case/simgos-sync-plugin/specialist/plugin.go b/internal/use-case/simgos-sync-plugin/specialist/plugin.go new file mode 100644 index 00000000..892d7a17 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/specialist/plugin.go @@ -0,0 +1,167 @@ +package specialist + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/specialist" + elog "simrs-vx/internal/domain/sync-entities/log" + + d "github.com/karincake/dodol" +) + +func Create(input *e.CreateDto) error { + endpoint := getPrefixEndpoint() + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Update(input *e.UpdateDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/specialist", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-plugin/subspecialist/plugin.go b/internal/use-case/simgos-sync-plugin/subspecialist/plugin.go new file mode 100644 index 00000000..15d6f650 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/subspecialist/plugin.go @@ -0,0 +1,167 @@ +package subspecialist + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/subspecialist" + elog "simrs-vx/internal/domain/sync-entities/log" + + d "github.com/karincake/dodol" +) + +func Create(input *e.CreateDto) error { + endpoint := getPrefixEndpoint() + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Update(input *e.UpdateDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/subspecialist", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-use-case/specialist/case.go b/internal/use-case/simgos-sync-use-case/specialist/case.go new file mode 100644 index 00000000..4e6de31d --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/case.go @@ -0,0 +1,214 @@ +package specialist + +import ( + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/specialist" + esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" +) + +const source = "specialist" + +func Create(input e.CreateDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + ) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // STEP 1: Insert to simgos + sgData, err := CreateSimgosData(input, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 2: Insert to Link + syncLink, err := CreateLinkData(*input.Id, sgData.Id, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 3: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } + + if err = txSimgos.Commit().Error; err != nil { + // STEP 4: Rollback Partial + go func() { + _ = DeleteLinkData(syncLink, &event) + }() + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + // InsertSimxLog + if err := CreateLogData(input, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + var ( + txSimgos = dg.IS["simrs"].Begin() + err error + ) + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + // STEP 1: Get Installation Link + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err + } + + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + }, nil +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MPolihfis + err error + ) + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + // STEP 1: Get Installation Link + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { + return nil, err + } + + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + // STEP 3: Delete M_Unit Simgos + err = HardDeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 4: Commit two trx + if err = txSync.Commit().Error; err != nil { + txSync.Rollback() + txSimgos.Rollback() + return nil, err + } + + if err = txSimgos.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.Id, &event) + }() + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/specialist/helper.go b/internal/use-case/simgos-sync-use-case/specialist/helper.go new file mode 100644 index 00000000..95839354 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/helper.go @@ -0,0 +1,60 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package specialist + +import ( + "encoding/json" + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/specialist" + esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esyncLog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/specialist" +) + +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MPolihfis) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Code = inputSrc.Code + data.Poliklinik = inputSrc.Name + data.Active = 1 + data.Status = 1 + + return +} + +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.SpecialistSimxLog) { + // encode to JSON + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") + jsonString := string(jsonData) + + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + + data.Value = &jsonString + data.Date = &now + data.Status = status + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.SpecialistLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return +} diff --git a/internal/use-case/simgos-sync-use-case/specialist/lib.go b/internal/use-case/simgos-sync-use-case/specialist/lib.go new file mode 100644 index 00000000..c7123f0b --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/lib.go @@ -0,0 +1,188 @@ +package specialist + +import ( + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/specialist" + esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esynclog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/specialist" +) + +var now = time.Now() + +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPolihfis, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := setDataSimgos(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPolihfis, error) { + pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") + data := esimgos.MPolihfis{} + + var tx = dg.IS["simrs"] + + if err := tx. + Where("\"id\" = ?", simgosId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.SpecialistLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.Id = dataSimgos.Simgos_Id + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func HardDeleteSimgosData(data *esimgos.MPolihfis, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx. + Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.SpecialistLink, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.SpecialistLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.SpecialistLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.SpecialistLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/specialist/middleware-runner.go b/internal/use-case/simgos-sync-use-case/specialist/middleware-runner.go new file mode 100644 index 00000000..938c1f92 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/middleware-runner.go @@ -0,0 +1,104 @@ +package specialist + +import ( + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/specialist" +) + +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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Specialist) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Specialist) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) 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/simgos-sync-use-case/specialist/middleware.go b/internal/use-case/simgos-sync-use-case/specialist/middleware.go new file mode 100644 index 00000000..298e51a8 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/middleware.go @@ -0,0 +1,9 @@ +package specialist + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/specialist/tycovar.go b/internal/use-case/simgos-sync-use-case/specialist/tycovar.go new file mode 100644 index 00000000..16fa50b0 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/tycovar.go @@ -0,0 +1,44 @@ +/* +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 specialist + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/specialist" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Specialist, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Specialist, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Specialist, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/case.go b/internal/use-case/simgos-sync-use-case/subspecialist/case.go new file mode 100644 index 00000000..e16d50bd --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/case.go @@ -0,0 +1,214 @@ +package subspecialist + +import ( + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/subspecialist" + //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" +) + +const source = "subspecialist" + +func Create(input e.CreateDto) (*d.Data, error) { + //var ( + // txSync = dg.I.Begin() + // txSimgos = dg.IS["simrs"].Begin() + //) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + //// STEP 1: Insert to simgos + //sgData, err := CreateSimgosData(input, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 2: Insert to Link + //syncLink, err := CreateLinkData(*input.Id, sgData.Id, &event, txSync) + //if err != nil { + // txSimgos.Rollback() + // return nil, err + //} + // + //// STEP 3: Commit two trx + //if err = txSync.Commit().Error; err != nil { + // txSync.Rollback() + // txSimgos.Rollback() + // return nil, err + //} + // + //if err = txSimgos.Commit().Error; err != nil { + // // STEP 4: Rollback Partial + // go func() { + // _ = DeleteLinkData(syncLink, &event) + // }() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + // InsertSimxLog + if err := CreateLogData(input, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + //var ( + // txSimgos = dg.IS["simrs"].Begin() + // err error + //) + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + //// STEP 1: Get Installation Link + //syncLink, err := ReadDetailLinkData(*input.Id, &event) + //if err != nil { + // return nil, err + //} + // + //// Step 2: Update Simgos + //err = UpdateSimgosData(input, syncLink, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 4: Commit two trx + //if err = txSimgos.Commit().Error; err != nil { + // txSimgos.Rollback() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + }, nil +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + //var ( + // txSync = dg.I.Begin() + // txSimgos = dg.IS["simrs"].Begin() + // sgData *esimgos.MPolihfis + // err error + //) + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + //// STEP 1: Get Installation Link + //syncLink, err := ReadDetailLinkData(*input.Id, &event) + //if err != nil { + // return nil, err + //} + // + //// STEP 2: Get Simgos + //sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + //if err != nil { + // return nil, err + //} + // + //// STEP 3: Delete M_Unit Simgos + //err = HardDeleteSimgosData(sgData, &event, txSimgos) + //if err != nil { + // return nil, err + //} + // + //// STEP 4: Delete Installation Link + //err = DeleteLinkData(syncLink, &event, txSync) + //if err != nil { + // txSimgos.Rollback() + // return nil, err + //} + // + //// STEP 4: Commit two trx + //if err = txSync.Commit().Error; err != nil { + // txSync.Rollback() + // txSimgos.Rollback() + // return nil, err + //} + // + //if err = txSimgos.Commit().Error; err != nil { + // // STEP 5: Rollback Partial + // go func() { + // _, _ = CreateLinkData(uint(*input.Id), sgData.Id, &event) + // }() + // return nil, err + //} + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/helper.go b/internal/use-case/simgos-sync-use-case/subspecialist/helper.go new file mode 100644 index 00000000..898f161c --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/helper.go @@ -0,0 +1,59 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package subspecialist + +import ( + "encoding/json" + erc "simrs-vx/internal/domain/references/common" + //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esyncLog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/specialist" +) + +//func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MPolihfis) { +// var inputSrc *e.CreateDto +// if inputT, ok := any(input).(*e.CreateDto); ok { +// inputSrc = inputT +// } else { +// inputTemp := any(input).(*e.UpdateDto) +// inputSrc = &inputTemp.CreateDto +// } +// +// data.Code = inputSrc.Code +// data.Poliklinik = inputSrc.Name +// data.CodePolirs = *inputSrc.Unit_Code +// data.Active = 1 +// data.Status = 1 +// +// return +//} + +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.SpecialistSimxLog) { + // encode to JSON + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") + jsonString := string(jsonData) + + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + + data.Value = &jsonString + data.Date = &now + data.Status = status + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.SpecialistLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return +} diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/lib.go b/internal/use-case/simgos-sync-use-case/subspecialist/lib.go new file mode 100644 index 00000000..781e899d --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/lib.go @@ -0,0 +1,187 @@ +package subspecialist + +import ( + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + esynclog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/specialist" +) + +var now = time.Now() + +//func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPolihfis, error) { +// pl.SetLogInfo(event, nil, "started", "DBCreate") +// +// data := setDataSimgos(&input) +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx.Create(&data).Error; err != nil { +// return nil, plh.HandleCreateError(input, event, err) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return &data, nil +//} + +//func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPolihfis, error) { +// pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") +// data := esimgos.MPolihfis{} +// +// var tx = dg.IS["simrs"] +// +// if err := tx. +// Where("\"kode_unit\" = ?", simgosId). +// First(&data).Error; err != nil { +// if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { +// return nil, processedErr +// } +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return &data, nil +//} + +//func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.SpecialistLink, event *pl.Event, dbx ...*gorm.DB) error { +// pl.SetLogInfo(event, input, "started", "DBUpdate") +// +// data := setDataSimgos(&input) +// data.Id = dataSimgos.Simgos_Id +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx.Save(&data).Error; err != nil { +// event.Status = "failed" +// event.ErrInfo = pl.ErrorInfo{ +// Code: "data-update-fail", +// Detail: "Database update failed", +// Raw: err, +// } +// return pl.SetLogError(event, input) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return nil +//} + +//func HardDeleteSimgosData(data *esimgos.MPolihfis, event *pl.Event, dbx ...*gorm.DB) error { +// pl.SetLogInfo(event, data, "started", "DBDelete") +// +// var tx *gorm.DB +// if len(dbx) > 0 { +// tx = dbx[0] +// } else { +// tx = dg.IS["simrs"] +// } +// +// if err := tx. +// Delete(&data).Error; err != nil { +// event.Status = "failed" +// event.ErrInfo = pl.ErrorInfo{ +// Code: "data-delete-fail", +// Detail: "Database delete failed", +// Raw: err, +// } +// return pl.SetLogError(event, data) +// } +// +// pl.SetLogInfo(event, nil, "complete") +// return nil +//} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.SpecialistLink, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.SpecialistLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.SpecialistLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.SpecialistLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/middleware-runner.go b/internal/use-case/simgos-sync-use-case/subspecialist/middleware-runner.go new file mode 100644 index 00000000..134041f1 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/middleware-runner.go @@ -0,0 +1,104 @@ +package subspecialist + +import ( + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/subspecialist" +) + +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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Subspecialist) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Subspecialist) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) 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/simgos-sync-use-case/subspecialist/middleware.go b/internal/use-case/simgos-sync-use-case/subspecialist/middleware.go new file mode 100644 index 00000000..ad2ba452 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/middleware.go @@ -0,0 +1,9 @@ +package subspecialist + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/tycovar.go b/internal/use-case/simgos-sync-use-case/subspecialist/tycovar.go new file mode 100644 index 00000000..ed4540b8 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/tycovar.go @@ -0,0 +1,44 @@ +/* +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 subspecialist + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/subspecialist" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Subspecialist, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Subspecialist, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Subspecialist, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From 938ec89c20e3c7b24e9a72d4c2d238bb2d377ef2 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 18 Nov 2025 09:58:41 +0700 Subject: [PATCH 12/23] add enabled sync --- cmd/main-api/config.yml-example | 1 + internal/infra/sync-cfg/tycovar.go | 1 + .../division/middleware-runner.go | 35 +++++++++++++++++-- .../installation/middleware-runner.go | 30 +++++++++++++++- .../specialist/middleware-runner.go | 35 +++++++++++++++++-- .../subspecialist/middleware-runner.go | 35 +++++++++++++++++-- .../main-use-case/unit/middleware-runner.go | 35 +++++++++++++++++-- 7 files changed, 159 insertions(+), 13 deletions(-) diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index 448cff66..b2426b1f 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -69,5 +69,6 @@ bpjsCfg: baseUrl: syncUrlCfg: + enable: false host: prefix: new-to-old \ No newline at end of file diff --git a/internal/infra/sync-cfg/tycovar.go b/internal/infra/sync-cfg/tycovar.go index fdcda00b..6ae230fb 100644 --- a/internal/infra/sync-cfg/tycovar.go +++ b/internal/infra/sync-cfg/tycovar.go @@ -5,4 +5,5 @@ var O SyncUrlCfg = SyncUrlCfg{} type SyncUrlCfg struct { Prefix string `yaml:"prefix"` Host string `yaml:"host"` + Enable bool `yaml:"enable"` } diff --git a/internal/use-case/main-use-case/division/middleware-runner.go b/internal/use-case/main-use-case/division/middleware-runner.go index b39e3176..51c90d77 100644 --- a/internal/use-case/main-use-case/division/middleware-runner.go +++ b/internal/use-case/main-use-case/division/middleware-runner.go @@ -1,29 +1,38 @@ package division import ( - e "simrs-vx/internal/domain/main-entities/division" - esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/division" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, + Event: event, + SyncOn: sync.O.Enable, } } // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -40,6 +49,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e // ExecuteCreateMiddleware executes createlog middleware func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -55,6 +68,10 @@ func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, in } func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Division) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -70,6 +87,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Division) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -85,6 +106,10 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, } func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -100,6 +125,10 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e } func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) diff --git a/internal/use-case/main-use-case/installation/middleware-runner.go b/internal/use-case/main-use-case/installation/middleware-runner.go index 5a8e2ad0..b85e3236 100644 --- a/internal/use-case/main-use-case/installation/middleware-runner.go +++ b/internal/use-case/main-use-case/installation/middleware-runner.go @@ -6,6 +6,8 @@ import ( "gorm.io/gorm" + sync "simrs-vx/internal/infra/sync-cfg" + e "simrs-vx/internal/domain/main-entities/installation" esync "simrs-vx/internal/domain/sync-entities/log" ) @@ -14,17 +16,23 @@ type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, + Event: event, + SyncOn: sync.O.Enable, } } // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -41,6 +49,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -56,6 +68,10 @@ func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, in } func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Installation) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -71,6 +87,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -86,6 +106,10 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, } func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e.UpdateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -101,6 +125,10 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e } func (me *middlewareRunner) RunDeleteMiddleware(middlewares []DeleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) diff --git a/internal/use-case/main-use-case/specialist/middleware-runner.go b/internal/use-case/main-use-case/specialist/middleware-runner.go index c937d2bc..9a10d624 100644 --- a/internal/use-case/main-use-case/specialist/middleware-runner.go +++ b/internal/use-case/main-use-case/specialist/middleware-runner.go @@ -1,29 +1,38 @@ package specialist import ( - e "simrs-vx/internal/domain/main-entities/specialist" - esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/specialist" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, + Event: event, + SyncOn: sync.O.Enable, } } // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -40,6 +49,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e // ExecuteCreateMiddleware executes createlog middleware func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -55,6 +68,10 @@ func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, in } func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Specialist) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -70,6 +87,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Specialist) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -85,6 +106,10 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, } func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -100,6 +125,10 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e } func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) diff --git a/internal/use-case/main-use-case/subspecialist/middleware-runner.go b/internal/use-case/main-use-case/subspecialist/middleware-runner.go index 065e5ecc..ad1eba08 100644 --- a/internal/use-case/main-use-case/subspecialist/middleware-runner.go +++ b/internal/use-case/main-use-case/subspecialist/middleware-runner.go @@ -1,29 +1,38 @@ package subspecialist import ( - e "simrs-vx/internal/domain/main-entities/subspecialist" - esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/subspecialist" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, + Event: event, + SyncOn: sync.O.Enable, } } // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -39,6 +48,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e } func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -54,6 +67,10 @@ func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, in } func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Subspecialist) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -69,6 +86,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Subspecialist) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -84,6 +105,10 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, } func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -99,6 +124,10 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e } func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) diff --git a/internal/use-case/main-use-case/unit/middleware-runner.go b/internal/use-case/main-use-case/unit/middleware-runner.go index f45fe208..979548bc 100644 --- a/internal/use-case/main-use-case/unit/middleware-runner.go +++ b/internal/use-case/main-use-case/unit/middleware-runner.go @@ -1,29 +1,38 @@ package unit import ( - e "simrs-vx/internal/domain/main-entities/unit" - esync "simrs-vx/internal/domain/sync-entities/log" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/unit" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, + Event: event, + SyncOn: sync.O.Enable, } } // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -40,6 +49,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e // ExecuteCreateMiddleware executes create middleware func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -55,6 +68,10 @@ func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, in } func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Unit) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -70,6 +87,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Unit) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -85,6 +106,10 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, } func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.UpdateDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) @@ -100,6 +125,10 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e } func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, nil) From 12dda8a6782532120782b838bd6d444f345574f6 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 18 Nov 2025 12:02:10 +0700 Subject: [PATCH 13/23] update dual trx --- .../simgos-sync-use-case/division/case.go | 145 ++++++++---------- .../simgos-sync-use-case/installation/case.go | 144 ++++++++--------- .../simgos-sync-use-case/specialist/case.go | 145 ++++++++---------- .../subspecialist/case.go | 142 ++++++++--------- .../simgos-sync-use-case/unit/case.go | 142 ++++++++--------- pkg/dualtrx-helper/dualtrx-helper.go | 66 ++++++++ 6 files changed, 384 insertions(+), 400 deletions(-) create mode 100644 pkg/dualtrx-helper/dualtrx-helper.go diff --git a/internal/use-case/simgos-sync-use-case/division/case.go b/internal/use-case/simgos-sync-use-case/division/case.go index 14dd0886..699848e9 100644 --- a/internal/use-case/simgos-sync-use-case/division/case.go +++ b/internal/use-case/simgos-sync-use-case/division/case.go @@ -3,21 +3,24 @@ package division import ( pl "simrs-vx/pkg/logger" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" + dualtx "simrs-vx/pkg/dualtrx-helper" + e "simrs-vx/internal/domain/main-entities/division" esimgos "simrs-vx/internal/domain/simgos-entities/division" - esync "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/division" + elog "simrs-vx/internal/domain/sync-entities/log" ) const source = "division" func Create(input e.CreateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MUnit + syncLink *esync.DivisionLink + err error ) event := pl.Event{ @@ -28,31 +31,26 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - // STEP 1: Insert to simgos - sgData, err := CreateSimgosData(input, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 1: Insert to simgos + sgData, err = CreateSimgosData(input, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 2: Insert to Link + syncLink, err = CreateLinkData(*input.Id, sgData.KodeUnit, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + if err != nil { - return nil, err - } - - // STEP 2: Insert to Link - syncLink, err := CreateLinkData(*input.Id, sgData.KodeUnit, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 3: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 4: Rollback Partial - go func() { - _ = DeleteLinkData(syncLink, &event) - }() + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } return nil, err } @@ -67,7 +65,7 @@ func Create(input e.CreateDto) (*d.Data, error) { }, nil } -func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -76,14 +74,13 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { - // InsertSimxLog + tx := dualtx.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) @@ -103,11 +100,6 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - var ( - txSimgos = dg.IS["simrs"].Begin() - err error - ) - event := pl.Event{ Feature: "Update", Source: source, @@ -116,23 +108,21 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") - // STEP 1: Get Installation Link + // STEP 1: Get Link syncLink, err := ReadDetailLinkData(*input.Id, &event) if err != nil { return nil, err } - // Step 2: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + tx := dualtx.NewTx() + err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // Step 2: Update Simgos + if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { + return err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - return nil, err - } + return nil + }) pl.SetLogInfo(&event, nil, "complete") @@ -146,12 +136,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MUnit - err error - ) + var isLinkDeleted bool event := pl.Event{ Feature: "Delete", @@ -161,43 +146,41 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") - // STEP 1: Get Installation Link + // STEP 1: Get Link syncLink, err := ReadDetailLinkData(*input.Id, &event) if err != nil { return nil, err } // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) if err != nil { return nil, err } - // STEP 3: Delete M_Unit Simgos - err = HardDeleteSimgosData(sgData, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 3: Delete Simgos + err = HardDeleteSimgosData(sgData, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 4: Delete Link + err = DeleteLinkData(syncLink, &event, tx.Sync) + if err != nil { + return err + } + + isLinkDeleted = true + return nil + }) + if err != nil { - return nil, err - } - - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 4: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - _, _ = CreateLinkData(uint(*input.Id), sgData.KodeUnit, &event) - }() + if isLinkDeleted { + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.KodeUnit, &event) + }() + } return nil, err } diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index c920fca1..f4b8d2b1 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,24 +1,27 @@ package installation import ( - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" "gorm.io/gorm" + dualtx "simrs-vx/pkg/dualtrx-helper" + e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/installation" + elog "simrs-vx/internal/domain/sync-entities/log" ) const source = "installation" func Create(input e.CreateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MInstalasi + syncLink *esync.InstallationLink + err error ) event := pl.Event{ @@ -29,31 +32,26 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - // STEP 1: Insert to simgos - sgData, err := CreateSimgosData(input, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 1: Insert to simgos + sgData, err = CreateSimgosData(input, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 2: Insert to Link + syncLink, err = CreateLinkData(*input.Id, sgData.No_Instalasi, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + if err != nil { - return nil, err - } - - // STEP 2: Insert to Link - syncLink, err := CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 3: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 4: Rollback Partial - go func() { - _ = DeleteLinkData(syncLink, &event) - }() + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } return nil, err } @@ -68,7 +66,7 @@ func Create(input e.CreateDto) (*d.Data, error) { }, nil } -func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -77,14 +75,13 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { - // InsertSimxLog + tx := dualtx.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) @@ -104,11 +101,6 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - var ( - txSimgos = dg.IS["simrs"].Begin() - err error - ) - event := pl.Event{ Feature: "Update", Source: source, @@ -117,23 +109,21 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") - // STEP 1: Get Installation Link + // STEP 1: Get Link syncLink, err := ReadDetailLinkData(*input.Id, &event) if err != nil { return nil, err } - // Step 2: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + tx := dualtx.NewTx() + err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // Step 2: Update Simgos + if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { + return err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - return nil, err - } + return nil + }) pl.SetLogInfo(&event, nil, "complete") @@ -147,12 +137,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MInstalasi - err error - ) + var isLinkDeleted bool event := pl.Event{ Feature: "Delete", @@ -162,43 +147,42 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") - // STEP 1: Get Installation Link + // STEP 1: Get Link syncLink, err := ReadDetailLinkData(*input.Id, &event) if err != nil { return nil, err } // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) if err != nil { return nil, err } - // STEP 3: Delete M_Instalation Simgos - err = SoftDeleteSimgosData(sgData, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 3: Delete Simgos + err = SoftDeleteSimgosData(sgData, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 4: Delete Link + err = DeleteLinkData(syncLink, &event, tx.Sync) + if err != nil { + return err + } + + isLinkDeleted = true + return nil + }) + if err != nil { - return nil, err - } + if isLinkDeleted { + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.No_Instalasi, &event) + }() + } - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 4: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - _, _ = CreateLinkData(uint(*input.Id), sgData.No_Instalasi, &event) - }() return nil, err } diff --git a/internal/use-case/simgos-sync-use-case/specialist/case.go b/internal/use-case/simgos-sync-use-case/specialist/case.go index 4e6de31d..58da2a2f 100644 --- a/internal/use-case/simgos-sync-use-case/specialist/case.go +++ b/internal/use-case/simgos-sync-use-case/specialist/case.go @@ -3,21 +3,24 @@ package specialist import ( pl "simrs-vx/pkg/logger" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" + dualtx "simrs-vx/pkg/dualtrx-helper" + e "simrs-vx/internal/domain/main-entities/specialist" esimgos "simrs-vx/internal/domain/simgos-entities/specialist" - esync "simrs-vx/internal/domain/sync-entities/log" + elog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/specialist" ) const source = "specialist" func Create(input e.CreateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MPolihfis + syncLink *esync.SpecialistLink + err error ) event := pl.Event{ @@ -28,34 +31,28 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - // STEP 1: Insert to simgos - sgData, err := CreateSimgosData(input, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 1: Insert to simgos + sgData, err = CreateSimgosData(input, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 2: Insert to Link + syncLink, err = CreateLinkData(*input.Id, sgData.Id, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + if err != nil { + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } return nil, err } - - // STEP 2: Insert to Link - syncLink, err := CreateLinkData(*input.Id, sgData.Id, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 3: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 4: Rollback Partial - go func() { - _ = DeleteLinkData(syncLink, &event) - }() - return nil, err - } - pl.SetLogInfo(&event, nil, "complete") return &d.Data{ @@ -67,7 +64,7 @@ func Create(input e.CreateDto) (*d.Data, error) { }, nil } -func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -76,14 +73,13 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { - // InsertSimxLog + tx := dualtx.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) @@ -103,11 +99,6 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - var ( - txSimgos = dg.IS["simrs"].Begin() - err error - ) - event := pl.Event{ Feature: "Update", Source: source, @@ -122,17 +113,16 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - // Step 2: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + tx := dualtx.NewTx() + err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, tx) + if err != nil { + return err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - return nil, err - } + return nil + }) pl.SetLogInfo(&event, nil, "complete") @@ -146,12 +136,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MPolihfis - err error - ) + var isLinkDeleted bool event := pl.Event{ Feature: "Delete", @@ -161,43 +146,41 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") - // STEP 1: Get Installation Link + // STEP 1: Get Link syncLink, err := ReadDetailLinkData(*input.Id, &event) if err != nil { return nil, err } // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) if err != nil { return nil, err } - // STEP 3: Delete M_Unit Simgos - err = HardDeleteSimgosData(sgData, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 3: Delete Simgos + err = HardDeleteSimgosData(sgData, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 4: Delete Link + err = DeleteLinkData(syncLink, &event, tx.Sync) + if err != nil { + return err + } + + isLinkDeleted = true + return nil + }) + if err != nil { - return nil, err - } - - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 4: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - _, _ = CreateLinkData(uint(*input.Id), sgData.Id, &event) - }() + if isLinkDeleted { + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.Id, &event) + }() + } return nil, err } diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/case.go b/internal/use-case/simgos-sync-use-case/subspecialist/case.go index e16d50bd..50b6c09b 100644 --- a/internal/use-case/simgos-sync-use-case/subspecialist/case.go +++ b/internal/use-case/simgos-sync-use-case/subspecialist/case.go @@ -1,23 +1,25 @@ package subspecialist import ( + dualtx "simrs-vx/pkg/dualtrx-helper" pl "simrs-vx/pkg/logger" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/subspecialist" //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" - esync "simrs-vx/internal/domain/sync-entities/log" + elog "simrs-vx/internal/domain/sync-entities/log" + //esync "simrs-vx/internal/domain/sync-entities/subspecialist" ) const source = "subspecialist" func Create(input e.CreateDto) (*d.Data, error) { //var ( - // txSync = dg.I.Begin() - // txSimgos = dg.IS["simrs"].Begin() + // sgData *esimgos.MUnit + // syncLink *esync.SubspecialistLink + // err error //) event := pl.Event{ @@ -28,31 +30,26 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - //// STEP 1: Insert to simgos - //sgData, err := CreateSimgosData(input, &event, txSimgos) + //err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // // STEP 1: Insert to simgos + // sgData, err = CreateSimgosData(input, &event, tx.Simgos) + // if err != nil { + // return err + // } + // + // // STEP 2: Insert to Link + // syncLink, err = CreateLinkData(*input.Id, sgData.KodeUnit, &event, tx.Sync) + // if err != nil { + // return err + // } + // + // return nil + //}) + // //if err != nil { - // return nil, err - //} - // - //// STEP 2: Insert to Link - //syncLink, err := CreateLinkData(*input.Id, sgData.Id, &event, txSync) - //if err != nil { - // txSimgos.Rollback() - // return nil, err - //} - // - //// STEP 3: Commit two trx - //if err = txSync.Commit().Error; err != nil { - // txSync.Rollback() - // txSimgos.Rollback() - // return nil, err - //} - // - //if err = txSimgos.Commit().Error; err != nil { - // // STEP 4: Rollback Partial - // go func() { - // _ = DeleteLinkData(syncLink, &event) - // }() + // if syncLink != nil { + // go func() { _ = DeleteLinkData(syncLink, &event) }() + // } // return nil, err //} @@ -67,7 +64,7 @@ func Create(input e.CreateDto) (*d.Data, error) { }, nil } -func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -76,8 +73,9 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { - // InsertSimxLog + tx := dualtx.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { return err } @@ -103,11 +101,6 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - //var ( - // txSimgos = dg.IS["simrs"].Begin() - // err error - //) - event := pl.Event{ Feature: "Update", Source: source, @@ -116,23 +109,21 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") - //// STEP 1: Get Installation Link + //// STEP 1: Get Link //syncLink, err := ReadDetailLinkData(*input.Id, &event) //if err != nil { // return nil, err //} // - //// Step 2: Update Simgos - //err = UpdateSimgosData(input, syncLink, &event, txSimgos) - //if err != nil { - // return nil, err - //} + //tx := dualtx.NewTx() + //err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // // Step 2: Update Simgos + // if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { + // return err + // } // - //// STEP 4: Commit two trx - //if err = txSimgos.Commit().Error; err != nil { - // txSimgos.Rollback() - // return nil, err - //} + // return nil + //}) pl.SetLogInfo(&event, nil, "complete") @@ -146,12 +137,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - //var ( - // txSync = dg.I.Begin() - // txSimgos = dg.IS["simrs"].Begin() - // sgData *esimgos.MPolihfis - // err error - //) + //var isLinkDeleted bool event := pl.Event{ Feature: "Delete", @@ -161,43 +147,41 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") - //// STEP 1: Get Installation Link + //// STEP 1: Get Link //syncLink, err := ReadDetailLinkData(*input.Id, &event) //if err != nil { // return nil, err //} // //// STEP 2: Get Simgos - //sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + //sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) //if err != nil { // return nil, err //} // - //// STEP 3: Delete M_Unit Simgos - //err = HardDeleteSimgosData(sgData, &event, txSimgos) + //err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // // STEP 3: Delete Simgos + // err = HardDeleteSimgosData(sgData, &event, tx.Simgos) + // if err != nil { + // return err + // } + // + // // STEP 4: Delete Link + // err = DeleteLinkData(syncLink, &event, tx.Sync) + // if err != nil { + // return err + // } + // + // isLinkDeleted = true + // return nil + //}) + // //if err != nil { - // return nil, err - //} - // - //// STEP 4: Delete Installation Link - //err = DeleteLinkData(syncLink, &event, txSync) - //if err != nil { - // txSimgos.Rollback() - // return nil, err - //} - // - //// STEP 4: Commit two trx - //if err = txSync.Commit().Error; err != nil { - // txSync.Rollback() - // txSimgos.Rollback() - // return nil, err - //} - // - //if err = txSimgos.Commit().Error; err != nil { - // // STEP 5: Rollback Partial - // go func() { - // _, _ = CreateLinkData(uint(*input.Id), sgData.Id, &event) - // }() + // if isLinkDeleted { + // go func() { + // _, _ = CreateLinkData(uint(*input.Id), sgData.KodeUnit, &event) + // }() + // } // return nil, err //} diff --git a/internal/use-case/simgos-sync-use-case/unit/case.go b/internal/use-case/simgos-sync-use-case/unit/case.go index bb87f1c6..42094d22 100644 --- a/internal/use-case/simgos-sync-use-case/unit/case.go +++ b/internal/use-case/simgos-sync-use-case/unit/case.go @@ -3,21 +3,24 @@ package unit import ( pl "simrs-vx/pkg/logger" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" + dualtx "simrs-vx/pkg/dualtrx-helper" + e "simrs-vx/internal/domain/main-entities/unit" esimgos "simrs-vx/internal/domain/simgos-entities/unit" - esync "simrs-vx/internal/domain/sync-entities/log" + elog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/unit" ) const source = "unit" func Create(input e.CreateDto) (*d.Data, error) { var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MPloy + syncLink *esync.UnitLink + err error ) event := pl.Event{ @@ -28,31 +31,26 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - // STEP 1: Insert to simgos - sgData, err := CreateSimgosData(input, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 1: Insert to simgos + sgData, err = CreateSimgosData(input, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 2: Insert to Link + syncLink, err = CreateLinkData(*input.Id, sgData.Kode, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + if err != nil { - return nil, err - } - - // STEP 2: Insert to Link - syncLink, err := CreateLinkData(*input.Id, sgData.Kode, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 3: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 4: Rollback Partial - go func() { - _ = DeleteLinkData(syncLink, &event) - }() + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } return nil, err } @@ -67,7 +65,7 @@ func Create(input e.CreateDto) (*d.Data, error) { }, nil } -func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -76,14 +74,13 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { - // InsertSimxLog + tx := dualtx.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) @@ -103,11 +100,6 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { } func Update(input e.UpdateDto) (*d.Data, error) { - var ( - txSimgos = dg.IS["simrs"].Begin() - err error - ) - event := pl.Event{ Feature: "Update", Source: source, @@ -122,17 +114,16 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - // Step 2: Update Simgos - err = UpdateSimgosData(input, syncLink, &event, txSimgos) - if err != nil { - return nil, err - } + tx := dualtx.NewTx() + err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // Step 2: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, tx) + if err != nil { + return err + } - // STEP 4: Commit two trx - if err = txSimgos.Commit().Error; err != nil { - txSimgos.Rollback() - return nil, err - } + return nil + }) pl.SetLogInfo(&event, nil, "complete") @@ -146,12 +137,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - var ( - txSync = dg.I.Begin() - txSimgos = dg.IS["simrs"].Begin() - sgData *esimgos.MPloy - err error - ) + var isLinkDeleted bool event := pl.Event{ Feature: "Delete", @@ -168,36 +154,34 @@ func Delete(input e.DeleteDto) (*d.Data, error) { } // STEP 2: Get Simgos - sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) if err != nil { return nil, err } - // STEP 3: Delete M_Poly Simgos - err = HardDeleteSimgosData(sgData, &event, txSimgos) + err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + // STEP 3: Delete M_Poly Simgos + err = HardDeleteSimgosData(sgData, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, tx.Sync) + if err != nil { + return err + } + + isLinkDeleted = true + return nil + }) + if err != nil { - return nil, err - } - - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } - - // STEP 4: Commit two trx - if err = txSync.Commit().Error; err != nil { - txSync.Rollback() - txSimgos.Rollback() - return nil, err - } - - if err = txSimgos.Commit().Error; err != nil { - // STEP 5: Rollback Partial - go func() { - _, _ = CreateLinkData(uint(*input.Id), sgData.Kode, &event) - }() + if isLinkDeleted { + go func() { + _, _ = CreateLinkData(uint(*input.Id), sgData.Kode, &event) + }() + } return nil, err } diff --git a/pkg/dualtrx-helper/dualtrx-helper.go b/pkg/dualtrx-helper/dualtrx-helper.go new file mode 100644 index 00000000..2f16203c --- /dev/null +++ b/pkg/dualtrx-helper/dualtrx-helper.go @@ -0,0 +1,66 @@ +package dualtrx_helper + +import ( + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" +) + +type Dualtx struct { + Sync *gorm.DB + Simgos *gorm.DB +} + +func NewDualtx() *Dualtx { + return &Dualtx{ + Sync: dg.I.Begin(), + Simgos: dg.IS["simrs"].Begin(), + } +} + +func NewTx() *Dualtx { + return &Dualtx{ + Sync: dg.I, + Simgos: dg.IS["simrs"], + } +} + +func (t *Dualtx) Commit() error { + if err := t.Sync.Commit().Error; err != nil { + return err + } + + if err := t.Simgos.Commit().Error; err != nil { + return err + } + return nil +} + +func (t *Dualtx) Rollback() { + t.Sync.Rollback() + t.Simgos.Rollback() +} + +type DualTxFunc func(tx *Dualtx) error + +func WithDualTx(fn DualTxFunc) error { + var ( + tx = NewDualtx() + err error + ) + + defer func() { + if err != nil { + tx.Rollback() + } + }() + + if err = fn(tx); err != nil { + return err + } + + if err = tx.Commit(); err != nil { + return err + } + + return nil +} From 6ab240b8d5e33012fa89f72fa7cd6dcc5348c8e6 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Tue, 18 Nov 2025 12:15:04 +0700 Subject: [PATCH 14/23] adjust html template --- assets/docs/general-consent.html | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/assets/docs/general-consent.html b/assets/docs/general-consent.html index e311cb54..4cedc378 100644 --- a/assets/docs/general-consent.html +++ b/assets/docs/general-consent.html @@ -125,7 +125,17 @@ >
c) Anggota keluarga saya : - .......................................... + {{ range $i, $name := .FamilyMembers }} + {{ if lt $i 2 }} +
  • {{ $name }}
  • + {{ end }} + {{ end }} + +{{ end }}
    Saya memahami bahwa pembukaan rahasia ini hanya sejauh yang diperlukan untuk tujuan perawatan, pembiayaan atau administrasi yang @@ -252,20 +262,16 @@ > - Pasien/Keluarga


    .......................................
    Nama - terang + Pasien/keluarga/
    penanggung jawab


    .......................................
    {{ .Responsible_Name }} - Pemberi Informasi


    .......................................
    Nama - terang + Pemberi Informasi


    .......................................
    {{ .InformationGivenBy_Name }} - Saksi I


    .......................................
    Nama - terang + Saksi I


    .......................................
    {{ .Witness1_Name }} - Saksi II


    .......................................
    Nama - terang + Saksi II


    .......................................
    {{ .Witness2_Name }} From 94314e641d40a21f8ee81da8fb1d1516387ca85d Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 18 Nov 2025 13:51:27 +0700 Subject: [PATCH 15/23] rename package --- .../use-case/simgos-sync-use-case/division/case.go | 10 +++++----- .../simgos-sync-use-case/installation/case.go | 10 +++++----- .../use-case/simgos-sync-use-case/specialist/case.go | 10 +++++----- .../simgos-sync-use-case/subspecialist/case.go | 11 ++++++----- internal/use-case/simgos-sync-use-case/unit/case.go | 10 +++++----- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/internal/use-case/simgos-sync-use-case/division/case.go b/internal/use-case/simgos-sync-use-case/division/case.go index 699848e9..62c04143 100644 --- a/internal/use-case/simgos-sync-use-case/division/case.go +++ b/internal/use-case/simgos-sync-use-case/division/case.go @@ -6,7 +6,7 @@ import ( d "github.com/karincake/dodol" "gorm.io/gorm" - dualtx "simrs-vx/pkg/dualtrx-helper" + db "simrs-vx/pkg/dualtrx-helper" e "simrs-vx/internal/domain/main-entities/division" esimgos "simrs-vx/internal/domain/simgos-entities/division" @@ -31,7 +31,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 1: Insert to simgos sgData, err = CreateSimgosData(input, &event, tx.Simgos) if err != nil { @@ -74,7 +74,7 @@ func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - tx := dualtx.NewTx() + tx := db.NewTx() err := tx.Sync.Transaction(func(tx *gorm.DB) error { // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { @@ -114,7 +114,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - tx := dualtx.NewTx() + tx := db.NewTx() err = tx.Simgos.Transaction(func(tx *gorm.DB) error { // Step 2: Update Simgos if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { @@ -158,7 +158,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return nil, err } - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 3: Delete Simgos err = HardDeleteSimgosData(sgData, &event, tx.Simgos) if err != nil { diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index f4b8d2b1..edbf709d 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -7,7 +7,7 @@ import ( "gorm.io/gorm" - dualtx "simrs-vx/pkg/dualtrx-helper" + db "simrs-vx/pkg/dualtrx-helper" e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" @@ -32,7 +32,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 1: Insert to simgos sgData, err = CreateSimgosData(input, &event, tx.Simgos) if err != nil { @@ -75,7 +75,7 @@ func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - tx := dualtx.NewTx() + tx := db.NewTx() err := tx.Sync.Transaction(func(tx *gorm.DB) error { // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { @@ -115,7 +115,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - tx := dualtx.NewTx() + tx := db.NewTx() err = tx.Simgos.Transaction(func(tx *gorm.DB) error { // Step 2: Update Simgos if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { @@ -159,7 +159,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return nil, err } - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 3: Delete Simgos err = SoftDeleteSimgosData(sgData, &event, tx.Simgos) if err != nil { diff --git a/internal/use-case/simgos-sync-use-case/specialist/case.go b/internal/use-case/simgos-sync-use-case/specialist/case.go index 58da2a2f..cf76771c 100644 --- a/internal/use-case/simgos-sync-use-case/specialist/case.go +++ b/internal/use-case/simgos-sync-use-case/specialist/case.go @@ -6,7 +6,7 @@ import ( d "github.com/karincake/dodol" "gorm.io/gorm" - dualtx "simrs-vx/pkg/dualtrx-helper" + db "simrs-vx/pkg/dualtrx-helper" e "simrs-vx/internal/domain/main-entities/specialist" esimgos "simrs-vx/internal/domain/simgos-entities/specialist" @@ -31,7 +31,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 1: Insert to simgos sgData, err = CreateSimgosData(input, &event, tx.Simgos) if err != nil { @@ -73,7 +73,7 @@ func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - tx := dualtx.NewTx() + tx := db.NewTx() err := tx.Sync.Transaction(func(tx *gorm.DB) error { // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { @@ -113,7 +113,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - tx := dualtx.NewTx() + tx := db.NewTx() err = tx.Simgos.Transaction(func(tx *gorm.DB) error { // Step 2: Update Simgos err = UpdateSimgosData(input, syncLink, &event, tx) @@ -158,7 +158,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return nil, err } - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 3: Delete Simgos err = HardDeleteSimgosData(sgData, &event, tx.Simgos) if err != nil { diff --git a/internal/use-case/simgos-sync-use-case/subspecialist/case.go b/internal/use-case/simgos-sync-use-case/subspecialist/case.go index 50b6c09b..2c633101 100644 --- a/internal/use-case/simgos-sync-use-case/subspecialist/case.go +++ b/internal/use-case/simgos-sync-use-case/subspecialist/case.go @@ -1,12 +1,13 @@ package subspecialist import ( - dualtx "simrs-vx/pkg/dualtrx-helper" pl "simrs-vx/pkg/logger" d "github.com/karincake/dodol" "gorm.io/gorm" + db "simrs-vx/pkg/dualtrx-helper" + e "simrs-vx/internal/domain/main-entities/subspecialist" //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" elog "simrs-vx/internal/domain/sync-entities/log" @@ -30,7 +31,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - //err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + //err = db.WithDualTx(func(tx *db.Dualtx) error { // // STEP 1: Insert to simgos // sgData, err = CreateSimgosData(input, &event, tx.Simgos) // if err != nil { @@ -73,7 +74,7 @@ func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - tx := dualtx.NewTx() + tx := db.NewTx() err := tx.Sync.Transaction(func(tx *gorm.DB) error { // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { @@ -115,7 +116,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // return nil, err //} // - //tx := dualtx.NewTx() + //tx := db.NewTx() //err = tx.Simgos.Transaction(func(tx *gorm.DB) error { // // Step 2: Update Simgos // if err = UpdateSimgosData(input, syncLink, &event, tx); err != nil { @@ -159,7 +160,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // return nil, err //} // - //err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + //err = db.WithDualTx(func(tx *db.Dualtx) error { // // STEP 3: Delete Simgos // err = HardDeleteSimgosData(sgData, &event, tx.Simgos) // if err != nil { diff --git a/internal/use-case/simgos-sync-use-case/unit/case.go b/internal/use-case/simgos-sync-use-case/unit/case.go index 42094d22..e17a8b22 100644 --- a/internal/use-case/simgos-sync-use-case/unit/case.go +++ b/internal/use-case/simgos-sync-use-case/unit/case.go @@ -6,7 +6,7 @@ import ( d "github.com/karincake/dodol" "gorm.io/gorm" - dualtx "simrs-vx/pkg/dualtrx-helper" + db "simrs-vx/pkg/dualtrx-helper" e "simrs-vx/internal/domain/main-entities/unit" esimgos "simrs-vx/internal/domain/simgos-entities/unit" @@ -31,7 +31,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 1: Insert to simgos sgData, err = CreateSimgosData(input, &event, tx.Simgos) if err != nil { @@ -74,7 +74,7 @@ func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - tx := dualtx.NewTx() + tx := db.NewTx() err := tx.Sync.Transaction(func(tx *gorm.DB) error { // Insert to Log if err := CreateLogData(input, &event, tx); err != nil { @@ -114,7 +114,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return nil, err } - tx := dualtx.NewTx() + tx := db.NewTx() err = tx.Simgos.Transaction(func(tx *gorm.DB) error { // Step 2: Update Simgos err = UpdateSimgosData(input, syncLink, &event, tx) @@ -159,7 +159,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return nil, err } - err = dualtx.WithDualTx(func(tx *dualtx.Dualtx) error { + err = db.WithDualTx(func(tx *db.Dualtx) error { // STEP 3: Delete M_Poly Simgos err = HardDeleteSimgosData(sgData, &event, tx.Simgos) if err != nil { From 0d340553b32f6f55fa059c95786bd9bad7636197 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Tue, 18 Nov 2025 14:44:19 +0700 Subject: [PATCH 16/23] feat (generate-file): general-consent pdf done --- assets/docs/general-consent.html | 82 +++++++--- .../main-entities/general-consent/dto.go | 2 +- .../main-use-case/general-consent/case.go | 13 ++ .../main-use-case/generate-file/case.go | 11 +- .../main-use-case/generate-file/helper.go | 17 +- .../main-use-case/generate-file/tycovar.go | 2 + pkg/conv-helper/conv-helper.go | 153 ++++++++++++++++++ pkg/use-case-helper/use-case-helper.go | 42 +++++ 8 files changed, 299 insertions(+), 23 deletions(-) create mode 100644 pkg/conv-helper/conv-helper.go diff --git a/assets/docs/general-consent.html b/assets/docs/general-consent.html index 4cedc378..a3f69826 100644 --- a/assets/docs/general-consent.html +++ b/assets/docs/general-consent.html @@ -125,11 +125,11 @@ >
    c) Anggota keluarga saya : - {{ if eq (len .FamilyMembers) 0 }} + {{ if eq (len .PatientRelatives_Name) 0 }} .......................................... {{ else }} -
      - {{ range $i, $name := .FamilyMembers }} +
        + {{ range $i, $name := .PatientRelatives_Name }} {{ if lt $i 2 }}
      • {{ $name }}
      • {{ end }} @@ -247,7 +247,7 @@
        - Malang, ............................................................ + Malang, {{ .Date }}
        @@ -260,20 +260,66 @@ border-collapse: collapse; " > - - - Pasien/keluarga/
        penanggung jawab


        .......................................
        {{ .Responsible_Name }} - - - Pemberi Informasi


        .......................................
        {{ .InformationGivenBy_Name }} - - - Saksi I


        .......................................
        {{ .Witness1_Name }} - - - Saksi II


        .......................................
        {{ .Witness2_Name }} - - + + +
        + Pasien/keluarga/
        penanggung jawab +
        + +
        + ....................................... +
        + +
        + {{ .Responsible_Name }} +
        + + + +
        + Pemberi Informasi +
        + +
        + ....................................... +
        + +
        + {{ .InformationGivenBy_Name }} +
        + + + +
        + Saksi I +
        + +
        + ....................................... +
        + +
        + {{ .Witness1_Name }} +
        + + + +
        + Saksi II +
        + +
        + ....................................... +
        + +
        + {{ .Witness2_Name }} +
        + + + + + diff --git a/internal/domain/main-entities/general-consent/dto.go b/internal/domain/main-entities/general-consent/dto.go index 3c659f2f..03dd7dc9 100644 --- a/internal/domain/main-entities/general-consent/dto.go +++ b/internal/domain/main-entities/general-consent/dto.go @@ -5,7 +5,7 @@ import ( ) type CreateDto struct { - Encounter_Id *uint `json:"-"` + Encounter_Id *uint `json:"encounter_id"` Value *string `json:"value"` } diff --git a/internal/use-case/main-use-case/general-consent/case.go b/internal/use-case/main-use-case/general-consent/case.go index a8c181ef..e6dd2a61 100644 --- a/internal/use-case/main-use-case/general-consent/case.go +++ b/internal/use-case/main-use-case/general-consent/case.go @@ -1,11 +1,14 @@ package generalconsent import ( + "errors" "strconv" // main entities e "simrs-vx/internal/domain/main-entities/general-consent" + ue "simrs-vx/internal/use-case/main-use-case/encounter" + pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -35,6 +38,11 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } + // check if encounter is done + if ue.IsDone(*input.Encounter_Id, &event, tx) { + return errors.New("encounter is already done") + } + if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -192,6 +200,11 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } + // check if encounter is done + if ue.IsDone(*input.Encounter_Id, &event, tx) { + return errors.New("encounter is already done") + } + if err := UpdateData(input, data, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go index c39a7edc..1c3ca51b 100644 --- a/internal/use-case/main-use-case/generate-file/case.go +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -8,6 +8,7 @@ import ( ugc "simrs-vx/internal/use-case/main-use-case/general-consent" pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -31,11 +32,17 @@ func Generate(input GenerateDto) (*d.Data, error) { // general-consent case ere.DTCGC: // get value from general consent by ref_id - gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *input.Ref_Id}, &event, nil) + gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *input.Ref_Id}, &event) if err != nil { return nil, err } + if gc.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil { + return nil, err + } + } + // map template data templateData := GeneralConsentPDF{} if gc.Value != nil { @@ -54,6 +61,8 @@ func Generate(input GenerateDto) (*d.Data, error) { input.FormatType = erc.DFTCPDF input.TemplateName = TDNGC + input.Encounter_Id = gc.Encounter_Id + templateData.Date = pu.FormatIndonesianDate(gc.CreatedAt) // generate file urlPub, err := generateFile(input, templateData) diff --git a/internal/use-case/main-use-case/generate-file/helper.go b/internal/use-case/main-use-case/generate-file/helper.go index ec377dc2..9bee2212 100644 --- a/internal/use-case/main-use-case/generate-file/helper.go +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -9,19 +9,21 @@ import ( erc "simrs-vx/internal/domain/references/common" docscfg "simrs-vx/internal/infra/docs-cfg" + pc "simrs-vx/pkg/conv-helper" pf "simrs-vx/pkg/file-helper" pm "simrs-vx/pkg/minio-helper" pp "simrs-vx/pkg/pdf-helper" + pu "simrs-vx/pkg/use-case-helper" ) // generate temporary file, upload to minio, generate public url, delete temporary file func generateFile(input GenerateDto, templateData any) (string, error) { - newPath, err := pf.PathToSaveFile(fmt.Sprintf("./public/%s/%d/%s", input.EntityType_Code, *input.Ref_Id, input.Type_Code)) + newPath, err := pf.PathToSaveFile(fmt.Sprintf("./temporary/%s", input.EntityType_Code)) if err != nil { return "", err } - fPath := fmt.Sprintf("%s/%s_%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType) + fPath := fmt.Sprintf("%s/%s-%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType) templatePath := docscfg.O.GetPath() + string(input.TemplateName) @@ -44,7 +46,7 @@ func generateFile(input GenerateDto, templateData any) (string, error) { bucketName := input.EntityType_Code - objectName := fmt.Sprintf("%v/%s-%d", *input.Ref_Id, input.Type_Code, time.Now().UnixNano()) + objectName := fmt.Sprintf("%s/%s-%d.%s", *pc.UintToString(input.Encounter_Id), input.Type_Code, time.Now().UnixNano(), input.FormatType) pdfUpload := pm.UploadPathInput{ BucketName: string(bucketName), Name: objectName, @@ -81,3 +83,12 @@ func generatePDF(input GeneratePDFdto) error { return nil } + +func removeFile(bucket, fileUrl string) error { + fPath := pu.GetLastTwoPathSegments(fileUrl) + err := pm.I.RemoveObject(bucket, fPath) + if err != nil { + return err + } + return nil +} diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index b5d09ceb..1e16cdb8 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -11,6 +11,7 @@ type GeneralConsentPDF struct { InformationGivenBy_Name string `json:"informationGivenBy_name"` Witness1_Name string `json:"witness1_name"` Witness2_Name string `json:"witness2_name"` + Date string `json:"date"` } type GenerateDto struct { @@ -19,6 +20,7 @@ type GenerateDto struct { Type_Code ere.DocTypeCode `json:"type_code" validate:"required"` FormatType erc.DocFormatTypeCode `json:"formatType"` TemplateName TemplateDocsName `json:"-"` + Encounter_Id *uint `json:"-"` } type GeneratePDFdto struct { diff --git a/pkg/conv-helper/conv-helper.go b/pkg/conv-helper/conv-helper.go new file mode 100644 index 00000000..03063c74 --- /dev/null +++ b/pkg/conv-helper/conv-helper.go @@ -0,0 +1,153 @@ +package convhelper + +import ( + "fmt" + "strconv" + "time" + + "gorm.io/gorm" +) + +// check string pointer, if nil return default value +func StrConvDefault(f *string, def string) string { + if f == nil { + return def + } + return *f +} + +// check string pointer, if nil return empty string +func StrConvEmpty(f *string) string { + return StrConvDefault(f, "-") +} + +func ByteConvStr(f *byte) string { + if f != nil { + return strconv.Itoa(int(*f)) + } + return "" +} + +func Float32Conv(f *float32) float32 { + if f == nil { + return 0 + } + return *f +} + +func StrRelConv(b bool, d string) string { + if b { + return d + } else { + return "" + } +} + +func UintToString(f interface{}) *string { + if f == nil { + return nil + } + + var t string + switch v := f.(type) { + case uint: + t = strconv.FormatUint(uint64(v), 10) + case uint8: + t = strconv.FormatUint(uint64(v), 10) + case uint16: + t = strconv.FormatUint(uint64(v), 10) + case uint32: + t = strconv.FormatUint(uint64(v), 10) + case uint64: + t = strconv.FormatUint(v, 10) + // Handle pointer types + case *uint: + if v != nil { + t = strconv.FormatUint(uint64(*v), 10) + } + case *uint8: + if v != nil { + t = strconv.FormatUint(uint64(*v), 10) + } + case *uint16: + if v != nil { + t = strconv.FormatUint(uint64(*v), 10) + } + case *uint32: + if v != nil { + t = strconv.FormatUint(uint64(*v), 10) + } + case *uint64: + if v != nil { + t = strconv.FormatUint(*v, 10) + } + default: + return nil // Unsupported type + } + + return &t +} + +func BoolToString(f *bool) *string { + if f == nil { + return nil + } + t := strconv.FormatBool(*f) + return &t +} + +func TimeToString(f *time.Time) *string { + if f == nil { + return nil + } + t := f.Format("2006-01-02 15:04:05") + return &t +} + +// Handling gorm.DeletedAt +func DeletedAtToString(deletedAt *gorm.DeletedAt) *string { + if deletedAt == nil || !deletedAt.Valid { + return nil + } + return TimeToString(&deletedAt.Time) +} + +func BoolToFloat64(b bool) float64 { + if b { + return 1.0 + } + return 0.0 +} + +func Float64ToBool(f float64) bool { + return f == 1.0 +} + +func StringToBool(s string) bool { + return s == "1" +} + +func StringToFloat32(s string) float32 { + f, _ := strconv.ParseFloat(s, 32) + return float32(f) +} + +func StringToFloat64(s string) float64 { + f, _ := strconv.ParseFloat(s, 64) + return f +} + +func Float64ToString(f float64) string { + return fmt.Sprintf("%.2f", f) +} + +func StringToUint64(s string) *uint64 { + if s == "" { + return nil + } + u, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil + } + return &u +} diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go index 07df05ba..019f9773 100644 --- a/pkg/use-case-helper/use-case-helper.go +++ b/pkg/use-case-helper/use-case-helper.go @@ -3,6 +3,7 @@ package usecasehelper import ( "errors" "fmt" + "net/url" "strings" "time" @@ -166,3 +167,44 @@ func index[S ~[]E, E comparable](s S, v E) int { } return -1 } + +func FormatIndonesianDate(t time.Time) string { + monthNames := [...]string{ + "", // dummy index 0 + "Januari", + "Februari", + "Maret", + "April", + "Mei", + "Juni", + "Juli", + "Agustus", + "September", + "Oktober", + "November", + "Desember", + } + + return fmt.Sprintf("%d %s %d", t.Day(), monthNames[int(t.Month())], t.Year()) +} + +func GetLastTwoPathSegments(s string) string { + u, err := url.Parse(s) + var path string + + if err == nil && u.Path != "" { + path = u.Path + } else { + path = s + } + + parts := strings.Split(strings.Trim(path, "/"), "/") + n := len(parts) + + if n >= 2 { + return parts[n-2] + "/" + parts[n-1] + } + + // fallback: return entire string if less than 2 segments + return strings.Trim(path, "/") +} From 8a95d681a06656013ebec8c5a6c758e865bfe8d3 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Tue, 18 Nov 2025 15:34:58 +0700 Subject: [PATCH 17/23] adjustment --- assets/docs/general-consent.html | 12 ++++++------ .../use-case/main-use-case/generate-file/tycovar.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/assets/docs/general-consent.html b/assets/docs/general-consent.html index a3f69826..de84a742 100644 --- a/assets/docs/general-consent.html +++ b/assets/docs/general-consent.html @@ -125,11 +125,11 @@ >
        c) Anggota keluarga saya : - {{ if eq (len .PatientRelatives_Name) 0 }} + {{ if eq (len .Relatives) 0 }} .......................................... {{ else }}
          - {{ range $i, $name := .PatientRelatives_Name }} + {{ range $i, $name := .Relatives }} {{ if lt $i 2 }}
        • {{ $name }}
        • {{ end }} @@ -271,7 +271,7 @@
          - {{ .Responsible_Name }} + {{ .Responsible }}
          @@ -285,7 +285,7 @@
          - {{ .InformationGivenBy_Name }} + {{ .Informant }}
          @@ -299,7 +299,7 @@
          - {{ .Witness1_Name }} + {{ .Witness1 }}
          @@ -313,7 +313,7 @@
          - {{ .Witness2_Name }} + {{ .Witness2 }}
          diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index 1e16cdb8..f9fdf689 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -6,12 +6,12 @@ import ( ) type GeneralConsentPDF struct { - PatientRelatives_Name []string `json:"patientRelatives_name"` - Responsible_Name string `json:"responsible_name"` - InformationGivenBy_Name string `json:"informationGivenBy_name"` - Witness1_Name string `json:"witness1_name"` - Witness2_Name string `json:"witness2_name"` - Date string `json:"date"` + Relatives []string `json:"relatives"` + Responsible string `json:"responsible"` + Informant string `json:"informant"` + Witness1 string `json:"witness1"` + Witness2 string `json:"witness2"` + Date string `json:"date"` } type GenerateDto struct { From 8644bcaedc8d4179e4c84f8d97c3474fc22b1fbd Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 19 Nov 2025 13:21:04 +0700 Subject: [PATCH 18/23] add patient --- .../domain/main-entities/person/entity.go | 2 +- .../{Encounter => encounter}/entity.go | 0 .../domain/simgos-entities/patient/entity.go | 6 +- .../simgos-sync-handler/patient/handler.go | 63 ++++ .../simgos-sync-handler.go | 2 + .../main-use-case/division/middleware.go | 8 +- .../main-use-case/installation/middleware.go | 8 +- .../use-case/main-use-case/patient/case.go | 151 ++++++--- .../use-case/main-use-case/patient/lib.go | 3 +- .../patient/middleware-runner.go | 81 ++++- .../main-use-case/patient/middleware.go | 19 +- .../use-case/main-use-case/patient/tycovar.go | 35 +- .../main-use-case/person-address/lib.go | 25 ++ .../main-use-case/person-contact/lib.go | 25 ++ .../main-use-case/person-insurance/lib.go | 25 ++ .../main-use-case/person-relative/lib.go | 25 ++ .../main-use-case/specialist/middleware.go | 8 +- internal/use-case/main-use-case/unit/case.go | 2 +- .../use-case/main-use-case/unit/middleware.go | 8 +- .../simgos-sync-plugin/patient/plugin.go | 167 +++++++++ .../simgos-sync-use-case/patient/case.go | 203 +++++++++++ .../simgos-sync-use-case/patient/helper.go | 316 ++++++++++++++++++ .../simgos-sync-use-case/patient/lib.go | 194 +++++++++++ .../patient/middleware-runner.go | 104 ++++++ .../patient/middleware.go | 9 + .../simgos-sync-use-case/patient/tycovar.go | 44 +++ 26 files changed, 1454 insertions(+), 79 deletions(-) rename internal/domain/simgos-entities/{Encounter => encounter}/entity.go (100%) create mode 100644 internal/interface/simgos-sync-handler/patient/handler.go create mode 100644 internal/use-case/simgos-sync-plugin/patient/plugin.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/case.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/patient/tycovar.go diff --git a/internal/domain/main-entities/person/entity.go b/internal/domain/main-entities/person/entity.go index d6e6744a..f36ec83a 100644 --- a/internal/domain/main-entities/person/entity.go +++ b/internal/domain/main-entities/person/entity.go @@ -34,7 +34,7 @@ type Person struct { Ocupation_Code *erp.OcupationCode `json:"occupation_code" gorm:"size:15"` Ocupation_Name *string `json:"occupation_name" gorm:"size:50"` MaritalStatus_Code *erp.MaritalStatusCode `json:"maritalStatus_code" gorm:"size:10"` - Nationality *string `json:"nationality": gorm:"size:50"` + Nationality *string `json:"nationality" gorm:"size:50"` Ethnic_Code *string `json:"ethnic_code" gorm:"size:20"` Ethnic *ee.Ethnic `json:"ethnic,omitempty" gorm:"foreignKey:Ethnic_Code;references:Code"` Language_Code *string `json:"language_code" gorm:"size:10"` diff --git a/internal/domain/simgos-entities/Encounter/entity.go b/internal/domain/simgos-entities/encounter/entity.go similarity index 100% rename from internal/domain/simgos-entities/Encounter/entity.go rename to internal/domain/simgos-entities/encounter/entity.go diff --git a/internal/domain/simgos-entities/patient/entity.go b/internal/domain/simgos-entities/patient/entity.go index 3013e121..20b7fee6 100644 --- a/internal/domain/simgos-entities/patient/entity.go +++ b/internal/domain/simgos-entities/patient/entity.go @@ -4,14 +4,14 @@ import "time" type MPasien struct { Id uint `json:"id" gorm:"primaryKey;autoIncrement;column:id"` - Nomr string `json:"nomr" gorm:"column:nomr"` + Nomr string `json:"nomr" gorm:"uniqueIndex;column:nomr"` Title string `json:"title" gorm:"column:title"` Nama string `json:"nama" gorm:"column:nama"` Tempat string `json:"tempat" gorm:"column:tempat"` Tgllahir *time.Time `json:"tgllahir" gorm:"column:tgllahir"` Jeniskelamin string `json:"jeniskelamin" gorm:"column:jeniskelamin"` Alamat string `json:"alamat" gorm:"column:alamat"` - Kelurahan uint `json:"kelurahan" gorm:"column:kelurahan"` + Kelurahan uint64 `json:"kelurahan" gorm:"column:kelurahan"` Kdkecamatan uint `json:"kdkecamatan" gorm:"column:kdkecamatan"` Kota uint `json:"kota" gorm:"column:kota"` Kdprovinsi uint `json:"kdprovinsi" gorm:"column:kdprovinsi"` @@ -41,7 +41,7 @@ type MPasien struct { Sim *string `json:"sim" gorm:"column:sim"` Paspor *string `json:"paspor" gorm:"column:paspor"` Disabilitas *string `json:"disabilitas" gorm:"column:disabilitas"` - Bahasa string `json:"bahasa" json:"bahasa"` + Bahasa string `json:"bahasa" gorm:"column:bahasa"` HambatanKomunikasi string `json:"hambatan_komunikasi" gorm:"column:hambatan_komunikasi"` Kebangsaan string `json:"kebangsaan" gorm:"column:kebangsaan"` Notelprumah1 string `json:"notelprumah1" gorm:"column:notelprumah1"` diff --git a/internal/interface/simgos-sync-handler/patient/handler.go b/internal/interface/simgos-sync-handler/patient/handler.go new file mode 100644 index 00000000..ae2e1db8 --- /dev/null +++ b/internal/interface/simgos-sync-handler/patient/handler.go @@ -0,0 +1,63 @@ +package patient + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/patient" + esync "simrs-vx/internal/domain/sync-entities/log" + u "simrs-vx/internal/use-case/simgos-sync-use-case/patient" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.Patient{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.CreateSimxLog(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.Patient{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint(id) + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint(id) + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index 6b63e1d6..dee6fb3f 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -19,6 +19,7 @@ import ( "simrs-vx/internal/interface/main-handler/home" division "simrs-vx/internal/interface/simgos-sync-handler/division" installation "simrs-vx/internal/interface/simgos-sync-handler/installation" + patient "simrs-vx/internal/interface/simgos-sync-handler/patient" specialist "simrs-vx/internal/interface/simgos-sync-handler/specialist" subspecialist "simrs-vx/internal/interface/simgos-sync-handler/subspecialist" unit "simrs-vx/internal/interface/simgos-sync-handler/unit" @@ -43,6 +44,7 @@ func SetRoutes() http.Handler { hc.SyncCrud(r, prefix+"/v1/division", division.O) hc.SyncCrud(r, prefix+"/v1/specialist", specialist.O) hc.SyncCrud(r, prefix+"/v1/subspecialist", subspecialist.O) + hc.SyncCrud(r, prefix+"/v1/patient", patient.O) return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/use-case/main-use-case/division/middleware.go b/internal/use-case/main-use-case/division/middleware.go index 317b137a..83452940 100644 --- a/internal/use-case/main-use-case/division/middleware.go +++ b/internal/use-case/main-use-case/division/middleware.go @@ -7,14 +7,14 @@ import ( // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "create-unit", Func: plugin.Create}) + createMw{Name: "sync-create-division", Func: plugin.Create}) createSimxLogMw = append(createSimxLogMw, - createLogMw{Name: "create-log", Func: plugin.CreateLog}) + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) updatePreMw = append(updatePreMw, - updateMw{Name: "update-unit", Func: plugin.Update}) + updateMw{Name: "sync-update-division", Func: plugin.Update}) deletePreMw = append(deletePreMw, - deleteMw{Name: "delete-unit", Func: plugin.Delete}) + deleteMw{Name: "sync-delete-division", Func: plugin.Delete}) } diff --git a/internal/use-case/main-use-case/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index b8dfa4c8..f32b7381 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -7,14 +7,14 @@ import ( // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "create-installation", Func: plugin.Create}) + createMw{Name: "sync-create-installation", Func: plugin.Create}) createSimxLogMw = append(createSimxLogMw, - createLogMw{Name: "create-log", Func: plugin.CreateLog}) + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) updatePreMw = append(updatePreMw, - updateMw{Name: "update-installation", Func: plugin.Update}) + updateMw{Name: "sync-update-installation", Func: plugin.Update}) deletePreMw = append(deletePreMw, - deleteMw{Name: "delete-installation", Func: plugin.Delete}) + deleteMw{Name: "sync-delete-installation", Func: plugin.Delete}) } diff --git a/internal/use-case/main-use-case/patient/case.go b/internal/use-case/main-use-case/patient/case.go index a0766d12..96a5f06d 100644 --- a/internal/use-case/main-use-case/patient/case.go +++ b/internal/use-case/main-use-case/patient/case.go @@ -3,22 +3,25 @@ package patient import ( "strconv" - evm "simrs-vx/internal/domain/bpjs-entities/vclaim-member" - e "simrs-vx/internal/domain/main-entities/patient" - - uvm "simrs-vx/internal/use-case/bpjs-use-case/vclaim-member" - upe "simrs-vx/internal/use-case/main-use-case/person" - upa "simrs-vx/internal/use-case/main-use-case/person-address" - upc "simrs-vx/internal/use-case/main-use-case/person-contact" - upi "simrs-vx/internal/use-case/main-use-case/person-insurance" - upr "simrs-vx/internal/use-case/main-use-case/person-relative" - pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" + + erc "simrs-vx/internal/domain/references/common" + + evm "simrs-vx/internal/domain/bpjs-entities/vclaim-member" + e "simrs-vx/internal/domain/main-entities/patient" + esync "simrs-vx/internal/domain/sync-entities/log" + + uvm "simrs-vx/internal/use-case/bpjs-use-case/vclaim-member" + upe "simrs-vx/internal/use-case/main-use-case/person" + upa "simrs-vx/internal/use-case/main-use-case/person-address" + upc "simrs-vx/internal/use-case/main-use-case/person-contact" + upi "simrs-vx/internal/use-case/main-use-case/person-insurance" + upr "simrs-vx/internal/use-case/main-use-case/person-relative" ) const source = "patient" @@ -33,9 +36,9 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { @@ -88,21 +91,26 @@ func Create(input e.CreateDto) (*d.Data, error) { data = *resData } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + dataPatient, err := ReadDetailData(e.ReadDetailDto{Id: uint16(data.Id)}, &event, tx) + if err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateSyncMiddleware(createSimxSyncMw, dataPatient); err != nil { + return err + } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -128,7 +136,7 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { @@ -179,7 +187,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { @@ -225,6 +233,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -232,13 +241,6 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - if person_id, err := upe.CreateOrUpdatePerson(input.Person, &event, tx); err != nil { return err } else { @@ -277,21 +279,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - pl.SetLogInfo(&event, nil, "complete") + dataPatient, err := ReadDetailData(e.ReadDetailDto{Id: uint16(data.Id)}, &event) + if err != nil { + return err + } - mwRunner.setMwType(pu.MWTPost) + mwRunner.setMwType(pu.MWTPre) // Run post-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + if err := mwRunner.RunUpdateMiddleware(updatePreMw, dataPatient); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -315,6 +322,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -322,30 +330,68 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } + input.Id = data.Id + // Delete Patient if err := DeleteData(data, &event, tx); err != nil { return err } - mwRunner.setMwType(pu.MWTPost) + // Delete PersonInsurance + if insurance := data.Person.Insurances; insurance != nil && len(*insurance) > 0 { + if err = upi.DeleteMultipleData(data.Person_Id, &event, tx); err != nil { + return err + } + } + + // Delete PersonRelative + if relative := data.Person.Relatives; relative != nil && len(*relative) > 0 { + if err = upr.DeleteMultipleData(data.Person_Id, &event, tx); err != nil { + return err + } + } + + // Delete PersonContacts + if contact := data.Person.Contacts; contact != nil && len(*contact) > 0 { + if err = upc.DeleteMultipleData(data.Person_Id, &event, tx); err != nil { + return err + } + } + + // Delete PersonAddress + if address := data.Person.Addresses; address != nil && len(*address) > 0 { + if err = upa.DeleteMultipleData(data.Person_Id, &event, tx); err != nil { + return err + } + } + + // Delete VclaimMember + if vclaim := data.Person.VclaimMember; vclaim != nil { + if err = uvm.DeleteData(vclaim, &event, tx); err != nil { + return err + } + } + + // Delete Person + if err = upe.DeleteData(data.Person, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPre) // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -421,3 +467,32 @@ func Search(input e.SearchDto) (*d.Data, error) { Data: e.ToResponseList(dataList), }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/patient/lib.go b/internal/use-case/main-use-case/patient/lib.go index 36c9d416..e615c854 100644 --- a/internal/use-case/main-use-case/patient/lib.go +++ b/internal/use-case/main-use-case/patient/lib.go @@ -92,7 +92,8 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e Preload("Person.Relatives.Village.District.Regency.Province"). Preload("Person.Addresses.Village.District.Regency.Province"). Preload("Person.Addresses.PostalRegion.Village.District.Regency.Province"). - Preload("Person.Insurances.InsuranceCompany") + Preload("Person.Insurances.InsuranceCompany"). + Preload("Person.VclaimMember") if err := tx.First(&data, input.Id).Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { diff --git a/internal/use-case/main-use-case/patient/middleware-runner.go b/internal/use-case/main-use-case/patient/middleware-runner.go index 2e9ea66a..f9e05bb7 100644 --- a/internal/use-case/main-use-case/patient/middleware-runner.go +++ b/internal/use-case/main-use-case/patient/middleware-runner.go @@ -1,24 +1,29 @@ package patient import ( - e "simrs-vx/internal/domain/main-entities/patient" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/patient" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { Event *pl.Event Tx *gorm.DB MwType pu.MWType + SyncOn bool } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, - Tx: tx, + Event: event, + SyncOn: sync.O.Enable, } } @@ -38,7 +43,51 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e return nil } +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateSyncMiddleware(middlewares []createSyncMw, input *e.Patient) error { + if !me.SyncOn { + return nil + } + + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + if !me.SyncOn { + return nil + } + + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Patient) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -54,6 +103,10 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu } func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -68,13 +121,17 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []updateMw, input *e.Patient) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -83,13 +140,17 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e.DeleteDto) error { + if !me.SyncOn { + return nil + } + for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/patient/middleware.go b/internal/use-case/main-use-case/patient/middleware.go index 6b0175dc..cc97473f 100644 --- a/internal/use-case/main-use-case/patient/middleware.go +++ b/internal/use-case/main-use-case/patient/middleware.go @@ -6,16 +6,29 @@ import ( "io" "net/http" + "gorm.io/gorm" + e "simrs-vx/internal/domain/main-entities/patient" - "gorm.io/gorm" + plugin "simrs-vx/internal/use-case/simgos-sync-plugin/patient" ) // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "check-vclaim-member", Func: checkVclaimMember}, - ) + createMw{Name: "check-vclaim-member", Func: checkVclaimMember}) + + createSimxSyncMw = append(createSimxSyncMw, + createSyncMw{Name: "sync-create-patient", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "sync-update-patient", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "sync-delete-patient", Func: plugin.Delete}) } func checkVclaimMember(input *e.CreateDto, data *e.Patient, tx *gorm.DB) error { diff --git a/internal/use-case/main-use-case/patient/tycovar.go b/internal/use-case/main-use-case/patient/tycovar.go index 29e5c368..cedb1514 100644 --- a/internal/use-case/main-use-case/patient/tycovar.go +++ b/internal/use-case/main-use-case/patient/tycovar.go @@ -12,6 +12,7 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/patient" + elog "simrs-vx/internal/domain/sync-entities/log" ) type createMw struct { @@ -19,6 +20,26 @@ type createMw struct { Func func(input *e.CreateDto, data *e.Patient, tx *gorm.DB) error } +type createSyncMw struct { + Name string + Func func(input *e.Patient) error +} + +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + +type updateMw struct { + Name string + Func func(input *e.Patient) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error +} + type readListMw struct { Name string Func func(input *e.ReadListDto, data *e.Patient, tx *gorm.DB) error @@ -29,18 +50,20 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Patient, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxSyncMw []createSyncMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw type BPJSResponse struct { diff --git a/internal/use-case/main-use-case/person-address/lib.go b/internal/use-case/main-use-case/person-address/lib.go index d0a581bd..94cbfa69 100644 --- a/internal/use-case/main-use-case/person-address/lib.go +++ b/internal/use-case/main-use-case/person-address/lib.go @@ -191,3 +191,28 @@ func CreateOrUpdateBatch(input []e.UpdateDto, event *pl.Event, tx ...*gorm.DB) e return nil } + +func DeleteMultipleData(personId *uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, personId, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Where("\"Person_Id\" = ?", *personId). + Delete(&e.PersonAddress{}).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, personId) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/person-contact/lib.go b/internal/use-case/main-use-case/person-contact/lib.go index 034800a4..2c0028bf 100644 --- a/internal/use-case/main-use-case/person-contact/lib.go +++ b/internal/use-case/main-use-case/person-contact/lib.go @@ -191,3 +191,28 @@ func CreateOrUpdateBatch(input []e.UpdateDto, event *pl.Event, tx ...*gorm.DB) e return nil } + +func DeleteMultipleData(personId *uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, personId, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Where("\"Person_Id\" = ?", *personId). + Delete(&e.PersonContact{}).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, personId) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/person-insurance/lib.go b/internal/use-case/main-use-case/person-insurance/lib.go index 86c6dc83..835694ff 100644 --- a/internal/use-case/main-use-case/person-insurance/lib.go +++ b/internal/use-case/main-use-case/person-insurance/lib.go @@ -191,3 +191,28 @@ func CreateOrUpdateBatch(input []e.UpdateDto, event *pl.Event, tx ...*gorm.DB) e return nil } + +func DeleteMultipleData(personId *uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, personId, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Where("\"Person_Id\" = ?", *personId). + Delete(&e.PersonInsurance{}).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, personId) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/person-relative/lib.go b/internal/use-case/main-use-case/person-relative/lib.go index 28e12e2b..4531f736 100644 --- a/internal/use-case/main-use-case/person-relative/lib.go +++ b/internal/use-case/main-use-case/person-relative/lib.go @@ -191,3 +191,28 @@ func CreateOrUpdateBatch(input []e.UpdateDto, event *pl.Event, tx ...*gorm.DB) e return nil } + +func DeleteMultipleData(personId *uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, personId, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Where("\"Person_Id\" = ?", *personId). + Delete(&e.PersonRelative{}).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, personId) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/specialist/middleware.go b/internal/use-case/main-use-case/specialist/middleware.go index 5d119b01..969436e5 100644 --- a/internal/use-case/main-use-case/specialist/middleware.go +++ b/internal/use-case/main-use-case/specialist/middleware.go @@ -7,14 +7,14 @@ import ( // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "create-unit", Func: plugin.Create}) + createMw{Name: "sync-create-specialist", Func: plugin.Create}) createSimxLogMw = append(createSimxLogMw, - createLogMw{Name: "create-log", Func: plugin.CreateLog}) + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) updatePreMw = append(updatePreMw, - updateMw{Name: "update-unit", Func: plugin.Update}) + updateMw{Name: "sync-update-specialiast", Func: plugin.Update}) deletePreMw = append(deletePreMw, - deleteMw{Name: "delete-unit", Func: plugin.Delete}) + deleteMw{Name: "sync-delete-specialist", Func: plugin.Delete}) } diff --git a/internal/use-case/main-use-case/unit/case.go b/internal/use-case/main-use-case/unit/case.go index 75b1c4ab..1db6fb79 100644 --- a/internal/use-case/main-use-case/unit/case.go +++ b/internal/use-case/main-use-case/unit/case.go @@ -210,7 +210,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner.setMwType(pu.MWTPost) + mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err diff --git a/internal/use-case/main-use-case/unit/middleware.go b/internal/use-case/main-use-case/unit/middleware.go index e3144448..0e62f502 100644 --- a/internal/use-case/main-use-case/unit/middleware.go +++ b/internal/use-case/main-use-case/unit/middleware.go @@ -7,14 +7,14 @@ import ( // example of middleware func init() { createPreMw = append(createPreMw, - createMw{Name: "create-unit", Func: plugin.Create}) + createMw{Name: "sync-create-unit", Func: plugin.Create}) createSimxLogMw = append(createSimxLogMw, - createLogMw{Name: "create-log", Func: plugin.CreateLog}) + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) updatePreMw = append(updatePreMw, - updateMw{Name: "update-unit", Func: plugin.Update}) + updateMw{Name: "sync-update-unit", Func: plugin.Update}) deletePreMw = append(deletePreMw, - deleteMw{Name: "delete-unit", Func: plugin.Delete}) + deleteMw{Name: "sync-delete-unit", Func: plugin.Delete}) } diff --git a/internal/use-case/simgos-sync-plugin/patient/plugin.go b/internal/use-case/simgos-sync-plugin/patient/plugin.go new file mode 100644 index 00000000..b10fdce5 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/patient/plugin.go @@ -0,0 +1,167 @@ +package patient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + d "github.com/karincake/dodol" + + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/patient" + elog "simrs-vx/internal/domain/sync-entities/log" +) + +func Create(input *e.Patient) error { + endpoint := getPrefixEndpoint() + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Update(input *e.Patient) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + 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() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return fmt.Errorf(errors.Message) + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/patient", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-use-case/patient/case.go b/internal/use-case/simgos-sync-use-case/patient/case.go new file mode 100644 index 00000000..e6d13c89 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/case.go @@ -0,0 +1,203 @@ +package patient + +import ( + pl "simrs-vx/pkg/logger" + + d "github.com/karincake/dodol" + "gorm.io/gorm" + + db "simrs-vx/pkg/dualtrx-helper" + + e "simrs-vx/internal/domain/main-entities/patient" + esimgos "simrs-vx/internal/domain/simgos-entities/patient" + elog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/patient" +) + +const source = "patient" + +func Create(input e.Patient) (*d.Data, error) { + var ( + sgData *esimgos.MPasien + syncLink *esync.PatientLink + err error + ) + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err = db.WithDualTx(func(tx *db.Dualtx) error { + // STEP 1: Insert to simgos + sgData, err = CreateSimgosData(input, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 2: Insert to Link + syncLink, err = CreateLinkData(input.Id, sgData.Id, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + tx := db.NewTx() + err := tx.Sync.Transaction(func(tx *gorm.DB) error { + // Insert to Log + if err := CreateLogData(input, &event, tx); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + }, nil +} + +func Update(input e.Patient) (*d.Data, error) { + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + // STEP 1: Get Link + syncLink, err := ReadDetailLinkData(uint16(input.Id), &event) + if err != nil { + return nil, err + } + + // STEP 2: Get Simgos + patientData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + tx := db.NewTx() + err = tx.Simgos.Transaction(func(tx *gorm.DB) error { + // Step 3: Update Simgos + if err = UpdateSimgosData(input, syncLink.Simgos_Id, patientData.Nomr, &event, tx); err != nil { + return err + } + + return nil + }) + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + }, nil +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + var isLinkDeleted bool + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + // STEP 1: Get Link + syncLink, err := ReadDetailLinkData(uint16(input.Id), &event) + if err != nil { + return nil, err + } + + // STEP 2: Get Simgos + sgData, err := ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + err = db.WithDualTx(func(tx *db.Dualtx) error { + // STEP 3: Delete Simgos + err = HardDeleteSimgosData(sgData, &event, tx.Simgos) + if err != nil { + return err + } + + // STEP 4: Delete Link + err = DeleteLinkData(syncLink, &event, tx.Sync) + if err != nil { + return err + } + + isLinkDeleted = true + return nil + }) + + if err != nil { + if isLinkDeleted { + go func() { + _, _ = CreateLinkData(input.Id, sgData.Id, &event) + }() + } + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/patient/helper.go b/internal/use-case/simgos-sync-use-case/patient/helper.go new file mode 100644 index 00000000..4b7ddfe6 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/helper.go @@ -0,0 +1,316 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package patient + +import ( + "encoding/json" + "fmt" + erc "simrs-vx/internal/domain/references/common" + erp "simrs-vx/internal/domain/references/person" + "strconv" + + e "simrs-vx/internal/domain/main-entities/patient" + ep "simrs-vx/internal/domain/main-entities/person" + epa "simrs-vx/internal/domain/main-entities/person-address" + epc "simrs-vx/internal/domain/main-entities/person-contact" + epr "simrs-vx/internal/domain/main-entities/person-relative" + esimgos "simrs-vx/internal/domain/simgos-entities/patient" + esyncLog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/patient" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm/clause" +) + +func setDataSimgos(input *e.Patient) (data esimgos.MPasien) { + if p := input.Person; p != nil { + mapPerson(p, &data) + } + + if input.Person != nil && input.Person.Addresses != nil { + mapAddress(input.Person.Addresses, &data) + } + + if input.Person != nil && input.Person.Contacts != nil { + mapContact(input.Person.Contacts, &data) + } + + if input.Person != nil && input.Person.Relatives != nil { + mapRelative(input.Person.Relatives, &data) + } + + data.Nip = input.RegisteredBy_User_Name + data.Tgldaftar = input.RegisteredAt + + return +} + +func generateNomrPatient() (string, error) { + const prefix = "12" // fixed starting prefix (same as $awal_rm) in simgos + const maxSuffix = 999999 + + type Row struct { + Nomr string `gorm:"column:nomr"` + } + + var r Row + + // Lock rows for this prefix → prevents race condition + if err := dg.IS["simrs"]. + Table("m_pasien"). + Select("nomr"). + Where("nomr LIKE ?", prefix+"%"). + Order("nomr DESC"). + Clauses(clause.Locking{Strength: "UPDATE"}). + Limit(1). + Scan(&r).Error; err != nil { + return "", err + } + + // No existing NOMR → start fresh + if r.Nomr == "" { + return prefix + "000001", nil + } + + suffix := r.Nomr[len(prefix):] // extract numeric part + num, _ := strconv.Atoi(suffix) + + // 3. If suffix reaches 999999 → increment the prefix + if num == maxSuffix { + p, _ := strconv.Atoi(prefix) + p++ + return fmt.Sprintf("%d000001", p), nil + } + + // 4. Normal increment + return prefix + fmt.Sprintf("%06d", num+1), nil +} + +func mapPerson(p *ep.Person, data *esimgos.MPasien) { + if p.FrontTitle != nil { + data.Title = *p.FrontTitle + } + + if p.BirthRegency != nil { + data.Tempat = p.BirthRegency.Name + } + + if p.BirthDate != nil { + data.Tgllahir = p.BirthDate + } + + if p.Gender_Code != nil { + if *p.Gender_Code == erp.GCMale { + data.Jeniskelamin = "L" + } else if *p.Gender_Code == erp.GCFemale { + data.Jeniskelamin = "P" + } + } + + data.Nama = p.Name + data.Noktp = *p.ResidentIdentityNumber + data.NoktpBaru = *p.ResidentIdentityNumber + data.Pekerjaan = *p.Ocupation_Name + + data.Status, data.TxtStatus = setMaritalStatus(p.MaritalStatus_Code) + data.Agama, data.TxtAgama = setReligion(p.Religion_Code) + data.Pendidikan, data.TxtPendidikan = setEducation(p.Education_Code) + + if c := p.VclaimMember; c != nil && c.CardNumber != nil { + data.NoKartu = *c.CardNumber + } + + if l := p.Language; l != nil { + data.Bahasa = l.Name + } + + if p.CommunicationIssueStatus { + data.HambatanKomunikasi = "Y" + } else { + data.HambatanKomunikasi = "T" + } + + if p.Nationality != nil { + data.Kebangsaan = *p.Nationality + } + + if p.Ethnic != nil { + data.Suku = p.Ethnic.Name + } +} + +func setMaritalStatus(code *erp.MaritalStatusCode) (uint, string) { + if code != nil { + switch *code { + case erp.MaritalStatusSingle: + return 1, "Belum Menikah" + case erp.MaritalStatusMarried: + return 2, "Menikah" + case erp.MaritalStatusDivorced: + return 4, "Cerai" + case erp.MaritalStatusWidowed: + return 3, "Janda/Duda" + default: + return 0, "Tidak Diketahui" + } + } else { + return 0, "Tidak Diketahui" + } +} + +func setReligion(code *erp.ReligionCode) (uint, string) { + if code != nil { + switch *code { + case erp.RCIslam: + return 1, "Islam" + case erp.RCProtestan: + return 2, "Kristen Protestan" + case erp.RCKatolik: + return 3, "Katholik" + case erp.RCHindu: + return 4, "Hindu" + case erp.RCBudha: + return 5, "Budha" + case erp.RCKonghucu: + return 6, "Konghucu" + default: + return 9, "Lainnya" + } + } else { + return 0, "Tidak Diketahui" + } +} + +func setEducation(code *erp.EducationCode) (uint, string) { + if code != nil { + switch *code { + case erp.ECTS: + return 0, "Tidak Sekolah" + case erp.ECSD: + return 1, string(erp.ECSD) + case erp.ECSLTP: + return 2, string(erp.ECSLTP) + case erp.ECSLTA: + return 3, string(erp.ECSLTA) + case erp.ECD1, erp.ECD2, erp.ECD3, erp.ECD4: + return 4, "D3/Akademik" + case erp.ECS1, erp.ECS2, erp.ECS3: + return 5, "Universitas" + case erp.ECOther: + return 6, "Lainnya" + default: + return 7, "Tidak Diketahui" + } + } else { + return 7, "Tidak Diketahui" + } +} + +func mapAddress(addresses *[]epa.PersonAddress, data *esimgos.MPasien) { + if addresses == nil || len(*addresses) == 0 { + return + } + + a := (*addresses)[0] + data.Alamat = a.Address + data.AlamatKtp = a.Address + + if v := a.Village; v != nil { + villageCode, _ := strconv.Atoi(v.Code) + data.Kelurahan = uint64(villageCode) + data.TxtKelurahan = v.Name + + if d := v.District; d != nil { + districtCode, _ := strconv.Atoi(d.Code) + data.Kdkecamatan = uint(districtCode) + data.TxtKecamatan = d.Name + + if r := d.Regency; r != nil { + regencyCode, _ := strconv.Atoi(r.Code) + data.Kota = uint(regencyCode) + data.TxtKota = r.Name + + if p := r.Province; p != nil { + provinceCode, _ := strconv.Atoi(p.Code) + data.Kdprovinsi = uint(provinceCode) + data.TxtProvinsi = p.Name + } + } + } + } +} + +func mapContact(contact *[]epc.PersonContact, data *esimgos.MPasien) { + if contact == nil || len(*contact) == 0 { + return + } + + for _, c := range *contact { + if c.Type_Code == erp.CTPhone || c.Type_Code == erp.CTMPhone { + data.Notelp = c.Value + break + } + } + +} + +func mapRelative(relative *[]epr.PersonRelative, data *esimgos.MPasien) { + if relative == nil || len(*relative) == 0 { + return + } + + r := (*relative)[0] + data.PenanggungjawabNama = *r.Name + + switch r.Relationship_Code { + case erp.RCMother, erp.RCFather: + data.PenanggungjawabHubungan = "ORANG TUA" + switch { + case r.Relationship_Code == erp.RCMother: + data.NamaIbu = *r.Name + eduCode, _ := setEducation(r.Education_Code) + data.PendidikanIbu = strconv.Itoa(int(eduCode)) + case r.Relationship_Code == erp.RCFather: + data.NamaAyah = *r.Name + eduCode, _ := setEducation(r.Education_Code) + data.PendidikanAyah = strconv.Itoa(int(eduCode)) + } + case erp.RCChild: + data.PenanggungjawabHubungan = "ANAK" + default: + data.PenanggungjawabHubungan = "LAINNYA" + } + + data.PenanggungjawabAlamat = *r.Address + data.PenanggungjawabPhone = *r.PhoneNumber +} + +func setDataSimxLog(input *esyncLog.SimxLogDto) (data esync.PatientSimxLog) { + // encode to JSON + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") + jsonString := string(jsonData) + + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + + data.Value = &jsonString + data.Date = &now + data.Status = status + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.PatientLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return +} diff --git a/internal/use-case/simgos-sync-use-case/patient/lib.go b/internal/use-case/simgos-sync-use-case/patient/lib.go new file mode 100644 index 00000000..44d4c209 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/lib.go @@ -0,0 +1,194 @@ +package patient + +import ( + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/patient" + esimgos "simrs-vx/internal/domain/simgos-entities/patient" + esynclog "simrs-vx/internal/domain/sync-entities/log" + esync "simrs-vx/internal/domain/sync-entities/patient" +) + +var now = time.Now() + +func CreateSimgosData(input e.Patient, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MPasien, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := setDataSimgos(&input) + nomr, err := generateNomrPatient() + if err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + data.Nomr = nomr + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MPasien, error) { + pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") + data := esimgos.MPasien{} + + var tx = dg.IS["simrs"] + + if err := tx. + Where("\"id\" = ?", simgosId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simgosId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateSimgosData(input e.Patient, patientId uint, patientNomr string, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.Id = patientId + data.Nomr = patientNomr + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func HardDeleteSimgosData(data *esimgos.MPasien, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.IS["simrs"] + } + + if err := tx. + Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) (*esync.PatientLink, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.PatientLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.PatientLink{} + + var tx = dg.I + + if err := tx. + Where("\"Simx_Id\" = ?", simxId). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, simxId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func DeleteLinkData(data *esync.PatientLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/patient/middleware-runner.go b/internal/use-case/simgos-sync-use-case/patient/middleware-runner.go new file mode 100644 index 00000000..648be8d7 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/middleware-runner.go @@ -0,0 +1,104 @@ +package patient + +import ( + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/patient" +) + +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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Patient) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Patient) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) 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/simgos-sync-use-case/patient/middleware.go b/internal/use-case/simgos-sync-use-case/patient/middleware.go new file mode 100644 index 00000000..5fe75e0a --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/middleware.go @@ -0,0 +1,9 @@ +package patient + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/patient/tycovar.go b/internal/use-case/simgos-sync-use-case/patient/tycovar.go new file mode 100644 index 00000000..d7e9abfe --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/tycovar.go @@ -0,0 +1,44 @@ +/* +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 patient + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/patient" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Patient, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Patient, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Patient, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From f207b2716f762f3ed260ed4ef2b4064bb45567c0 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 19 Nov 2025 14:10:28 +0700 Subject: [PATCH 19/23] adjust control letter template html --- assets/docs/control-letter.html | 132 ++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 assets/docs/control-letter.html diff --git a/assets/docs/control-letter.html b/assets/docs/control-letter.html new file mode 100644 index 00000000..10425b28 --- /dev/null +++ b/assets/docs/control-letter.html @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + +
          + logo + +
          + SURAT RENCANA KONTROL +
          +
          + RSUD dr. Saiful Anwar +
          +
          +
          + No. :replace +
          +
          +
          + Kepada Yth +
          +
          +
          + DANI SETIAWAN +
          +
          + Sp./Sub. Rehabilitasi Medik +
          +
          + +
          + Mohon Pemeriksaan dan Penanganan Lebih Lanjut: +
          + + + + + + + + + + + + + + + + + + + + + + + +
          + No.Kartu + + :Replace +
          + Nama Peserta + + :Replace +
          + Tgl.Lahir + + :Replace +
          + Diagnosa + + :Replace +
          + Rencana Kontrol + + :Replace +
          + +
          + Demikian atas bantuannya, diucapkan banyak terima kasih. +
          + + + + + + +
          + Mengetahui, DPJP :Replace +
          + +
          + Tgl. Entry: 2025-06-07 | Tgl. Cetak: 07-06-2025 16:52 PM +
          + +
          + + + \ No newline at end of file From f8674b4eca6cd88761eee4ce6fb46d92cec8ba7c Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 19 Nov 2025 16:10:21 +0700 Subject: [PATCH 20/23] adjust patient --- internal/domain/main-entities/patient/dto.go | 1 + .../use-case/main-use-case/patient/case.go | 7 ++ .../use-case/main-use-case/patient/helper.go | 77 ++++++++++++------- .../patient/middleware-runner.go | 19 +++++ .../use-case/main-use-case/patient/tycovar.go | 6 ++ .../simgos-sync-use-case/patient/case.go | 28 +++++++ 6 files changed, 109 insertions(+), 29 deletions(-) diff --git a/internal/domain/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index ec270abf..56a52e0f 100644 --- a/internal/domain/main-entities/patient/dto.go +++ b/internal/domain/main-entities/patient/dto.go @@ -28,6 +28,7 @@ type CreateDto struct { RegisteredAt *time.Time `json:"registeredAt"` RegisteredBy_User_Name *string `json:"registeredBy_user_name" validate:"maxLength=100"` Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + Number *string `json:"number"` pa.AuthInfo } diff --git a/internal/use-case/main-use-case/patient/case.go b/internal/use-case/main-use-case/patient/case.go index 6c048322..1a8437e5 100644 --- a/internal/use-case/main-use-case/patient/case.go +++ b/internal/use-case/main-use-case/patient/case.go @@ -45,6 +45,13 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } + // Run pre-middleware -> Generate number + nomr, err := mwRunner.RunGenerateNumberMiddleware(generatePatientNumber) + if err != nil { + return err + } + input.Number = nomr + if person_id, err := upe.CreateOrUpdatePerson(input.Person, &event, tx); err != nil { return err } else { diff --git a/internal/use-case/main-use-case/patient/helper.go b/internal/use-case/main-use-case/patient/helper.go index c53ebca3..2c62ca21 100644 --- a/internal/use-case/main-use-case/patient/helper.go +++ b/internal/use-case/main-use-case/patient/helper.go @@ -24,7 +24,7 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Patient) error { inputSrc = &inputTemp.CreateDto } - if data.Id == 0 { + if data.Number == nil { medRecNum, err := GenerateNextMedicalRecordNumber() if err != nil { return err @@ -52,38 +52,57 @@ func GenerateNextMedicalRecordNumber() (string, error) { return "", err } - var nextInt int64 - var format string + //var nextInt int64 + //var format string + //if last == "" { + // // No existing records, start with 10 digits + // nextInt = 1 + // format = "%08d" + //} else { + // n, err := strconv.ParseInt(last, 10, 64) + // if err != nil { + // return "", err + // } + // nextInt = n + 1 + // + // // Dynamically determine format based on existing number + // digitCount := len(last) + // + // // If the incremented number needs more digits, expand format + // nextStr := strconv.FormatInt(nextInt, 10) + // if len(nextStr) > digitCount { + // digitCount = len(nextStr) + // } + // + // // Ensure minimum 10 digits as per requirement + // if digitCount < 10 { + // digitCount = 10 + // } + // + // format = fmt.Sprintf("%%0%dd", digitCount) + //} + + const prefix = "12" // fixed starting prefix (same as $awal_rm) in simgos + const maxSuffix = 999999 + + // No existing NOMR → start fresh if last == "" { - // No existing records, start with 10 digits - nextInt = 1 - format = "%010d" - } else { - n, err := strconv.ParseInt(last, 10, 64) - if err != nil { - return "", err - } - nextInt = n + 1 - - // Dynamically determine format based on existing number - digitCount := len(last) - - // If the incremented number needs more digits, expand format - nextStr := strconv.FormatInt(nextInt, 10) - if len(nextStr) > digitCount { - digitCount = len(nextStr) - } - - // Ensure minimum 10 digits as per requirement - if digitCount < 10 { - digitCount = 10 - } - - format = fmt.Sprintf("%%0%dd", digitCount) + return prefix + "000001", nil } - return fmt.Sprintf(format, nextInt), nil + suffix := last[len(prefix):] // extract numeric part + num, _ := strconv.Atoi(suffix) + + // 3. If suffix reaches 999999 → increment the prefix + if num == maxSuffix { + p, _ := strconv.Atoi(prefix) + p++ + return fmt.Sprintf("%d000001", p), nil + } + + // 4. Normal increment + return prefix + fmt.Sprintf("%06d", num+1), nil } func endpointMapper(noBpjs string) string { diff --git a/internal/use-case/main-use-case/patient/middleware-runner.go b/internal/use-case/main-use-case/patient/middleware-runner.go index f9e05bb7..1ea1623d 100644 --- a/internal/use-case/main-use-case/patient/middleware-runner.go +++ b/internal/use-case/main-use-case/patient/middleware-runner.go @@ -159,6 +159,25 @@ func (me *middlewareRunner) RunDeleteMiddleware(middlewares []deleteMw, input *e return nil } +func (me *middlewareRunner) RunGenerateNumberMiddleware(middlewares generateNumberMw) (generateNorm *string, err error) { + if !me.SyncOn { + return nil, nil + } + + logData := pu.GetLogData(nil, nil) + + pl.SetLogInfo(me.Event, logData, "started", middlewares.Name) + + generateNorm, err = middlewares.Func() + if err != nil { + return nil, pu.HandleMiddlewareError(me.Event, string(me.MwType), middlewares.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + + return +} + func (me *middlewareRunner) setMwType(mwType pu.MWType) { me.MwType = mwType } diff --git a/internal/use-case/main-use-case/patient/tycovar.go b/internal/use-case/main-use-case/patient/tycovar.go index cedb1514..d2370122 100644 --- a/internal/use-case/main-use-case/patient/tycovar.go +++ b/internal/use-case/main-use-case/patient/tycovar.go @@ -50,6 +50,11 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Patient, tx *gorm.DB) error } +type generateNumberMw struct { + Name string + Func func() (*string, error) +} + type UpdateMw = updateMw type DeleteMw = deleteMw @@ -65,6 +70,7 @@ var updatePreMw []updateMw var updatePostMw []readDetailMw var deletePreMw []deleteMw var deletePostMw []readDetailMw +var generatePatientNumber generateNumberMw type BPJSResponse struct { MetaData struct { diff --git a/internal/use-case/simgos-sync-use-case/patient/case.go b/internal/use-case/simgos-sync-use-case/patient/case.go index dd95bed4..b87cd952 100644 --- a/internal/use-case/simgos-sync-use-case/patient/case.go +++ b/internal/use-case/simgos-sync-use-case/patient/case.go @@ -201,3 +201,31 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func Generate() (*d.Data, error) { + event := pl.Event{ + Feature: "Generate Nomr", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, nil, "started", "create") + + norm, err := generateNomrPatient() + if err != nil { + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data:d.II{ + + } + }, nil +} From a5ea881e1f6ff35be2d71acdaa63fd7bca7f9302 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 19 Nov 2025 16:17:23 +0700 Subject: [PATCH 21/23] wip, need to test --- assets/docs/control-letter.html | 20 +- .../vclaim-sep-control-letter/dto.go | 16 +- .../domain/references/encounter/encounter.go | 1 + .../vclaim-sep-control-letter/case.go | 277 ++++++++++++++++++ .../vclaim-sep-control-letter/helper.go | 24 ++ .../vclaim-sep-control-letter/lib.go | 148 ++++++++++ .../middleware-runner.go | 103 +++++++ .../vclaim-sep-control-letter/middleware.go | 9 + .../vclaim-sep-control-letter/tycovar.go | 44 +++ .../main-use-case/generate-file/case.go | 97 +++++- .../main-use-case/generate-file/tycovar.go | 61 +++- 11 files changed, 775 insertions(+), 25 deletions(-) create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/case.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/lib.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware-runner.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-sep-control-letter/tycovar.go diff --git a/assets/docs/control-letter.html b/assets/docs/control-letter.html index 10425b28..c55c7b9d 100644 --- a/assets/docs/control-letter.html +++ b/assets/docs/control-letter.html @@ -37,7 +37,7 @@
          - No. :replace + No. : {{ .Number }}
          @@ -52,10 +52,10 @@
          - DANI SETIAWAN + {{ .Doctor_Name }}
          - Sp./Sub. Rehabilitasi Medik + Sp./Sub. {{ .DstUnit_Name }}
          @@ -72,7 +72,7 @@ No.Kartu - :Replace + : {{ .CardNumber }} @@ -80,7 +80,7 @@ Nama Peserta - :Replace + : {{ .Name }} @@ -88,7 +88,7 @@ Tgl.Lahir - :Replace + : {{ .BirthDate }} @@ -96,7 +96,7 @@ Diagnosa - :Replace + : {{ .Diagnose }} @@ -104,7 +104,7 @@ Rencana Kontrol - :Replace + : {{ .PlanDate }} @@ -117,13 +117,13 @@ - Mengetahui, DPJP :Replace + Mengetahui, DPJP : {{ .ResponsibleDoctor_Name }}
          - Tgl. Entry: 2025-06-07 | Tgl. Cetak: 07-06-2025 16:52 PM + Tgl. Cetak : {{ .PrintDate }}
          diff --git a/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go b/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go index f12b57da..19530c2c 100644 --- a/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go +++ b/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go @@ -6,6 +6,9 @@ import ( type CreateDto struct { VclaimSep_Number *string `json:"vclaimSep_number"` + Number *string `json:"number" gorm:"unique;size:20"` + Value *string `json:"value"` + FileUrl *string `json:"fileUrl" gorm:"unique;size:1024"` } type ReadListDto struct { @@ -20,7 +23,8 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint `json:"id"` + Id *uint `json:"id"` + Number *string `json:"number"` } type UpdateDto struct { @@ -29,7 +33,8 @@ type UpdateDto struct { } type DeleteDto struct { - Id uint `json:"id"` + Id uint `json:"id"` + Number *string `json:"number"` } type MetaDto struct { @@ -41,12 +46,17 @@ type MetaDto struct { type ResponseDto struct { ecore.Main VclaimSep_Number *string `json:"vclaimSep_number"` - Counter *uint `json:"counter"` + Number *string `json:"number"` + Value *string `json:"value"` + FileUrl *string `json:"fileUrl"` } func (d VclaimSepControlLetter) ToResponse() ResponseDto { resp := ResponseDto{ VclaimSep_Number: d.VclaimSep_Number, + Number: d.Number, + Value: d.Value, + FileUrl: d.FileUrl, } resp.Main = d.Main return resp diff --git a/internal/domain/references/encounter/encounter.go b/internal/domain/references/encounter/encounter.go index 532e1625..1acee75b 100644 --- a/internal/domain/references/encounter/encounter.go +++ b/internal/domain/references/encounter/encounter.go @@ -120,6 +120,7 @@ const ( DTCSEP DocTypeCode = "vclaim-sep" // SEP DTCSIPP DocTypeCode = "vclaim-sipp" // SIPP DTCGC DocTypeCode = "general-consent" + DTCVSCL DocTypeCode = "vclaim-control-letter" // vclaim control letter ETCPerson EntityTypeCode = "person" ETCEncounter EntityTypeCode = "encounter" diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/case.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/case.go new file mode 100644 index 00000000..7c19819f --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/case.go @@ -0,0 +1,277 @@ +package vclaimsepcontrolletter + +import ( + "strconv" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "vclaim-sep-control-letter" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.VclaimSepControlLetter{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.VclaimSepControlLetter + var dataList []e.VclaimSepControlLetter + var metaList *e.MetaDto + 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 &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.VclaimSepControlLetter + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Number: input.Number} + var data *e.VclaimSepControlLetter + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Number: input.Number} + var data *e.VclaimSepControlLetter + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go new file mode 100644 index 00000000..891e7c4d --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go @@ -0,0 +1,24 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package vclaimsepcontrolletter + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.VclaimSepControlLetter) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.VclaimSep_Number = inputSrc.VclaimSep_Number + data.Number = inputSrc.Number + data.Value = inputSrc.Value + data.FileUrl = inputSrc.FileUrl +} diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/lib.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/lib.go new file mode 100644 index 00000000..9f66baff --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/lib.go @@ -0,0 +1,148 @@ +package vclaimsepcontrolletter + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.VclaimSepControlLetter, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.VclaimSepControlLetter{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + setData(&input, &data) + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.VclaimSepControlLetter, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.VclaimSepControlLetter{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.VclaimSepControlLetter{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.VclaimSepControlLetter, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.VclaimSepControlLetter{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if input.Number != nil { + tx = tx.Where("\"Number\" = ?", *input.Number) + } + if input.Id != nil { + tx = tx.Where("\"Id\" = ?", *input.Id) + } + + if err := tx.First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.VclaimSepControlLetter, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&input, data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func DeleteData(data *e.VclaimSepControlLetter, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware-runner.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware-runner.go new file mode 100644 index 00000000..c0a0ecae --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware-runner.go @@ -0,0 +1,103 @@ +package vclaimsepcontrolletter + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" + 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, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.VclaimSepControlLetter) 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) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.VclaimSepControlLetter) 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) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.VclaimSepControlLetter) 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) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.VclaimSepControlLetter) 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) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.VclaimSepControlLetter) 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/vclaim-sep-control-letter/middleware.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go new file mode 100644 index 00000000..57262cc2 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go @@ -0,0 +1,9 @@ +package vclaimsepcontrolletter + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/tycovar.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/tycovar.go new file mode 100644 index 00000000..2e921ae9 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/tycovar.go @@ -0,0 +1,44 @@ +/* +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 vclaimsepcontrolletter + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.VclaimSepControlLetter, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.VclaimSepControlLetter, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.VclaimSepControlLetter, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go index 1c3ca51b..f0e71203 100644 --- a/internal/use-case/main-use-case/generate-file/case.go +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -4,9 +4,15 @@ import ( "encoding/json" "errors" + evs "simrs-vx/internal/domain/bpjs-entities/vclaim-sep" + evscl "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" egc "simrs-vx/internal/domain/main-entities/general-consent" + + uvs "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep" + uvscl "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep-control-letter" ugc "simrs-vx/internal/use-case/main-use-case/general-consent" + pc "simrs-vx/pkg/conv-helper" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -25,6 +31,8 @@ func Generate(input GenerateDto) (*d.Data, error) { Source: source, } + var response ResponseDto + // Start log pl.SetLogInfo(&event, input, "started", "create") @@ -32,7 +40,7 @@ func Generate(input GenerateDto) (*d.Data, error) { // general-consent case ere.DTCGC: // get value from general consent by ref_id - gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *input.Ref_Id}, &event) + gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id))}, &event) if err != nil { return nil, err } @@ -75,20 +83,89 @@ func Generate(input GenerateDto) (*d.Data, error) { return nil, err } - response := ResponseDto{ + response = ResponseDto{ + FileUrl: urlPub, + } + + // control-letter + case ere.DTCVSCL: + // get value from control letter by ref_id + cl, err := uvscl.ReadDetailData(evscl.ReadDetailDto{Number: input.Ref_Id}, &event) + if err != nil { + if !pu.IsDataNotFoundError(err) { + return nil, err + } + } + + if cl != nil && cl.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *cl.FileUrl); err != nil { + return nil, err + } + } + + // map template data + clData := VclaimControlLetter{} + if input.Data != nil { + err := json.Unmarshal([]byte(*input.Data), &clData) + if err != nil { + event.ErrInfo = pl.ErrorInfo{ + Code: "data-unmarshal-fail", + Detail: err.Error(), + Raw: err, + } + return nil, err + } + } else { + return nil, errors.New("there is no data to be used") + } + + if cl == nil { + createCL := evscl.CreateDto{ + VclaimSep_Number: &clData.VclaimSep.Number, + Number: &clData.Number, + Value: input.Data, + } + if cl, err = uvscl.CreateData(createCL, &event); err != nil { + return nil, err + } + + } + // get encounter id by vclaim sep number + vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: &clData.VclaimSep.Number}, &event) + if err != nil { + return nil, err + } + + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNCL + input.Encounter_Id = vs.Encounter_Id + + templateData := clData.generateTemplateData() + // generate file + urlPub, err := generateFile(input, templateData) + if err != nil { + return nil, err + } + + cl.FileUrl = &urlPub + if err := dg.I.Save(&cl).Error; err != nil { + return nil, err + } + + response = ResponseDto{ FileUrl: urlPub, } - return &d.Data{ - Meta: d.II{ - "source": source, - "structure": "single-data", - "status": "created", - }, - Data: response, - }, nil default: return nil, errors.New("invalid type code") } + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: response, + }, nil } diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index fed3e28c..ecfbc6a1 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -1,8 +1,13 @@ package generatefile import ( + "fmt" + "time" + erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" + + pu "simrs-vx/pkg/use-case-helper" ) type GeneralConsentPDF struct { @@ -14,18 +19,34 @@ type GeneralConsentPDF struct { Date string `json:"date"` } +type ControlLetterPDF struct { + Number string + Doctor_Name string + DstUnit_Name string + CardNumber string + Name string + BirthDate string + Diagnose string + PlanDate string + ResponsibleDoctor_Name string + PrintDate string +} + type GenerateDto struct { EntityType_Code ere.EntityTypeCode `json:"entityType_code" validate:"required"` - Ref_Id *uint `json:"ref_id" validate:"required"` + Ref_Id *string `json:"ref_id" validate:"required"` Type_Code ere.DocTypeCode `json:"type_code" validate:"required"` FormatType erc.DocFormatTypeCode `json:"formatType"` TemplateName TemplateDocsName `json:"-"` Encounter_Id *uint `json:"-"` + + Data *string `json:"data"` } type VclaimControlLetter struct { Number string `json:"noSuratKontrol"` - Date string `json:"tglRencanaKontrol"` + PlannedControlDate string `json:"tglRencanaKontrol"` + IssuedDate string `json:"tglTerbit"` Doctor_Name string `json:"namaDokter"` DstUnit_Name string `json:"namaPoliTujuan"` ResponsibleDoctor_Name string `json:"namaDokterPembuat"` @@ -60,4 +81,40 @@ type TemplateDocsName string // TemplateDocsName is the name of the template file in the assets/docs folder const ( TDNGC TemplateDocsName = "general-consent.html" + TDNCL TemplateDocsName = "control-letter.html" ) + +func (v VclaimControlLetter) generateTemplateData() ControlLetterPDF { + + return ControlLetterPDF{ + Number: v.Number, + Doctor_Name: v.Doctor_Name, + DstUnit_Name: v.DstUnit_Name, + CardNumber: v.VclaimSep.VclaimMember.CardNumber, + Name: v.generateNameWithGender(), + BirthDate: v.generateBirthDate(), + Diagnose: v.VclaimSep.Diagnose, + PlanDate: v.PlannedControlDate, + ResponsibleDoctor_Name: v.ResponsibleDoctor_Name, + PrintDate: generatePrintDate(), + } +} + +func (v VclaimControlLetter) generateNameWithGender() string { + gender := "Perempuan" + if v.VclaimSep.VclaimMember.Gender == "L" { + gender = "Laki-Laki" + } + + return fmt.Sprintf("%s (%s)", v.VclaimSep.VclaimMember.Name, gender) +} + +func (v VclaimControlLetter) generateBirthDate() string { + t, _ := time.Parse("2006-01-02", v.VclaimSep.VclaimMember.BirthDate) + return pu.FormatIndonesianDate(t) +} + +func generatePrintDate() string { + now := time.Now() + return now.Format("2006/01/02") +} From 9a056701c6bdd4257eaba6764f8ab8666e4a35d2 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 20 Nov 2025 09:10:58 +0700 Subject: [PATCH 22/23] update nomr patient --- .../simgos-sync-handler/patient/handler.go | 5 +++ .../simgos-sync-handler.go | 2 + .../use-case/main-use-case/patient/helper.go | 5 ++- .../main-use-case/patient/middleware.go | 2 + .../simgos-sync-plugin/patient/plugin.go | 40 +++++++++++++++++++ .../simgos-sync-use-case/patient/case.go | 8 ++-- .../simgos-sync-use-case/patient/helper.go | 1 + .../simgos-sync-use-case/patient/lib.go | 6 --- 8 files changed, 58 insertions(+), 11 deletions(-) diff --git a/internal/interface/simgos-sync-handler/patient/handler.go b/internal/interface/simgos-sync-handler/patient/handler.go index ae2e1db8..cfc0c8da 100644 --- a/internal/interface/simgos-sync-handler/patient/handler.go +++ b/internal/interface/simgos-sync-handler/patient/handler.go @@ -61,3 +61,8 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { res, err := u.Delete(dto) rw.DataResponse(w, res, err) } + +func (obj myBase) GenerateNomr(w http.ResponseWriter, r *http.Request) { + res, err := u.GenerateNomr() + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index dee6fb3f..2a67b7e9 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -1,6 +1,7 @@ package simgossynchandler import ( + "fmt" "net/http" hc "simrs-vx/pkg/handler-crud-helper" @@ -45,6 +46,7 @@ func SetRoutes() http.Handler { hc.SyncCrud(r, prefix+"/v1/specialist", specialist.O) hc.SyncCrud(r, prefix+"/v1/subspecialist", subspecialist.O) hc.SyncCrud(r, prefix+"/v1/patient", patient.O) + r.HandleFunc(fmt.Sprintf("GET %s/v1/patient-nomr-generator", prefix), patient.O.GenerateNomr) return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/use-case/main-use-case/patient/helper.go b/internal/use-case/main-use-case/patient/helper.go index 2c62ca21..82142632 100644 --- a/internal/use-case/main-use-case/patient/helper.go +++ b/internal/use-case/main-use-case/patient/helper.go @@ -24,12 +24,15 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Patient) error { inputSrc = &inputTemp.CreateDto } - if data.Number == nil { + switch { + case inputSrc.Number == nil && data.Number == nil: medRecNum, err := GenerateNextMedicalRecordNumber() if err != nil { return err } data.Number = &medRecNum + case inputSrc.Number != nil: + data.Number = inputSrc.Number } data.Person_Id = inputSrc.Person_Id diff --git a/internal/use-case/main-use-case/patient/middleware.go b/internal/use-case/main-use-case/patient/middleware.go index cc97473f..4270c14c 100644 --- a/internal/use-case/main-use-case/patient/middleware.go +++ b/internal/use-case/main-use-case/patient/middleware.go @@ -29,6 +29,8 @@ func init() { deletePreMw = append(deletePreMw, deleteMw{Name: "sync-delete-patient", Func: plugin.Delete}) + + generatePatientNumber = generateNumberMw{Name: "generate-nomr-patient", Func: plugin.GenerateNomrPatient} } func checkVclaimMember(input *e.CreateDto, data *e.Patient, tx *gorm.DB) error { diff --git a/internal/use-case/simgos-sync-plugin/patient/plugin.go b/internal/use-case/simgos-sync-plugin/patient/plugin.go index b10fdce5..ebfb8dc4 100644 --- a/internal/use-case/simgos-sync-plugin/patient/plugin.go +++ b/internal/use-case/simgos-sync-plugin/patient/plugin.go @@ -162,6 +162,46 @@ func Delete(input *e.DeleteDto) error { return nil } +func GenerateNomrPatient() (*string, error) { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s-nomr-generator", prefixEndpoint) + + req, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + errors := d.FieldError{} + _ = json.Unmarshal(bodyBytes, &errors) + + return nil, fmt.Errorf(errors.Message) + } + + var result map[string]any + if err = json.Unmarshal(bodyBytes, &result); err != nil { + return nil, err + } + + dataMap := result["data"].(map[string]any) + nomr := dataMap["nomr"].(string) + + return &nomr, nil +} + func getPrefixEndpoint() string { return fmt.Sprintf("%s%s/v1/patient", sync.O.Host, sync.O.Prefix) } diff --git a/internal/use-case/simgos-sync-use-case/patient/case.go b/internal/use-case/simgos-sync-use-case/patient/case.go index b87cd952..d403a8ac 100644 --- a/internal/use-case/simgos-sync-use-case/patient/case.go +++ b/internal/use-case/simgos-sync-use-case/patient/case.go @@ -202,7 +202,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { } -func Generate() (*d.Data, error) { +func GenerateNomr() (*d.Data, error) { event := pl.Event{ Feature: "Generate Nomr", Source: source, @@ -224,8 +224,8 @@ func Generate() (*d.Data, error) { "structure": "single-data", "status": "created", }, - Data:d.II{ - - } + Data: d.IS{ + "nomr": norm, + }, }, nil } diff --git a/internal/use-case/simgos-sync-use-case/patient/helper.go b/internal/use-case/simgos-sync-use-case/patient/helper.go index 4b7ddfe6..0bffcc9b 100644 --- a/internal/use-case/simgos-sync-use-case/patient/helper.go +++ b/internal/use-case/simgos-sync-use-case/patient/helper.go @@ -43,6 +43,7 @@ func setDataSimgos(input *e.Patient) (data esimgos.MPasien) { data.Nip = input.RegisteredBy_User_Name data.Tgldaftar = input.RegisteredAt + data.Nomr = *input.Number return } diff --git a/internal/use-case/simgos-sync-use-case/patient/lib.go b/internal/use-case/simgos-sync-use-case/patient/lib.go index 20f6cdcf..6d50f877 100644 --- a/internal/use-case/simgos-sync-use-case/patient/lib.go +++ b/internal/use-case/simgos-sync-use-case/patient/lib.go @@ -21,11 +21,6 @@ func CreateSimgosData(input e.Patient, event *pl.Event, dbx ...*gorm.DB) (*esimg pl.SetLogInfo(event, nil, "started", "DBCreate") data := setDataSimgos(&input) - nomr, err := generateNomrPatient() - if err != nil { - return nil, plh.HandleCreateError(input, event, err) - } - data.Nomr = nomr var tx *gorm.DB if len(dbx) > 0 { @@ -65,7 +60,6 @@ func UpdateSimgosData(input e.Patient, patientData *esimgos.MPasien, event *pl.E data := setDataSimgos(&input) data.Id = patientData.Id - data.Nomr = patientData.Nomr data.CreatedAt = patientData.CreatedAt var tx *gorm.DB From ffb8c4e413b410c84d4337e482b5dd0c7b30180a Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Thu, 20 Nov 2025 13:18:33 +0700 Subject: [PATCH 23/23] feat (generate-file): control letter done --- assets/docs/control-letter.html | 67 ++++-- .../main-use-case/generate-file/case.go | 226 +++++++++--------- .../main-use-case/generate-file/helper.go | 9 +- .../main-use-case/generate-file/tycovar.go | 9 +- pkg/pdf-helper/pdf-helper.go | 19 +- 5 files changed, 185 insertions(+), 145 deletions(-) diff --git a/assets/docs/control-letter.html b/assets/docs/control-letter.html index c55c7b9d..780a052f 100644 --- a/assets/docs/control-letter.html +++ b/assets/docs/control-letter.html @@ -3,6 +3,9 @@ @@ -25,7 +34,7 @@ - - - -
          - logo + logo
          @@ -35,15 +44,10 @@ RSUD dr. Saiful Anwar
          -
          - No. : {{ .Number }} -
          +
          + No. : {{ .Number }}
          @@ -65,46 +69,50 @@ Mohon Pemeriksaan dan Penanganan Lebih Lanjut:
          - + + + + +
          No.Kartu : - : {{ .CardNumber }} + {{ .CardNumber }}
          Nama Peserta : - : {{ .Name }} + {{ .Name }}
          Tgl.Lahir : - : {{ .BirthDate }} + {{ .BirthDate }}
          Diagnosa : - : {{ .Diagnose }} + {{ .Diagnose }}
          Rencana Kontrol : - : {{ .PlanDate }} + {{ .PlanDate }}
          @@ -112,19 +120,26 @@
          Demikian atas bantuannya, diucapkan banyak terima kasih.
          - - - - - - -
          - Mengetahui, DPJP : {{ .ResponsibleDoctor_Name }} -
          -
          - Tgl. Cetak : {{ .PrintDate }} -
          + + + + + + + + + + + + + + + + + + +
          Mengetahui:
          Tgl. Cetak: {{ .PrintDate }}{{ .ResponsibleDoctor_Name }}
          diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go index f0e71203..39cb2f02 100644 --- a/internal/use-case/main-use-case/generate-file/case.go +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -18,6 +18,7 @@ import ( dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" + "gorm.io/gorm" erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" @@ -36,128 +37,137 @@ func Generate(input GenerateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - switch input.Type_Code { - // general-consent - case ere.DTCGC: - // get value from general consent by ref_id - gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id))}, &event) - if err != nil { - return nil, err - } - - if gc.FileUrl != nil { - if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil { - return nil, err - } - } - - // map template data - templateData := GeneralConsentPDF{} - if gc.Value != nil { - err := json.Unmarshal([]byte(*gc.Value), &templateData) + err := dg.I.Transaction(func(tx *gorm.DB) error { + switch input.Type_Code { + // general-consent + case ere.DTCGC: + // get value from general consent by ref_id + gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id))}, &event) if err != nil { - event.ErrInfo = pl.ErrorInfo{ - Code: "data-unmarshal-fail", - Detail: err.Error(), - Raw: err, + return err + } + + if gc.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil { + return err } - return nil, err } - } else { - return nil, errors.New("no value in this general consent") - } - input.FormatType = erc.DFTCPDF - input.TemplateName = TDNGC - input.Encounter_Id = gc.Encounter_Id - templateData.Date = pu.FormatIndonesianDate(gc.CreatedAt) - - // generate file - urlPub, err := generateFile(input, templateData) - if err != nil { - return nil, err - } - - gc.FileUrl = &urlPub - if err := dg.I.Save(&gc).Error; err != nil { - return nil, err - } - - response = ResponseDto{ - FileUrl: urlPub, - } - - // control-letter - case ere.DTCVSCL: - // get value from control letter by ref_id - cl, err := uvscl.ReadDetailData(evscl.ReadDetailDto{Number: input.Ref_Id}, &event) - if err != nil { - if !pu.IsDataNotFoundError(err) { - return nil, err + // map template data + templateData := GeneralConsentPDF{} + if gc.Value != nil { + err := json.Unmarshal([]byte(*gc.Value), &templateData) + if err != nil { + event.ErrInfo = pl.ErrorInfo{ + Code: "data-unmarshal-fail", + Detail: err.Error(), + Raw: err, + } + return err + } + } else { + return errors.New("no value in this general consent") } - } - if cl != nil && cl.FileUrl != nil { - if err := removeFile(string(input.EntityType_Code), *cl.FileUrl); err != nil { - return nil, err - } - } + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNGC + input.Encounter_Id = gc.Encounter_Id + templateData.Date = pu.FormatIndonesianDate(gc.CreatedAt) - // map template data - clData := VclaimControlLetter{} - if input.Data != nil { - err := json.Unmarshal([]byte(*input.Data), &clData) + // generate file + urlPub, err := generateFile(input, templateData) if err != nil { - event.ErrInfo = pl.ErrorInfo{ - Code: "data-unmarshal-fail", - Detail: err.Error(), - Raw: err, + return err + } + + gc.FileUrl = &urlPub + if err := dg.I.Save(&gc).Error; err != nil { + return err + } + + response = ResponseDto{ + FileUrl: urlPub, + } + + // control-letter + case ere.DTCVSCL: + // get value from control letter by ref_id + cl, err := uvscl.ReadDetailData(evscl.ReadDetailDto{Number: input.Ref_Id}, &event) + if err != nil { + if !pu.IsDataNotFoundError(err) { + return err } - return nil, err - } - } else { - return nil, errors.New("there is no data to be used") - } - - if cl == nil { - createCL := evscl.CreateDto{ - VclaimSep_Number: &clData.VclaimSep.Number, - Number: &clData.Number, - Value: input.Data, - } - if cl, err = uvscl.CreateData(createCL, &event); err != nil { - return nil, err } - } - // get encounter id by vclaim sep number - vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: &clData.VclaimSep.Number}, &event) - if err != nil { - return nil, err + if cl != nil && cl.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *cl.FileUrl); err != nil { + return err + } + } + + // map template data + clData := VclaimControlLetter{} + if input.Data != nil { + err := json.Unmarshal([]byte(*input.Data), &clData) + if err != nil { + event.ErrInfo = pl.ErrorInfo{ + Code: "data-unmarshal-fail", + Detail: err.Error(), + Raw: err, + } + return err + } + } else { + return errors.New("there is no data to be used") + } + + if cl == nil { + createCL := evscl.CreateDto{ + VclaimSep_Number: &clData.VclaimSep.Number, + Number: &clData.Number, + Value: input.Data, + } + if cl, err = uvscl.CreateData(createCL, &event, tx); err != nil { + return err + } + + } + // get encounter id by vclaim sep number + vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: &clData.VclaimSep.Number}, &event) + if err != nil { + return err + } + + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNCL + input.Encounter_Id = vs.Encounter_Id + input.UseA5Lanscape = true + + templateData := clData.generateTemplateData() + // generate file + urlPub, err := generateFile(input, templateData) + if err != nil { + return err + } + + cl.FileUrl = &urlPub + if err := tx.Save(&cl).Error; err != nil { + return err + } + + response = ResponseDto{ + FileUrl: urlPub, + } + + default: + return errors.New("invalid type code") } - input.FormatType = erc.DFTCPDF - input.TemplateName = TDNCL - input.Encounter_Id = vs.Encounter_Id + return nil + }) - templateData := clData.generateTemplateData() - // generate file - urlPub, err := generateFile(input, templateData) - if err != nil { - return nil, err - } - - cl.FileUrl = &urlPub - if err := dg.I.Save(&cl).Error; err != nil { - return nil, err - } - - response = ResponseDto{ - FileUrl: urlPub, - } - - default: - return nil, errors.New("invalid type code") + if err != nil { + return nil, err } return &d.Data{ diff --git a/internal/use-case/main-use-case/generate-file/helper.go b/internal/use-case/main-use-case/generate-file/helper.go index 9bee2212..8cd3543f 100644 --- a/internal/use-case/main-use-case/generate-file/helper.go +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -30,9 +30,10 @@ func generateFile(input GenerateDto, templateData any) (string, error) { switch input.FormatType { case erc.DFTCPDF: if err := generatePDF(GeneratePDFdto{ - TemplatePath: templatePath, - TemplateData: templateData, - PdfPath: fPath, + TemplatePath: templatePath, + TemplateData: templateData, + PdfPath: fPath, + UseA5Lanscape: input.UseA5Lanscape, }); err != nil { return "", err } @@ -73,7 +74,7 @@ func generatePDF(input GeneratePDFdto) error { // parse template data into html template r := pp.NewRequestPdf("") if err := r.ParseTemplate(input.TemplatePath, input.TemplateData); err == nil { - _, err := r.GenerateByCommand(input.PdfPath, input.TemplatePath) + _, err := r.GenerateByCommand(input.PdfPath, input.TemplatePath, input.UseA5Lanscape) if err != nil { return errors.New("generate pdf by command error : " + err.Error()) } diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index ecfbc6a1..2f6a0ced 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -39,6 +39,7 @@ type GenerateDto struct { FormatType erc.DocFormatTypeCode `json:"formatType"` TemplateName TemplateDocsName `json:"-"` Encounter_Id *uint `json:"-"` + UseA5Lanscape bool `json:"-"` Data *string `json:"data"` } @@ -46,7 +47,6 @@ type GenerateDto struct { type VclaimControlLetter struct { Number string `json:"noSuratKontrol"` PlannedControlDate string `json:"tglRencanaKontrol"` - IssuedDate string `json:"tglTerbit"` Doctor_Name string `json:"namaDokter"` DstUnit_Name string `json:"namaPoliTujuan"` ResponsibleDoctor_Name string `json:"namaDokterPembuat"` @@ -67,9 +67,10 @@ type VclaimMember struct { } type GeneratePDFdto struct { - TemplatePath string - TemplateData any - PdfPath string + TemplatePath string + TemplateData any + PdfPath string + UseA5Lanscape bool } type ResponseDto struct { diff --git a/pkg/pdf-helper/pdf-helper.go b/pkg/pdf-helper/pdf-helper.go index 597052eb..9878ecbd 100644 --- a/pkg/pdf-helper/pdf-helper.go +++ b/pkg/pdf-helper/pdf-helper.go @@ -47,9 +47,9 @@ func (r *RequestPdf) ParseTemplate(templatePath string, data interface{}) error return nil } -func (r *RequestPdf) GenerateByCommand(pdfPath string, templatePath string) (bool, error) { - // wkhtmltopdf -L 0 -R 0 -B 0 -s Legal --enable-local-file-access resultAntigen2.html out.pdf +func (r *RequestPdf) GenerateByCommand(pdfPath string, templatePath string, useA5Lanscape bool) (bool, error) { t := time.Now().Unix() + var cmd *exec.Cmd if _, err := os.Stat("cloneTemplate/"); os.IsNotExist(err) { errDir := os.Mkdir("cloneTemplate/", 0777) @@ -63,7 +63,20 @@ func (r *RequestPdf) GenerateByCommand(pdfPath string, templatePath string) (boo panic(err) } - cmd := exec.Command("wkhtmltopdf", "--enable-local-file-access", "-L", "0", "-R", "0", "-B", "0", "-s", "A4", "cloneTemplate/"+htmlName, pdfPath) + if !useA5Lanscape { + cmd = exec.Command("wkhtmltopdf", "--enable-local-file-access", "-L", "0", "-R", "0", "-B", "0", "-s", "A4", "cloneTemplate/"+htmlName, pdfPath) + } else { + cmd = exec.Command("wkhtmltopdf", + "--enable-local-file-access", + "-L", "0", // left margin + "-R", "0", // right margin + "-B", "0", // bottom margin + "-T", "0", // top margin + "-s", "A5", // page size A5 + "-O", "Landscape", // landscape mode + "cloneTemplate/"+htmlName, + pdfPath) + } if err := cmd.Run(); err != nil { return false, err