diff --git a/assets/docs/control-letter.html b/assets/docs/control-letter.html new file mode 100644 index 00000000..780a052f --- /dev/null +++ b/assets/docs/control-letter.html @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + +
+ logo + +
+ SURAT RENCANA KONTROL +
+
+ RSUD dr. Saiful Anwar +
+
+ No. : {{ .Number }} +
+
+ Kepada Yth +
+
+
+ {{ .Doctor_Name }} +
+
+ Sp./Sub. {{ .DstUnit_Name }} +
+
+ +
+ Mohon Pemeriksaan dan Penanganan Lebih Lanjut: +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ No.Kartu + : + {{ .CardNumber }} +
+ Nama Peserta + : + {{ .Name }} +
+ Tgl.Lahir + : + {{ .BirthDate }} +
+ Diagnosa + : + {{ .Diagnose }} +
+ Rencana Kontrol + : + {{ .PlanDate }} +
+ +
+ Demikian atas bantuannya, diucapkan banyak terima kasih. +
+ + + + + + + + + + + + + + + + + + + +
Mengetahui:
Tgl. Cetak: {{ .PrintDate }}{{ .ResponsibleDoctor_Name }}
+ +
+ + + \ No newline at end of file diff --git a/assets/docs/general-consent.html b/assets/docs/general-consent.html new file mode 100644 index 00000000..de84a742 --- /dev/null +++ b/assets/docs/general-consent.html @@ -0,0 +1,325 @@ + + + + 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 : + {{ if eq (len .Relatives) 0 }} + .......................................... +{{ else }} +
    + {{ range $i, $name := .Relatives }} + {{ 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 + 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, {{ .Date }} +
+ + + + + + + + + + + + + + +
+
+ Pasien/keluarga/
penanggung jawab +
+ +
+ ....................................... +
+ +
+ {{ .Responsible }} +
+
+
+ Pemberi Informasi +
+ +
+ ....................................... +
+ +
+ {{ .Informant }} +
+
+
+ Saksi I +
+ +
+ ....................................... +
+ +
+ {{ .Witness1 }} +
+
+
+ Saksi II +
+ +
+ ....................................... +
+ +
+ {{ .Witness2 }} +
+
+ + diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index 448cff66..30351abf 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -69,5 +69,9 @@ bpjsCfg: baseUrl: syncUrlCfg: + enable: false 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/go.mod b/go.mod index 9d557637..493ad0a2 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.24.6 require ( ariga.io/atlas-provider-gorm v0.5.6 + github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.5.5 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/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/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/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/general-consent/dto.go b/internal/domain/main-entities/general-consent/dto.go new file mode 100644 index 00000000..03dd7dc9 --- /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:"encounter_id"` + 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 622c82bc..7142f97e 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/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index ec270abf..6418c6ac 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" ) @@ -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 } @@ -68,7 +69,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/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/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/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/domain/references/encounter/encounter.go b/internal/domain/references/encounter/encounter.go index dc3dd09e..1acee75b 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,23 @@ 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" + DTCVSCL DocTypeCode = "vclaim-control-letter" // vclaim control letter + + ETCPerson EntityTypeCode = "person" + ETCEncounter EntityTypeCode = "encounter" + ETCMCU EntityTypeCode = "mcu" ) func (ec EncounterClassCode) Code() string { @@ -118,3 +139,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/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 605c5722..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"` 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/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/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/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 aa6f76fb..4a49c022 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -17,6 +17,8 @@ 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" + 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" @@ -34,7 +36,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" @@ -57,6 +59,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 +134,7 @@ func SetRoutes() http.Handler { a.RegisterExtCall(ibpjs.SetConfig) a.RegisterExtCall(validation.RegisterValidation) a.RegisterExtCall(simgossync.SetConfig) + a.RegisterExtCall(docscfg.ParseCfg) r := http.NewServeMux() @@ -274,8 +278,10 @@ 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) + 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/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/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/patient/handler.go b/internal/interface/simgos-sync-handler/patient/handler.go new file mode 100644 index 00000000..cfc0c8da --- /dev/null +++ b/internal/interface/simgos-sync-handler/patient/handler.go @@ -0,0 +1,68 @@ +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) +} + +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 aebcc9bb..2a67b7e9 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -1,7 +1,9 @@ package simgossynchandler import ( + "fmt" "net/http" + hc "simrs-vx/pkg/handler-crud-helper" /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" @@ -14,11 +16,14 @@ 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" + 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" ) func SetRoutes() http.Handler { @@ -35,12 +40,13 @@ 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, - }) + 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) + 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/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/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/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/division/case.go b/internal/use-case/main-use-case/division/case.go index c955aed7..8f2e8488 100644 --- a/internal/use-case/main-use-case/division/case.go +++ b/internal/use-case/main-use-case/division/case.go @@ -1,16 +1,19 @@ package division import ( - e "simrs-vx/internal/domain/main-entities/division" "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" @@ -25,36 +28,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 +79,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 +130,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 +176,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 +184,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 +227,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 +235,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 +265,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..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,35 +1,64 @@ package division import ( - e "simrs-vx/internal/domain/main-entities/division" 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, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, - Tx: tx, + Event: event, + SyncOn: sync.O.Enable, } } // 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 { + 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) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// 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) + + 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) } @@ -39,6 +68,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e } 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) @@ -54,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) @@ -68,13 +105,17 @@ 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 { + 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 +124,17 @@ 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 { + 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/division/middleware.go b/internal/use-case/main-use-case/division/middleware.go index f066ffb8..83452940 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: "sync-create-division", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "sync-update-division", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "sync-delete-division", 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/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/general-consent/case.go b/internal/use-case/main-use-case/general-consent/case.go new file mode 100644 index 00000000..e6dd2a61 --- /dev/null +++ b/internal/use-case/main-use-case/general-consent/case.go @@ -0,0 +1,295 @@ +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" + + 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 + } + + // 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 { + 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 + } + + // 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 + } + + // 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..39cb2f02 --- /dev/null +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -0,0 +1,181 @@ +package generatefile + +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" + + 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" +) + +const source = "generate-file" + +func Generate(input GenerateDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Generate", + Source: source, + } + + var response ResponseDto + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + 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 { + return err + } + + if gc.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil { + return 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") + } + + 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 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 + } + } + + 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") + } + + return nil + }) + + if err != nil { + return nil, err + } + + 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/helper.go b/internal/use-case/main-use-case/generate-file/helper.go new file mode 100644 index 00000000..8cd3543f --- /dev/null +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -0,0 +1,95 @@ +package generatefile + +import ( + "errors" + "fmt" + "mime" + "path/filepath" + "time" + + 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("./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) + + templatePath := docscfg.O.GetPath() + string(input.TemplateName) + + switch input.FormatType { + case erc.DFTCPDF: + if err := generatePDF(GeneratePDFdto{ + TemplatePath: templatePath, + TemplateData: templateData, + PdfPath: fPath, + UseA5Lanscape: input.UseA5Lanscape, + }); 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("%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, + 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, input.UseA5Lanscape) + if err != nil { + return errors.New("generate pdf by command error : " + err.Error()) + } + } else { + return errors.New("parse template error : " + err.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 fed3e28c..2f6a0ced 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:"-"` + UseA5Lanscape bool `json:"-"` + + Data *string `json:"data"` } type VclaimControlLetter struct { Number string `json:"noSuratKontrol"` - Date string `json:"tglRencanaKontrol"` + PlannedControlDate string `json:"tglRencanaKontrol"` Doctor_Name string `json:"namaDokter"` DstUnit_Name string `json:"namaPoliTujuan"` ResponsibleDoctor_Name string `json:"namaDokterPembuat"` @@ -46,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 { @@ -60,4 +82,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") +} 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/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index dd4b0b42..f32b7381 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -7,16 +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..1a8437e5 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,15 +36,22 @@ 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 } + // 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 { @@ -88,21 +98,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 +143,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 +194,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 +240,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 +248,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 +286,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, tx) + 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 +329,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 +337,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 +474,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/helper.go b/internal/use-case/main-use-case/patient/helper.go index c53ebca3..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.Id == 0 { + 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 @@ -52,38 +55,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/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..1ea1623d 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) } @@ -98,6 +159,25 @@ func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, inpu 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/middleware.go b/internal/use-case/main-use-case/patient/middleware.go index 6b0175dc..4270c14c 100644 --- a/internal/use-case/main-use-case/patient/middleware.go +++ b/internal/use-case/main-use-case/patient/middleware.go @@ -6,16 +6,31 @@ 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}) + + 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/main-use-case/patient/tycovar.go b/internal/use-case/main-use-case/patient/tycovar.go index 29e5c368..d2370122 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,19 +50,27 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Patient, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type generateNumberMw struct { + Name string + Func func() (*string, error) +} -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +type UpdateMw = updateMw +type DeleteMw = deleteMw + +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 +var generatePatientNumber generateNumberMw type BPJSResponse struct { MetaData 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/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..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,35 +1,64 @@ package specialist import ( - e "simrs-vx/internal/domain/main-entities/specialist" 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, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, - Tx: tx, + Event: event, + SyncOn: sync.O.Enable, } } // 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 { + 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) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +// 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) + + 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) } @@ -39,6 +68,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e } 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) @@ -54,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) @@ -68,13 +105,17 @@ 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 { + 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 +124,17 @@ 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 { + 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/specialist/middleware.go b/internal/use-case/main-use-case/specialist/middleware.go index 298e51a8..969436e5 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: "sync-create-specialist", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "sync-update-specialiast", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "sync-delete-specialist", 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..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,35 +1,63 @@ package subspecialist import ( - e "simrs-vx/internal/domain/main-entities/subspecialist" 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, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, - Tx: tx, + Event: event, + SyncOn: sync.O.Enable, } } // 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 { + 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) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +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) } @@ -39,6 +67,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e } 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) @@ -54,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) @@ -68,13 +104,17 @@ 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 { + 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 +123,17 @@ 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 { + 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/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/main-use-case/unit/case.go b/internal/use-case/main-use-case/unit/case.go index 650c4511..1db6fb79 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 { + 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 +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..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,35 +1,64 @@ package unit import ( - e "simrs-vx/internal/domain/main-entities/unit" 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, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ - Event: event, - Tx: tx, + Event: event, + SyncOn: sync.O.Enable, } } // 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 { + 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) + } + + 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) } @@ -39,6 +68,10 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e } 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) @@ -54,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) @@ -68,13 +105,17 @@ 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 { + 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 +124,17 @@ 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 { + 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/unit/middleware.go b/internal/use-case/main-use-case/unit/middleware.go index bac48f4d..0e62f502 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: "sync-create-unit", Func: plugin.Create}) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-sync-log", Func: plugin.CreateLog}) + + updatePreMw = append(updatePreMw, + updateMw{Name: "sync-update-unit", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "sync-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/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/simgos-sync-plugin/division/plugin.go b/internal/use-case/simgos-sync-plugin/division/plugin.go new file mode 100644 index 00000000..54c1403c --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/division/plugin.go @@ -0,0 +1,167 @@ +package division + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + sync "simrs-vx/internal/infra/sync-cfg" + + 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 { + 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/division", sync.O.Host, sync.O.Prefix) +} 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/patient/plugin.go b/internal/use-case/simgos-sync-plugin/patient/plugin.go new file mode 100644 index 00000000..ebfb8dc4 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/patient/plugin.go @@ -0,0 +1,207 @@ +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 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-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-plugin/unit/plugin.go b/internal/use-case/simgos-sync-plugin/unit/plugin.go new file mode 100644 index 00000000..e854d898 --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/unit/plugin.go @@ -0,0 +1,167 @@ +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" + + 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/unit", 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..62c04143 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/case.go @@ -0,0 +1,197 @@ +package division + +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/division" + esimgos "simrs-vx/internal/domain/simgos-entities/division" + 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 ( + sgData *esimgos.MUnit + syncLink *esync.DivisionLink + 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.KodeUnit, &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.UpdateDto) (*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(*input.Id, &event) + if err != nil { + return nil, err + } + + 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 { + 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(*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(uint(*input.Id), sgData.KodeUnit, &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..e180d771 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/division/helper.go @@ -0,0 +1,56 @@ +/* +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/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.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 + 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..681be276 --- /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" + esimgos "simrs-vx/internal/domain/simgos-entities/division" + esync "simrs-vx/internal/domain/sync-entities/division" + esynclog "simrs-vx/internal/domain/sync-entities/log" +) + +var now = time.Now() + +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MUnit, 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.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") + 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/main-use-case/upload/tycovar.go b/internal/use-case/simgos-sync-use-case/division/tycovar.go similarity index 78% rename from internal/use-case/main-use-case/upload/tycovar.go rename to internal/use-case/simgos-sync-use-case/division/tycovar.go index 4849e215..c7733117 100644 --- a/internal/use-case/main-use-case/upload/tycovar.go +++ b/internal/use-case/simgos-sync-use-case/division/tycovar.go @@ -6,27 +6,27 @@ 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 +package division import ( "gorm.io/gorm" - e "simrs-vx/internal/domain/main-entities/upload" + e "simrs-vx/internal/domain/main-entities/division" ) type createMw struct { Name string - Func func(input *e.CreateDto, data interface{}, tx *gorm.DB) error + Func func(input *e.CreateDto, data *e.Division, tx *gorm.DB) error } type readListMw struct { Name string - Func func(input *e.ReadListDto, data interface{}, tx *gorm.DB) error + Func func(input *e.ReadListDto, data *e.Division, tx *gorm.DB) error } type readDetailMw struct { Name string - Func func(input *e.ReadDetailDto, data interface{}, tx *gorm.DB) error + Func func(input *e.ReadDetailDto, data *e.Division, tx *gorm.DB) error } type UpdateMw = 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 6c3a79bd..edbf709d 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,26 +1,27 @@ package installation 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" + db "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/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{ @@ -31,30 +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 = 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.No_Instalasi, &event, tx.Sync) + if err != nil { + return err + } + + return nil + }) + 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) - }() + if syncLink != nil { + go func() { _ = DeleteLinkData(syncLink, &event) }() + } return nil, err } @@ -69,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, @@ -78,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 := db.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 }) @@ -105,13 +101,6 @@ 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 - ) - event := pl.Event{ Feature: "Update", Source: source, @@ -120,51 +109,21 @@ func Update(input e.UpdateDto) (*d.Data, error) { // 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() + // STEP 1: Get Link + syncLink, err := ReadDetailLinkData(*input.Id, &event) + if err != nil { 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 + 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 { + return err } - } + + return nil + }) pl.SetLogInfo(&event, nil, "complete") @@ -178,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", @@ -193,53 +147,43 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // 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 - } + // STEP 1: Get Link + 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) + // 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 = SoftDeleteSimgosData(sgData, &event, tx.Simgos) if err != nil { - return nil, err + return err } - // STEP 3: Delete M_Instalation Simgos - err = DeleteSimgosData(sgData, &event, txSimgos) + // STEP 4: Delete Link + err = DeleteLinkData(syncLink, &event, tx.Sync) if err != nil { - return nil, err + return err } - // STEP 4: Delete Installation Link - err = DeleteLinkData(syncLink, &event, txSync) - if err != nil { - txSimgos.Rollback() - return nil, err - } + isLinkDeleted = true + return nil + }) - // 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 + if err != nil { + if isLinkDeleted { go func() { - inputRollback := e.UpdateDto{ - CreateDto: e.CreateDto{ - Name: sgData.Nama_Instalasi, - }, - } - _ = UpdateSimgosData(inputRollback, syncLink, &event) + _, _ = CreateLinkData(uint(*input.Id), sgData.No_Instalasi, &event) }() - return nil, err } + + return nil, err } pl.SetLogInfo(&event, nil, "complete") 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 3e7ca854..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,9 +1,9 @@ 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" "time" dg "github.com/karincake/apem/db-gorm-pg" @@ -12,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() @@ -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/patient/case.go b/internal/use-case/simgos-sync-use-case/patient/case.go new file mode 100644 index 00000000..d403a8ac --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/case.go @@ -0,0 +1,231 @@ +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, patientData, &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 + +} + +func GenerateNomr() (*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.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 new file mode 100644 index 00000000..0bffcc9b --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/helper.go @@ -0,0 +1,317 @@ +/* +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 + data.Nomr = *input.Number + + 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..6d50f877 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/patient/lib.go @@ -0,0 +1,189 @@ +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) + + 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, patientData *esimgos.MPasien, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.Id = patientData.Id + data.CreatedAt = patientData.CreatedAt + + 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/main-use-case/upload/middleware-runner.go b/internal/use-case/simgos-sync-use-case/patient/middleware-runner.go similarity index 87% rename from internal/use-case/main-use-case/upload/middleware-runner.go rename to internal/use-case/simgos-sync-use-case/patient/middleware-runner.go index 88e3e637..648be8d7 100644 --- a/internal/use-case/main-use-case/upload/middleware-runner.go +++ b/internal/use-case/simgos-sync-use-case/patient/middleware-runner.go @@ -1,4 +1,4 @@ -package upload +package patient import ( pl "simrs-vx/pkg/logger" @@ -6,7 +6,7 @@ import ( "gorm.io/gorm" - e "simrs-vx/internal/domain/main-entities/upload" + e "simrs-vx/internal/domain/main-entities/patient" ) type middlewareRunner struct { @@ -24,7 +24,7 @@ func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data interface{}) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Patient) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -39,7 +39,7 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e return nil } -func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data interface{}) error { +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Patient) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -54,7 +54,7 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu return nil } -func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error { +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -69,7 +69,7 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) @@ -84,7 +84,7 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Patient) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) 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 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..cf76771c --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/specialist/case.go @@ -0,0 +1,197 @@ +package specialist + +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/specialist" + esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + 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 ( + sgData *esimgos.MPolihfis + syncLink *esync.SpecialistLink + 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.UpdateDto) (*d.Data, 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 + } + + tx := db.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 + } + + 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(*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(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..2c633101 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/subspecialist/case.go @@ -0,0 +1,199 @@ +package subspecialist + +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/subspecialist" + //esimgos "simrs-vx/internal/domain/simgos-entities/specialist" + 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 ( + // sgData *esimgos.MUnit + // syncLink *esync.SubspecialistLink + // 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.KodeUnit, &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 + } + + 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) { + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + //// STEP 1: Get Link + //syncLink, err := ReadDetailLinkData(*input.Id, &event) + //if err != nil { + // return nil, err + //} + // + //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 { + // 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(*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(uint(*input.Id), sgData.KodeUnit, &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 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..e17a8b22 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/case.go @@ -0,0 +1,198 @@ +package unit + +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/unit" + esimgos "simrs-vx/internal/domain/simgos-entities/unit" + 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 ( + sgData *esimgos.MPloy + syncLink *esync.UnitLink + 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.Kode, &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.UpdateDto) (*d.Data, 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 + } + + tx := db.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 + } + + 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 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 + } + + err = db.WithDualTx(func(tx *db.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 { + if isLinkDeleted { + 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/unit/helper.go b/internal/use-case/simgos-sync-use-case/unit/helper.go new file mode 100644 index 00000000..a499958c --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/helper.go @@ -0,0 +1,59 @@ +/* +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/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" +) + +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.UnitSimxLog) { + // 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.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 new file mode 100644 index 00000000..720bd413 --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/unit/lib.go @@ -0,0 +1,188 @@ +package unit + +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/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.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.UnitLink, 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.UnitLink, 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.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 + 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/main-use-case/upload/middleware.go b/internal/use-case/simgos-sync-use-case/unit/middleware.go similarity index 93% rename from internal/use-case/main-use-case/upload/middleware.go rename to internal/use-case/simgos-sync-use-case/unit/middleware.go index 63beb606..bac48f4d 100644 --- a/internal/use-case/main-use-case/upload/middleware.go +++ b/internal/use-case/simgos-sync-use-case/unit/middleware.go @@ -1,4 +1,4 @@ -package upload +package unit // example of middleware // func init() { 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 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/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 +} 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/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) +} diff --git a/pkg/pdf-helper/pdf-helper.go b/pkg/pdf-helper/pdf-helper.go new file mode 100644 index 00000000..9878ecbd --- /dev/null +++ b/pkg/pdf-helper/pdf-helper.go @@ -0,0 +1,144 @@ +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, 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) + 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) + } + + 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 + } + 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 +} 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) { 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, "/") +}