This commit is contained in:
nochill 2024-02-22 10:43:39 +07:00
commit 9837cbf7cf
25 changed files with 1888 additions and 0 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
JWT_SALT=

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.env
.vscode
tmp

0
air.yaml Normal file
View File

212
foo.txt Normal file
View File

@ -0,0 +1,212 @@
[
{
"message": "No rekam medis 000000001 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "000000001",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000001 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000001",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000002 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000002",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000003 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000003",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000004 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000004",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000005 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000005",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000006 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000006",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000007 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000007",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000008 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000008",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000009 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000009",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000010 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000010",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000011 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000011",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000012 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000012",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000013 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000013",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000014 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000014",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS no rows in result set",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000015 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000015",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
},
{
"message": "No rekam medis 00000016 sudah ada, maka no rekam medis diganti dengan 00064483",
"no_rm": "00000016",
"status": "WARNING"
},
{
"message": "Terjadi kesalahan sistem, gagal saat import pasien dengan ERROR: INSERT has more target columns than expressions (SQLSTATE 42601)",
"no_rm": "00064483",
"status": "CRITICAL"
}
]

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module git.nochill.in/nochill/excel_import_playground
go 1.21.6
require (
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/gorilla/mux v1.8.1
github.com/henvic/pgq v0.0.2
github.com/jackc/pgx/v5 v5.5.3
github.com/joho/godotenv v1.5.1
github.com/xuri/excelize/v2 v2.8.0
)
require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect
github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

94
go.sum Normal file
View File

@ -0,0 +1,94 @@
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=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/henvic/pgq v0.0.2 h1:4q/G/cW7zpxpwq672Xuh7BkcKcXonZJ6b9kR8ub3EwQ=
github.com/henvic/pgq v0.0.2/go.mod h1:1Q6dKMwtbe2glBXlusJvNZnJrvgbwub/KcfiB/7UXA4=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca h1:uvPMDVyP7PXMMioYdyPH+0O+Ta/UO1WFfNYMO3Wz0eg=
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.8.0 h1:Vd4Qy809fupgp1v7X+nCS/MioeQmYVVzi495UCTqB7U=
github.com/xuri/excelize/v2 v2.8.0/go.mod h1:6iA2edBTKxKbZAa7X5bDhcCg51xdOn1Ar5sfoXRGrQg=
github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a h1:Mw2VNrNNNjDtw68VsEj2+st+oCSn4Uz7vZw6TbhcV1o=
github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

136
internal/excel.go Normal file
View File

@ -0,0 +1,136 @@
package internal
import (
"slices"
"time"
)
type EnumObject struct {
ID int `json:"id"`
Label string `json:"label"`
}
var Agama = []EnumObject{
{ID: 1, Label: "Islam"},
{ID: 2, Label: "Kristen (Protestan)"},
{ID: 3, Label: "Katolik"},
{ID: 4, Label: "Hindu"},
{ID: 5, Label: "Buddha"},
{ID: 6, Label: "Konghucu"},
{ID: 7, Label: "Penghayat"},
{ID: 8, Label: "Lain-lain"},
}
var Pendidikan = []EnumObject{
{ID: 1, Label: "Tidak sekolah"},
{ID: 2, Label: "SD"},
{ID: 3, Label: "SLTP sederajat"},
{ID: 4, Label: "SLTA sederajat"},
{ID: 5, Label: "D1-D3 sederajat"},
{ID: 6, Label: "D4"}, // Note: There was a duplicate ID for D4 and S1
{ID: 6, Label: "S1"}, // Note: There was a duplicate ID for D4 and S1
{ID: 7, Label: "S2"},
{ID: 8, Label: "S3"},
}
var StatusKawin = []EnumObject{
{ID: 1, Label: "Belum Kawin"},
{ID: 2, Label: "Kawin"},
{ID: 3, Label: "Cerai Hidup"},
{ID: 4, Label: "Cerai Mati"},
}
var Pekerjaan = []EnumObject{
{ID: 1, Label: "Tidak bekerja"},
{ID: 2, Label: "PNS"},
{ID: 3, Label: "TNI/POLRI"},
{ID: 4, Label: "BUMN"},
{ID: 5, Label: "Pegawai Swasta/Wirausaha"},
{ID: 6, Label: "Lain-lain"},
}
// export const importDataPasienSchema = z.object({
// no_rekammedis: z.string(),
// nama_pasien: z.string(),
// no_ktp: z.string().nullish(),
// no_bpjs: z.string().nullish(),
// tgl_lahir: z.date(),
// kelamin: z.number(),
// kebangsaan: z.string().nullish(),
// agama: z.string().nullish().transform(val => Agama.find(x => x.label === val)?.id),
// suku: z.string().nullish(),
// pendidikan: z.coerce.number().nullish(),
// pekerjaan: z.string().nullish().transform(val => Pekerjaan.find(x => x.label === val)?.id),
// hp: z.coerce.string().nullish().transform(val => validatePhoneRegex(val?.replace(/\s+/g, ''))),
// email: z.string().nullish(),
// status_nikah: z.string().nullish().transform(val => StatusKawin.find(x => x.label === val)?.id),
// provinsi: z.string().nullish(),
// kabupaten: z.string().nullish(),
// kecamatan: z.string().nullish(),
// kelurahan: z.string().nullish(),
// kodepos: z.string().nullish(),
// namajalan: z.string().nullish(),
// hp_penjamin: z.coerce.string().nullish().transform(val => validatePhoneRegex(val?.replace(/\s+/g, ''))),
// namapenjamin: z.string().nullish(),
// ktp_penjamin: z.coerce.string().nullish(),
// hubungan_penjamin: z.coerce.number().nullish(),
// pendidikan_penjamin: z.string().nullish().transform(val => Pendidikan.find(x => x.label === val)?.id),
// alamat_penjamin: z.string().nullish(),
// }).transform(({
// no_rekammedis,
// nama_pasien,
// no_ktp,
// no_bpjs,
// tgl_lahir,
// kelamin,
// status_nikah,
// namajalan,
// hp,
// pendidikan_penjamin,
// ...rest
// }) => ({
// noRm: no_rekammedis,
// namaPasien: nama_pasien,
// nik: no_ktp,
// noKartuPesertaBPJS: no_bpjs,
// tanggalLahir: tgl_lahir,
// jenisKelamin: kelamin,
// statusPernikahan: status_nikah,
// pendidikanPenjamin: pendidikan_penjamin,
// noHp: hp,
// nama_jalan: namajalan,
// ...rest
// }))
type Patient struct {
NoRekamMedis string `json:"no_rekammedis"`
NamaPasien string `json:"nama_pasien"`
NoKTP *string `json:"no_ktp"` // Nullable string
NoBPJS *string `json:"no_bpjs"` // Nullable string
TglLahir time.Time `json:"tgl_lahir"`
Kelamin int `json:"kelamin"`
Kebangsaan *string `json:"kebangsaan"` // Nullable string
Agama *int `json:"agama"` // Assuming Agama is an enum, and you'll convert from string to enum ID
Suku *string `json:"suku"` // Nullable string
Pendidikan *int `json:"pendidikan"` // Nullable int
Pekerjaan *int `json:"pekerjaan"` // Assuming Pekerjaan is an enum, and you'll convert from string to enum ID
HP *string `json:"hp"` // Nullable string, assume validation happens elsewhere
Email *string `json:"email"` // Nullable string
StatusNikah *int `json:"status_nikah"` // Assuming StatusKawin is an enum, and you'll convert from string to enum ID
Provinsi *string `json:"provinsi"` // Nullable string
Kabupaten *string `json:"kabupaten"` // Nullable string
Kecamatan *string `json:"kecamatan"` // Nullable string
Kelurahan *string `json:"kelurahan"` // Nullable string
Kodepos *string `json:"kodepos"` // Nullable string
NamaJalan *string `json:"namajalan"` // Nullable string
HPPenjamin *string `json:"hp_penjamin"` // Nullable string, assume validation happens elsewhere
NamaPenjamin *string `json:"namapenjamin"` // Nullable string
KTPPenjamin *string `json:"ktp_penjamin"` // Nullable string
HubunganPenjamin *int `json:"hubungan_penjamin"` // Nullable int
PendidikanPenjamin *int `json:"pendidikan_penjamin"` // Assuming Pendidikan is an enum, and you'll convert from string to enum ID
AlamatPenjamin *string `json:"alamat_penjamin"` // Nullable string
}
func FindValueByLabel(arr []EnumObject, s string) EnumObject {
idx := slices.IndexFunc(arr, func(c EnumObject) bool { return c.Label == s })
return arr[idx]
}

63
internal/jwt.go Normal file
View File

@ -0,0 +1,63 @@
package internal
import (
"context"
"log"
"os"
"github.com/golang-jwt/jwt/v5"
)
type JwtPayload struct {
UserId float64 `json:"id"`
NamaLengkap string `json:"nama_lengkap"`
NotesId string `json:"notes_id"`
FasyankesId float64 `json:"fasyankes_id"`
Email string `json:"email"`
UserType float64 `json:"user_type"`
PPKPelayanan interface{} `json:"ppk_pelayanan"`
AlamatFasyankes interface{} `json:"alamat_fasyankes"`
}
func GetPayloadFromContext(ctx context.Context) *JwtPayload {
payload, ok := ctx.Value(UserContext).(JwtPayload)
if !ok {
return nil
}
return &payload
}
func ParseJWT(s string) JwtPayload {
salt := os.Getenv("JWT_SALT")
mySecretKey := []byte(salt)
// Parse the token
token, err := jwt.Parse(s, func(token *jwt.Token) (interface{}, error) {
return mySecretKey, nil
})
if err != nil {
log.Fatal(err)
}
claims, valid := token.Claims.(jwt.MapClaims)
if !valid && !token.Valid {
log.Fatal("Invalid token")
}
data := claims["user"].(map[string]interface{})
parsedToken := JwtPayload{
UserId: data["id"].(float64),
NamaLengkap: data["namaLengkap"].(string),
NotesId: data["notesId"].(string),
FasyankesId: data["fasyankesId"].(float64),
Email: data["email"].(string),
UserType: data["userType"].(float64),
PPKPelayanan: data["ppkPelayanan"],
AlamatFasyankes: data["alamatFasyankes"],
}
return parsedToken
}

26
internal/middleware.go Normal file
View File

@ -0,0 +1,26 @@
package internal
import (
"context"
"net/http"
"strings"
)
type contextKey string
const UserContext contextKey = "user"
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Authorization header is required", http.StatusUnauthorized)
return
}
authToken := strings.Split(authHeader, "Bearer ")[1]
ctx := context.WithValue(r.Context(), UserContext, ParseJWT(authToken))
req := r.WithContext(ctx)
next.ServeHTTP(w, req)
})
}

16
internal/parse_excel.go Normal file
View File

@ -0,0 +1,16 @@
package internal
import "reflect"
func ParseExcelValueType[T interface{}](s string) T {
var i T
t := reflect.TypeOf(i)
switch t.Kind() {
case reflect.String:
return reflect.ValueOf(s).Convert(t).Interface().(T)
}
return i
}

View File

@ -0,0 +1,53 @@
package repository
import (
"context"
"git.nochill.in/nochill/excel_import_playground/model"
"github.com/jackc/pgx/v5"
)
func (q *Queries) CreateAlamat(ctx context.Context, arg model.Alamat) (model.Alamat, error) {
createAlamatQuery := `
INSERT INTO "Alamat" (
kategori,
negara,
provinsi,
kabupaten,
kecamatan,
kelurahan,
kode_pos,
nama_jalan,
rt,
rw,
no_hp,
no_telpon,
pasen_id,
status,
created_at,
updated_at,
) VALUES ($1,, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
RETURNING *
`
row, _ := q.db.Query(ctx, createAlamatQuery,
arg.Kategori,
arg.Negara,
arg.Provinsi,
arg.Kabupaten,
arg.Kecamatan,
arg.Kelurahan,
arg.Kode_pos,
arg.Nama_jalan,
arg.Rt,
arg.Rw,
arg.No_hp,
arg.No_telpon,
arg.Pasien_id,
arg.Status,
)
result, err := pgx.CollectExactlyOneRow[model.Alamat](row, pgx.RowToStructByName[model.Alamat])
return result, err
}

55
internal/repository/db.go Normal file
View File

@ -0,0 +1,55 @@
package repository
import (
"context"
"git.nochill.in/nochill/excel_import_playground/model"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)
type Querier interface {
FindLastPatientInCertainFasyankes(ctx context.Context, fasyankes_id int32) (model.Patient, error)
FindPatientByNoRm(ctx context.Context, noRm string) (model.Patient, error)
FindPatientByNik(ctx context.Context, nik string) (model.Patient, error)
FindPatientByBPJSCode(ctx context.Context, BPJSCode string) (model.Patient, error)
UpdatePatientNik(ctx context.Context, nik *string, patientId int32) error
UpdatePatient(ctx context.Context, arg UpdatePatientParams, patientId int32) (model.Patient, error)
// UpdatePatient(ctx context.Context, patientId int32, arg UpdatePatientParams) (model.Patient, error)
GenerateNoRm(ctx context.Context, fasyankesId int32) (string, error)
CreateKeluarga(ctx context.Context, arg model.Keluarga) (model.Keluarga, error)
CreateAlamat(ctx context.Context, arg model.Alamat) (model.Alamat, error)
FindWilayahByName(ctx context.Context, name string) (model.Wilayah, error)
}
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
type Queries struct {
db DBTX
}
type UpdateableField[T any] struct {
IsFilled bool `default:"false"`
Value T `default:"nil"`
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
func NewTx(db DBTX) *Queries {
return &Queries{db: db}
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}
var DbPool *pgxpool.Pool

View File

@ -0,0 +1,46 @@
package repository
import (
"context"
"git.nochill.in/nochill/excel_import_playground/model"
"github.com/jackc/pgx/v5"
)
func (q *Queries) CreateKeluarga(ctx context.Context, arg model.Keluarga) (model.Keluarga, error) {
createKeluargaQuery := `
INSERT INTO "Keluarga"(
pasien_id,
nama,
hubungan,
no_identitas,
no_hp,
kota_lahir,
alamat,
tanggal_lahir,
jenis_kelamin,
pendidikan,
pekerjaan,
email,
) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
`
row, _ := q.db.Query(ctx, createKeluargaQuery,
arg.PasienId,
arg.Nama,
arg.Hubungan,
arg.NoIdentitas,
arg.NoHp,
arg.KotaLahir,
arg.Alamat,
arg.TanggalLahir,
arg.JenisKelamin,
arg.Pendidikan,
arg.Pekerjaan,
arg.Email,
)
result, err := pgx.CollectExactlyOneRow[model.Keluarga](row, pgx.RowToStructByName[model.Keluarga])
return result, err
}

View File

@ -0,0 +1,485 @@
package repository
import (
"context"
"strings"
"time"
"git.nochill.in/nochill/excel_import_playground/model"
"git.nochill.in/nochill/excel_import_playground/util"
"github.com/henvic/pgq"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
)
func (q *Queries) FindLastPatientInCertainFasyankes(ctx context.Context, fasyankes_id int32) (model.Patient, error) {
findLastPasienQuery := `
SELECT * FROM "Pasien" p
WHERE fasyankes_id = $1
ORDER BY p.id DESC
LIMIT(1)
`
row, _ := q.db.Query(ctx, findLastPasienQuery, fasyankes_id)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
func (q *Queries) FindPatientByBPJSCode(ctx context.Context, BPJSCode string) (model.Patient, error) {
findPatientByBPJSCode := `
SELECT * FROM "Pasien" p
WHERE no_kartu_peserta_bpjs = $1
`
row, _ := q.db.Query(ctx, findPatientByBPJSCode, BPJSCode)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
func (q *Queries) FindPatientByNoRm(ctx context.Context, noRm string) (model.Patient, error) {
findPasienByNoRmQuery := `
SELECT * FROM "Pasien" p
WHERE no_rm = $1
`
row, _ := q.db.Query(ctx, findPasienByNoRmQuery, noRm)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
func (q *Queries) FindPatientByNik(ctx context.Context, nik string) (model.Patient, error) {
findPasienByNikQuery := `
SELECT * FROM "Pasien" p
WHERE nik = $1
`
row, _ := q.db.Query(ctx, findPasienByNikQuery, nik)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
type UpdatePatientParams struct {
NoRm UpdateableField[string] `json:"no_rm" db:"no_rm"`
NamaPasien UpdateableField[string] `json:"nama_pasien" db:"nama_pasien"`
JenisIdentitas UpdateableField[pgtype.Int4] `json:"jenis_identitas" db:"jenis_identitas"`
NoIdentitas UpdateableField[pgtype.Text] `json:"no_identitas" db:"no_identitas"`
FotoProfil UpdateableField[pgtype.Text] `json:"foto_profil" db:"foto_profil"`
FotoKtp UpdateableField[pgtype.Text] `json:"foto_ktp" db:"foto_ktp"`
KotaLahir UpdateableField[pgtype.Int4] `json:"kota_lahir" db:"kota_lahir"`
TanggalLahir UpdateableField[time.Time] `json:"tanggal_lahir" db:"tanggal_lahir"`
JenisKelamin UpdateableField[pgtype.Int4] `json:"jenis_kelamin" db:"jenis_kelamin"`
Suku UpdateableField[pgtype.Int4] `json:"suku" db:"suku"`
Agama UpdateableField[pgtype.Int4] `json:"agama" db:"agama"`
Kebangsaan UpdateableField[pgtype.Int4] `json:"kebangsaan" db:"kebangsaan"`
Bahasa UpdateableField[pgtype.Int4] `json:"bahasa" db:"bahasa"`
Pendidikan UpdateableField[pgtype.Int4] `json:"pendidikan" db:"pendidikan"`
StatusPerkawinan UpdateableField[pgtype.Int4] `json:"status_perkawinan" db:"status_perkawinan"`
Email UpdateableField[pgtype.Text] `json:"email" db:"email"`
Pekerjaan UpdateableField[pgtype.Int4] `json:"pekerjaan" db:"pekerjaan"`
NoHp UpdateableField[pgtype.Text] `json:"no_hp" db:"no_hp"`
IsDeleted UpdateableField[bool] `json:"is_deleted" db:"is_deleted"`
FasyankesID UpdateableField[int] `json:"fasyankes_id" db:"fasyankes_id"`
Nik UpdateableField[pgtype.Text] `json:"nik" db:"nik"`
NoKk UpdateableField[pgtype.Text] `json:"no_kk" db:"no_kk"`
NoKartuPesertaBPJS UpdateableField[pgtype.Text] `json:"no_kartu_peserta_bpjs" db:"no_kartu_peserta_bpjs"`
UpdateBy int `json:"update_by" db:"update_by"`
}
func (q *Queries) UpdatePatient(ctx context.Context, arg UpdatePatientParams, patientId int32) (model.Patient, error) {
builder := pgq.Update("\"Pasien\"").Set("update_by", arg.UpdateBy).Set("updated_at", time.Now().UTC())
if arg.NoRm.IsFilled {
builder.Set("no_rm", arg.NoRm.Value)
}
if arg.NamaPasien.IsFilled {
builder = builder.Set("nama_pasien", arg.NamaPasien.Value)
}
if arg.JenisIdentitas.IsFilled {
builder = builder.Set("jenis_identitas", arg.JenisIdentitas.Value)
}
if arg.NoIdentitas.IsFilled {
builder = builder.Set("no_identitas", arg.NoIdentitas.Value)
}
if arg.FotoProfil.IsFilled {
builder = builder.Set("foto_profil", arg.FotoProfil.Value)
}
if arg.FotoKtp.IsFilled {
builder = builder.Set("foto_ktp", arg.FotoKtp.Value)
}
if arg.KotaLahir.IsFilled {
builder = builder.Set("kota_lahir", arg.KotaLahir.Value)
}
if arg.TanggalLahir.IsFilled {
builder = builder.Set("tanggal_lahir", arg.TanggalLahir.Value)
}
if arg.JenisKelamin.IsFilled {
builder = builder.Set("jenis_kelamin", arg.JenisKelamin.Value)
}
if arg.Suku.IsFilled {
builder = builder.Set("suku", arg.Suku.Value)
}
if arg.Agama.IsFilled {
builder = builder.Set("agama", arg.Agama.Value)
}
if arg.Kebangsaan.IsFilled {
builder = builder.Set("kebangsaan", arg.Kebangsaan.Value)
}
if arg.Bahasa.IsFilled {
builder = builder.Set("bahasa", arg.Bahasa.Value)
}
if arg.Pendidikan.IsFilled {
builder = builder.Set("pendidikan", arg.Pendidikan.Value)
}
if arg.StatusPerkawinan.IsFilled {
builder = builder.Set("status_perkawinan", arg.StatusPerkawinan.Value)
}
if arg.Email.IsFilled {
builder = builder.Set("email", arg.Email.Value)
}
if arg.Pekerjaan.IsFilled {
builder = builder.Set("pekerjaan", arg.Pekerjaan.Value)
}
if arg.NoHp.IsFilled {
builder = builder.Set("no_hp", arg.NoHp.Value)
}
if arg.IsDeleted.IsFilled {
builder = builder.Set("is_deleted", arg.IsDeleted.Value)
}
if arg.FasyankesID.IsFilled {
builder = builder.Set("fasyankes_id", arg.FasyankesID.Value)
}
if arg.Nik.IsFilled {
builder = builder.Set("nik", arg.Nik.Value)
}
if arg.NoKk.IsFilled {
builder = builder.Set("no_kk", arg.NoKk.Value)
}
if arg.NoKartuPesertaBPJS.IsFilled {
builder = builder.Set("no_kartu_peserta_bpjs", arg.NoKartuPesertaBPJS.Value)
}
query, args, err := builder.Where(pgq.Eq{"id": patientId}).Suffix("RETURNING *").SQL()
if err != nil {
return model.Patient{}, err
}
row, _ := q.db.Query(ctx, query, args...)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
func (q *Queries) UpdatePatientNik(ctx context.Context, nik *string, patientId int32) error {
updatePatientQuery := `
UPDATE "Pasien" p
SET nik = $1
WHERE id = $2
`
_, err := q.db.Exec(ctx, updatePatientQuery, nik, patientId)
return err
}
// type UpdatePatientParams struct {
// NoRm string `json:"no_rm" db:"no_rm"`
// NamaPasien string `json:"nama_pasien" db:"nama_pasien"`
// JenisIdentitas pgtype.Int4 `json:"jenis_identitas" db:"jenis_identitas"`
// NoIdentitas pgtype.Text `json:"no_identitas" db:"no_identitas"`
// FotoProfil pgtype.Text `json:"foto_profil" db:"foto_profil"`
// FotoKtp pgtype.Text `json:"foto_ktp" db:"foto_ktp"`
// KotaLahir pgtype.Int4 `json:"kota_lahir" db:"kota_lahir"`
// TanggalLahir *time.Time `json:"tanggal_lahir" db:"tanggal_lahir"`
// JenisKelamin pgtype.Int4 `json:"jenis_kelamin" db:"jenis_kelamin"`
// Suku pgtype.Int4 `json:"suku" db:"suku"`
// Agama pgtype.Int4 `json:"agama" db:"agama"`
// Kebangsaan pgtype.Int4 `json:"kebangsaan" db:"kebangsaan"`
// Bahasa pgtype.Int4 `json:"bahasa" db:"bahasa"`
// Pendidikan pgtype.Int4 `json:"pendidikan" db:"pendidikan"`
// StatusPerkawinan pgtype.Int4 `json:"status_perkawinan" db:"status_perkawinan"`
// Email pgtype.Text `json:"email" db:"email"`
// Pekerjaan pgtype.Int4 `json:"pekerjaan" db:"pekerjaan"`
// NoHp pgtype.Text `json:"no_hp" db:"no_hp"`
// IsDeleted *bool `json:"is_deleted" db:"is_deleted"`
// FasyankesID int `json:"fasyankes_id" db:"fasyankes_id"`
// Nik pgtype.Text `json:"nik" db:"nik"`
// NoKk pgtype.Text `json:"no_kk" db:"no_kk"`
// NoKartuPesertaBPJS pgtype.Text `json:"no_kartu_peserta_bpjs" db:"no_kartu_peserta_bpjs"`
// UpdateBy int `json:"update_by" db:"update_by"`
// UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
// }
// func (q *Queries) UpdatePatient(ctx context.Context, patientId int32, arg UpdatePatientParams) (model.Patient, error) {
// updatePatientQuery := `
// UPDATE "Pasien" p
// SET
// no_rm = $1,
// nama_pasien = $2,
// jenis_identitas = $3,
// no_identitas = $4,
// foto_profil = $5,
// foto_ktp = $6,
// kota_lahir = $7,
// tanggal_lahir = $8,
// jenis_kelamin = $9,
// suku = $10,
// agama = $11,
// kebangsaan = $12,
// bahasa = $13,
// pendidikan = $14,
// status_perkawinan = $15,
// email = $16,
// pekerjaan = $17,
// no_hp = $18,
// is_deleted = $19,
// fasyankes_id = $20,
// nik = $21,
// no_kk = $22,
// no_kartu_peserta_bpjs = $23,
// update_by = $24,
// updated_at = $25
// WHERE id = $26
// `
// row, _ := q.db.Query(ctx, updatePatientQuery,
// arg.NoRm,
// arg.NamaPasien,
// arg.JenisIdentitas,
// arg.NoIdentitas,
// arg.FotoProfil,
// arg.FotoKtp,
// arg.KotaLahir,
// arg.TanggalLahir,
// arg.JenisKelamin,
// arg.Suku,
// arg.Agama,
// arg.Kebangsaan,
// arg.Bahasa,
// arg.Pendidikan,
// arg.StatusPerkawinan,
// arg.Email,
// arg.Pekerjaan,
// arg.NoHp,
// arg.IsDeleted,
// arg.FasyankesID,
// arg.Nik,
// arg.NoKk,
// arg.NoKartuPesertaBPJS,
// arg.UpdateBy,
// arg.UpdatedAt,
// patientId,
// )
// result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
// return result, err
// }
func (q *Queries) CreatePatient(ctx context.Context, arg model.Patient) (model.Patient, error) {
createPasienQuery := `
INSERT INTO "Pasien"(
no_rm,
nama_pasien,
jenis_identitas,
no_identitas,
foto_profil,
foto_ktp,
kota_lahir,
tanggal_lahir,
jenis_kelamin,
suku,
agama,
kebangsaan,
bahasa,
pendidikan,
status_perkawinan,
email,
pekerjaan,
no_hp,
is_deleted,
fasyankes_id,
nik,
no_kk,
no_kartu_peserta_bpjs,
create_by,
update_by
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
RETURNING *;
`
row, _ := q.db.Query(ctx, createPasienQuery,
arg.NoRm,
arg.NamaPasien,
arg.JenisIdentitas,
arg.NoIdentitas,
arg.FotoProfil,
arg.FotoKtp,
arg.KotaLahir,
arg.TanggalLahir,
arg.JenisKelamin,
arg.Suku,
arg.Agama,
arg.Kebangsaan,
arg.Bahasa,
arg.Pendidikan,
arg.StatusPerkawinan,
arg.Email,
arg.Pekerjaan,
arg.NoHp,
arg.IsDeleted,
arg.FasyankesID,
arg.Nik,
arg.NoKk,
arg.NoKartuPesertaBPJS,
arg.CreateBy,
arg.UpdateBy,
arg.CreatedAt,
arg.UpdatedAt,
)
result, err := pgx.CollectExactlyOneRow[model.Patient](row, pgx.RowToStructByName[model.Patient])
return result, err
}
type ImportDataPasienParams struct {
NoRekamMedis string
NamaPasien string
Nik pgtype.Text
NoBPJS pgtype.Text
TpLahir pgtype.Text
TglLahir time.Time
Kelamin int8
Kebangsaan *int8
Agama *int8
Suku *int32
Pendidikan *int8
Pekerjaan *int8
Hp pgtype.Text
Email pgtype.Text
StatusNikah *int8
Provinsi *string
Kabupaten *int32
Kecamatan *int32
Kelurahan *int32
Kodepos *int32
NamaJalan pgtype.Text
HpPenjamin pgtype.Text
NamaPenjamin pgtype.Text
KtpPenjamin pgtype.Text
HubunganPenjamin *int8
PendidikanPenjamin *int8
AlamatPenjamin pgtype.Text
}
func (store *SQLStore) ImportPatientTx(ctx context.Context, params ImportDataPasienParams) error {
err := store.execTx(ctx, func(q *Queries) error {
var err error
tempNilInt := int8(util.NilIntVal)
if params.Pendidikan == nil {
params.Pendidikan = &tempNilInt
}
if params.Agama == nil {
params.Agama = &tempNilInt
}
if params.Pekerjaan == nil {
params.Pekerjaan = &tempNilInt
}
if params.StatusNikah == nil {
params.StatusNikah = &tempNilInt
}
patient, err := q.CreatePatient(ctx, model.Patient{
NoRm: params.NoRekamMedis,
NamaPasien: params.NamaPasien,
Pendidikan: pgtype.Int4{Valid: params.Pendidikan != nil, Int32: int32(*params.Pendidikan)},
TanggalLahir: params.TglLahir,
Nik: params.Nik,
Agama: pgtype.Int4{Valid: params.Agama != nil, Int32: int32(*params.Agama)},
NoHp: params.Hp,
Pekerjaan: pgtype.Int4{Valid: params.Pekerjaan != nil, Int32: int32(*params.Agama)},
Email: params.Email,
NoKartuPesertaBPJS: params.NoBPJS,
JenisKelamin: params.Kelamin,
StatusPerkawinan: pgtype.Int4{Valid: params.StatusNikah != nil, Int32: int32(*params.StatusNikah)},
IsDeleted: false,
})
if err != nil {
return err
}
if params.NamaJalan.Valid {
var provinsi_id *int32
provinsi, err := q.FindWilayahByName(ctx, *params.Provinsi)
if err != nil {
return err
}
if provinsi.Id == 0 {
provinsi_id = nil
} else {
provinsi_id = &provinsi.Id
}
_, err = q.CreateAlamat(ctx, model.Alamat{
Provinsi: provinsi_id,
Nama_jalan: &params.NamaJalan.String,
Pasien_id: int32(patient.ID),
})
if err != nil {
return err
}
}
if params.HubunganPenjamin != nil && params.NamaPenjamin.Valid {
_, err = q.CreateKeluarga(ctx, model.Keluarga{
Id: int32(patient.ID),
Nama: &params.NamaPenjamin.String,
Hubungan: params.HubunganPenjamin,
})
if err != nil {
return err
}
}
return err
})
return err
}
func (q *Queries) GenerateNoRm(ctx context.Context, fasyankesId int32) (string, error) {
lastPasien, err := q.FindLastPatientInCertainFasyankes(ctx, fasyankesId)
if err != nil {
return "", err
}
return genNoRm(&lastPasien.NoRm), nil
}
func genNoRm(lastNoRm *string) string {
var tempNoRm string
if lastNoRm != nil {
tempNoRm = *lastNoRm
} else {
tempNoRm = "1"
}
length := strings.IndexFunc(tempNoRm, func(r rune) bool {
return r != '0'
})
return util.Pad(tempNoRm, int32(length))
}
// func FindPatientBy

View File

@ -0,0 +1,43 @@
package repository
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
)
type Store interface {
Querier
ImportPatientTx(ctx context.Context, params ImportDataPasienParams) error
}
type SQLStore struct {
*Queries
pool *pgxpool.Pool
}
func NewStore(pool *pgxpool.Pool) Store {
return &SQLStore{
pool: pool,
Queries: New(pool),
}
}
// TRANSACTION QUERY FUNCTION
func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
tx, err := store.pool.Begin(ctx)
if err != nil {
return err
}
q := NewTx(tx)
err = fn(q)
if err != nil {
if rbErr := tx.Rollback(ctx); rbErr != nil {
return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr)
}
return err
}
return tx.Commit(ctx)
}

View File

@ -0,0 +1,20 @@
package repository
import (
"context"
"git.nochill.in/nochill/excel_import_playground/model"
"github.com/jackc/pgx/v5"
)
func (q *Queries) FindWilayahByName(ctx context.Context, name string) (model.Wilayah, error) {
findWilayaByNameQuery := `
SELECT * FROM "Wilayah"
WHERE NamaWilayah = $1
`
row, _ := q.db.Query(ctx, findWilayaByNameQuery, name)
result, err := pgx.CollectExactlyOneRow[model.Wilayah](row, pgx.RowToStructByName[model.Wilayah])
return result, err
}

333
internal/rest/handler.go Normal file
View File

@ -0,0 +1,333 @@
package rest
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
"git.nochill.in/nochill/excel_import_playground/internal"
"git.nochill.in/nochill/excel_import_playground/internal/repository"
"git.nochill.in/nochill/excel_import_playground/util"
"github.com/gorilla/mux"
"github.com/jackc/pgx/v5/pgtype"
"github.com/xuri/excelize/v2"
)
func (s *Server) ImportPatientHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
currentUser := internal.GetPayloadFromContext(r.Context())
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "Invalid file", http.StatusBadRequest)
return
}
if !strings.HasSuffix(header.Filename, ".xlsx") && !strings.HasSuffix(header.Filename, ".xls") {
http.Error(w, "File is not an .xlsx or xls file", http.StatusBadRequest)
return
}
if header.Size > 30<<20 {
http.Error(w, "The uploaded file is too large.", http.StatusBadRequest)
return
}
buffer := make([]byte, 512) // Create a buffer to store the file header
if _, err = file.Read(buffer); err != nil {
http.Error(w, "Could not read file", http.StatusInternalServerError)
return
}
// contentType := http.DetectContentType(buffer)
// Reset the read pointer of the file
if _, err = file.Seek(0, 0); err != nil {
http.Error(w, "Could not read file", http.StatusInternalServerError)
return
}
f, err := excelize.OpenReader(file)
if err != nil {
http.Error(w, "Error opening Excel file", http.StatusInternalServerError)
return
}
defer f.Close()
rows, err := f.GetRows("import data pasien")
if err != nil {
json.NewEncoder(w).Encode(err)
return
}
// _, err = repository.Store.FindLastPatientInCertainFasyankes(s.Store, r.Context(), 2)
if err != nil {
w.WriteHeader(500)
log.Println(err)
json.NewEncoder(w).Encode(err)
return
}
// tempArr := make([]repository.ImportDataPasienParams, len(rows)-1)
var errorMsg []interface{}
for idx, row := range rows {
if idx >= 1 {
if row[0] == "" || row[1] == "" {
errorMsg = append(errorMsg, map[string]any{
"baris": idx + 1,
"status": "CRITICAL",
"error_message": "kolom A 'no_rekam medis' dan kolom B 'nama_pasien' wajib diisi",
})
continue
}
tanggalLahir, err := time.Parse(util.TIME_PARSE_LAYOUT, row[5])
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"baris": idx + 1,
"status": "CRITICAL",
"error_message": "tanggal lahir wajib diisi",
})
continue
}
kelamin, err := strconv.Atoi(row[6])
if err != nil {
log.Printf("row 6: %s, err: %v", row[6], err)
continue
}
value := repository.ImportDataPasienParams{
NoRekamMedis: row[0],
NamaPasien: row[1],
Nik: pgtype.Text{String: row[2], Valid: len(row[2]) > 0},
NoBPJS: pgtype.Text{String: row[3], Valid: len(row[3]) > 0},
TpLahir: pgtype.Text{String: row[4], Valid: len(row[4]) > 0},
TglLahir: tanggalLahir,
Kelamin: int8(kelamin),
Kebangsaan: util.StringToIntPtr[int8](row[6]),
Agama: util.StringToIntPtr[int8](row[7]),
Suku: util.StringToIntPtr[int32](row[8]),
Pendidikan: util.StringToIntPtr[int8](row[9]),
Pekerjaan: util.StringToIntPtr[int8](row[10]),
Hp: pgtype.Text{String: row[11], Valid: len(row[11]) > 0},
Email: pgtype.Text{String: row[12], Valid: len(row[12]) > 0},
StatusNikah: util.StringToIntPtr[int8](row[13]),
Provinsi: util.StringToStringPtr(row[14]),
Kabupaten: util.StringToIntPtr[int32](row[15]),
Kecamatan: util.StringToIntPtr[int32](row[16]),
Kelurahan: util.StringToIntPtr[int32](row[17]),
Kodepos: util.StringToIntPtr[int32](row[18]),
NamaJalan: pgtype.Text{String: row[19], Valid: len(row[19]) > 0},
HpPenjamin: pgtype.Text{String: row[20], Valid: len(row[20]) > 0},
NamaPenjamin: pgtype.Text{String: row[21], Valid: len(row[21]) > 0},
KtpPenjamin: pgtype.Text{String: row[22], Valid: len(row[22]) > 0},
HubunganPenjamin: util.StringToIntPtr[int8](row[23]),
PendidikanPenjamin: util.StringToIntPtr[int8](row[24]),
AlamatPenjamin: pgtype.Text{String: row[25], Valid: len(row[25]) > 0},
}
patientExist, err := repository.Store.FindPatientByNoRm(s.Store, r.Context(), value.NoRekamMedis)
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, mencari data pasien %v", err),
})
continue
}
if patientExist.NoRm != "" {
if value.NoRekamMedis == patientExist.NoRm {
updatedNoRekamMedis, err := repository.Store.GenerateNoRm(s.Store, r.Context(), int32(currentUser.FasyankesId))
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal generate NoRM %v", err),
})
continue
}
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "WARNING",
"message": fmt.Sprintf("No rekam medis %s sudah ada, maka no rekam medis diganti dengan %s", value.NoRekamMedis, updatedNoRekamMedis),
})
value.NoRekamMedis = updatedNoRekamMedis
}
}
if value.Nik.Valid {
patient, err := repository.Store.FindPatientByNik(s.Store, r.Context(), value.Nik.String)
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal mendapatakan data pasien %v", err),
})
}
if value.Nik.String == patient.Nik.String {
if patient.TanggalLahir.Before(value.TglLahir) {
value.Nik.Valid = false
} else {
_, err := repository.Store.UpdatePatient(s.Store, r.Context(), repository.UpdatePatientParams{
Nik: repository.UpdateableField[pgtype.Text]{IsFilled: true, Value: pgtype.Text{Valid: false, String: ""}},
}, int32(patient.ID))
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal mendapatakan update pasien lama karena NIK sama %v", err),
})
continue
}
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "WARNING",
"message": fmt.Sprintf("NIK kembar dengan pasien: %s, mohon dicek kembali current user: %v", patient.NoRm, value),
})
}
}
}
if value.NoBPJS.Valid {
patient, err := repository.Store.FindPatientByBPJSCode(s.Store, r.Context(), value.NoBPJS.String)
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal mendapatakan data pasien dengan BPJS %v", err),
})
}
if patient.ID != 0 {
if patient.TanggalLahir.Before(value.TglLahir) {
value.NoBPJS = pgtype.Text{Valid: false, String: ""}
} else {
_, err := repository.Store.UpdatePatient(
s.Store,
r.Context(),
repository.UpdatePatientParams{
NoKartuPesertaBPJS: repository.UpdateableField[pgtype.Text]{IsFilled: true, Value: pgtype.Text{Valid: false, String: ""}},
},
int32(patient.ID),
)
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal mendapatakan update pasien dengan BPJS %v", err),
})
}
continue
}
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "WARNING",
"message": fmt.Sprintf("No KartuBPJS ada yang sama dengan pasien: %s, mohon dicek kembali. current user: %v", patient.NoRm, value),
})
}
}
err = repository.Store.ImportPatientTx(s.Store, r.Context(), value)
if err != nil {
errorMsg = append(errorMsg, map[string]any{
"no_rm": value.NoRekamMedis,
"status": "CRITICAL",
"message": fmt.Sprintf("Terjadi kesalahan sistem, gagal saat import pasien dengan %v", err),
})
continue
}
}
}
if len(errorMsg) > 0 {
jsonMarshal, _ := json.MarshalIndent(errorMsg, "", " ")
if err := os.WriteFile("foo.txt", []byte(jsonMarshal), 0666); err != nil {
log.Fatal(err)
}
}
a := map[string]any{
"halo": "halo",
// "contentType": contentType,
// "patient": tempArr,
"errorData": errorMsg,
}
json.NewEncoder(w).Encode(a)
}
func validateFile(r *http.Request) error {
return nil
}
type updatePatientParams struct {
NamaPasien string `json:"patient_name"`
}
func (s *Server) UpdatePatient(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var req updatePatientParams
idParams, ok := vars["id"]
if !ok {
fmt.Println("id is missing in parameters")
}
id, err := strconv.Atoi(idParams)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(err)
return
}
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
updatedUser, err := repository.Store.UpdatePatient(s.Store, r.Context(), repository.UpdatePatientParams{
NamaPasien: repository.UpdateableField[string]{IsFilled: true, Value: req.NamaPasien},
}, int32(id))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(updatedUser)
}

15
internal/rest/server.go Normal file
View File

@ -0,0 +1,15 @@
package rest
import "git.nochill.in/nochill/excel_import_playground/internal/repository"
type Server struct {
Store repository.Store
}
func NewServer(store repository.Store) (*Server, error) {
server := &Server{
Store: store,
}
return server, nil
}

115
main.go Normal file
View File

@ -0,0 +1,115 @@
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"time"
"git.nochill.in/nochill/excel_import_playground/internal"
"git.nochill.in/nochill/excel_import_playground/internal/repository"
"git.nochill.in/nochill/excel_import_playground/internal/rest"
"github.com/gorilla/mux"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/joho/godotenv"
)
func main() {
runtime.GOMAXPROCS(5)
var wait time.Duration
flag.DurationVar(&wait, "graceful-timeout", time.Second*15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
flag.Parse()
err := godotenv.Load()
if err != nil {
log.Fatal(err.Error())
}
_ = os.Getenv("JWT_SALT")
DATABASE_URL := os.Getenv("DATABASE_URL")
APP_HOST := os.Getenv("APP_HOST")
poolConfig, err := pgxpool.ParseConfig(DATABASE_URL)
if err != nil {
log.Fatal("ENV DATABASE_URL NOT FOUND", err)
}
dbConn, err := pgxpool.NewWithConfig(context.Background(), poolConfig)
if err != nil {
log.Fatal("cannot connect to db: ", err)
}
store := repository.NewStore(dbConn)
server, err := rest.NewServer(store)
if err != nil {
log.Fatal("Somethng wrong while try to start Server", err)
}
r := mux.NewRouter()
r.Use(internal.AuthMiddleware)
r.HandleFunc("/", homeRoute).Methods("GET")
r.HandleFunc("/import-pasien", server.ImportPatientHandler).Methods("POST")
r.HandleFunc("/update-pasien/{id}", server.UpdatePatient).Methods("PATCH")
// r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions)
// r.Use(mux.CORSMethodMiddleware(r))
// val := internal.FindValueByLabel("PNS")
// fmt.Println(val.ID)
srv := &http.Server{
Handler: r,
Addr: APP_HOST,
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go func() {
host := fmt.Sprintf("Server running on %s", APP_HOST)
fmt.Println(host)
if err := srv.ListenAndServe(); err != nil {
log.Println(err)
}
}()
c := make(chan os.Signal, 1)
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
signal.Notify(c, os.Interrupt)
// Block until we receive our signal.
<-c
// Create a deadline to wait for.
ctx, cancel := context.WithTimeout(context.Background(), wait)
defer repository.DbPool.Close()
defer cancel()
// Doesn't block if no connections, but will otherwise wait
// until the timeout deadline.
srv.Shutdown(ctx)
// Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation.
log.Println("shutting down")
os.Exit(0)
}
func homeRoute(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res := map[string]any{
"halo": "halo",
}
json.NewEncoder(w).Encode(res)
}

24
model/alamat.go Normal file
View File

@ -0,0 +1,24 @@
package model
import "time"
type Alamat struct {
Id *int32 `json:"id,omitempty"`
Kategori *int32 `json:"kategori,omitempty"`
Negara *int32 `json:"negara,omitempty"`
Provinsi *int32 `json:"provinsi,omitempty"`
Kabupaten *int32 `json:"kabupaten,omitempty"`
Kecamatan *int32 `json:"kecamatan,omitempty"`
Kelurahan *int32 `json:"kelurahan,omitempty"`
Kode_pos *string `json:"kode_pos,omitempty"`
Nama_jalan *string `json:"nama_jalan,omitempty"`
Rt *string `json:"rt,omitempty"`
Rw *string `json:"rw,omitempty"`
No_hp *string `json:"no_hp,omitempty"`
No_telpon *string `json:"no_telpon,omitempty"`
Pasien_id int32 `json:"pasen_id"`
Status *bool `json:"status,omitempty"`
Created_at time.Time `json:"created_at"`
Updated_at time.Time `json:"updated_at"`
Is_deleted *bool `json:"is_deleted,omitempty"`
}

1
model/db.go Normal file
View File

@ -0,0 +1 @@
package model

19
model/keluarga.go Normal file
View File

@ -0,0 +1,19 @@
package model
import "time"
type Keluarga struct {
Id int32 `json:"id"`
PasienId int32 `json:"pasien_id"`
Nama *string `json:"nama,omitempty"`
Hubungan *int8 `json:"hubungan,omitempty"`
NoIdentitas *string `json:"no_identitas,omitempty"`
NoHp *string `json:"no_hp,omitempty"`
KotaLahir *int32 `json:"kota_lahir,omitempty"`
Alamat *string `json:"alamat,omitempty"`
TanggalLahir time.Time `json:"tanggal_lahir,omitempty"`
JenisKelamin *int32 `json:"jenis_kelamin,omitempty"`
Pendidikan *int32 `json:"pendidikan,omitempty"`
Pekerjaan *int32 `json:"pekerjaan,omitempty"`
Email *string `json:"email,omitempty"`
}

38
model/patient.go Normal file
View File

@ -0,0 +1,38 @@
package model
import (
"time"
"github.com/jackc/pgx/v5/pgtype"
)
type Patient struct {
ID int `json:"id" db:"id"`
NoRm string `json:"no_rm" db:"no_rm"`
NamaPasien string `json:"nama_pasien" db:"nama_pasien"`
JenisIdentitas pgtype.Int4 `json:"jenis_identitas" db:"jenis_identitas"`
NoIdentitas pgtype.Text `json:"no_identitas" db:"no_identitas"`
FotoProfil pgtype.Text `json:"foto_profil" db:"foto_profil"`
FotoKtp pgtype.Text `json:"foto_ktp" db:"foto_ktp"`
KotaLahir pgtype.Int4 `json:"kota_lahir" db:"kota_lahir"`
TanggalLahir time.Time `json:"tanggal_lahir" db:"tanggal_lahir"`
JenisKelamin int8 `json:"jenis_kelamin" db:"jenis_kelamin"`
Suku pgtype.Int4 `json:"suku" db:"suku"`
Agama pgtype.Int4 `json:"agama" db:"agama"`
Kebangsaan pgtype.Int4 `json:"kebangsaan" db:"kebangsaan"`
Bahasa pgtype.Int4 `json:"bahasa" db:"bahasa"`
Pendidikan pgtype.Int4 `json:"pendidikan" db:"pendidikan"`
StatusPerkawinan pgtype.Int4 `json:"status_perkawinan" db:"status_perkawinan"`
Email pgtype.Text `json:"email" db:"email"`
Pekerjaan pgtype.Int4 `json:"pekerjaan" db:"pekerjaan"`
NoHp pgtype.Text `json:"no_hp" db:"no_hp"`
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
FasyankesID int `json:"fasyankes_id" db:"fasyankes_id"`
Nik pgtype.Text `json:"nik" db:"nik"`
NoKk pgtype.Text `json:"no_kk" db:"no_kk"`
NoKartuPesertaBPJS pgtype.Text `json:"no_kartu_peserta_bpjs" db:"no_kartu_peserta_bpjs"`
CreateBy int `json:"create_by" db:"create_by"`
UpdateBy int `json:"update_by" db:"update_by"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

9
model/wilayah.go Normal file
View File

@ -0,0 +1,9 @@
package model
type Wilayah struct {
Id int32
KodeWilayah string
NamaWilayah string
KodeBPJS *string
NamaBPJS *string
}

53
util/common.go Normal file
View File

@ -0,0 +1,53 @@
package util
import (
"fmt"
"reflect"
"strconv"
)
const TIME_PARSE_LAYOUT = "2/1/2006"
const NilIntVal = int8(0)
func StringToIntPtr[T int8 | int32 | int64](s string) *T {
var val T
switch any(val).(type) {
case int8, int32, int64:
num, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return nil
}
switch any(val).(type) {
case int8:
val := int8(num)
return any(&val).(*T)
case int32:
val := int32(num)
return any(&val).(*T)
case int64:
val := int64(num)
return any(&val).(*T)
default:
return nil
}
default:
fmt.Printf("Unsupported type: %s\n", reflect.TypeOf(val).Name())
return nil
}
}
func StringToStringPtr(s string) *string {
if s == "" {
return nil
}
return &s
}
func Pad(numStr string, size int32) string {
for int32(len(numStr)) < size {
numStr = "0" + numStr
}
return numStr
}