Perbaikan Patient CRUD
Some checks failed
Go-test / build (push) Has been cancelled

This commit is contained in:
2025-12-01 11:52:30 +07:00
parent f10689c606
commit 5515e76320
6 changed files with 1661 additions and 101 deletions

View File

@@ -3,10 +3,93 @@ package patient
import (
"api-service/internal/models"
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
"strings"
"time"
)
type Date time.Time
func (d *Date) UnmarshalJSON(b []byte) error {
s := strings.Trim(string(b), "\"")
if s == "" || s == "null" {
*d = Date(time.Time{})
return nil
}
t, err := time.Parse("2006-01-02", s)
if err != nil {
return fmt.Errorf("invalid date format, expected YYYY-MM-DD: %w", err)
}
*d = Date(t)
return nil
}
// new: MarshalJSON so responses use "YYYY-MM-DD"
func (d Date) MarshalJSON() ([]byte, error) {
t := time.Time(d)
if t.IsZero() {
return []byte("null"), nil
}
return []byte("\"" + t.Format("2006-01-02") + "\""), nil
}
// new: database/sql/driver.Valuer implementation so DB driver can encode Date
func (d Date) Value() (driver.Value, error) {
t := time.Time(d)
if t.IsZero() {
return nil, nil
}
// return time.Time so pq/pgx has an encode plan for date/timestamp types
return t, nil
}
// new: sql.Scanner implementation so DB rows decode into Date
func (d *Date) Scan(src interface{}) error {
if src == nil {
*d = Date(time.Time{})
return nil
}
switch v := src.(type) {
case time.Time:
*d = Date(v)
return nil
case []byte:
s := string(v)
if t, err := time.Parse("2006-01-02", s); err == nil {
*d = Date(t)
return nil
}
if t, err := time.Parse(time.RFC3339, s); err == nil {
*d = Date(t)
return nil
}
return fmt.Errorf("cannot parse date from []byte: %s", s)
case string:
s := v
if s == "" {
*d = Date(time.Time{})
return nil
}
if t, err := time.Parse("2006-01-02", s); err == nil {
*d = Date(t)
return nil
}
if t, err := time.Parse(time.RFC3339, s); err == nil {
*d = Date(t)
return nil
}
return fmt.Errorf("cannot parse date from string: %s", s)
default:
return fmt.Errorf("unsupported scan type for Date: %T", src)
}
}
func (d Date) ToTime() time.Time {
return time.Time(d)
}
// Patient represents the data structure for the patient table
// with proper null handling and optimized JSON marshaling
type Patient struct {
@@ -15,7 +98,7 @@ type Patient struct {
MedicalRecordNumber sql.NullString `json:"medical_record_number,omitempty" db:"medical_record_number"`
PhoneNumber sql.NullString `json:"phone_number,omitempty" db:"phone_number"`
Gender sql.NullString `json:"gender,omitempty" db:"gender"`
BirthDate sql.NullTime `json:"birth_date,omitempty" db:"birth_date"`
BirthDate Date `json:"birth_date,omitempty" db:"birth_date"`
Address sql.NullString `json:"address,omitempty" db:"address"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FKSdProvinsiID models.NullableInt32 `json:"fk_sd_provinsi_id,omitempty" db:"fk_sd_provinsi_id"`
@@ -32,21 +115,20 @@ type Patient struct {
func (r Patient) MarshalJSON() ([]byte, error) {
type Alias Patient
aux := &struct {
Name *string `json:"name,omitempty"`
MedicalRecordNumber *string `json:"medical_record_number,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"`
Gender *string `json:"gender,omitempty"`
BirthDate *time.Time `json:"birth_date,omitempty"`
Address *string `json:"address,omitempty"`
Active *bool `json:"active,omitempty"`
FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id,omitempty"`
FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id,omitempty"`
FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id,omitempty"`
FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id,omitempty"`
DsSdProvinsi *string `json:"ds_sd_provinsi,omitempty"`
DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota,omitempty"`
DsSdKecamatan *string `json:"ds_sd_kecamatan,omitempty"`
DsSdKelurahan *string `json:"ds_sd_kelurahan,omitempty"`
Name *string `json:"name,omitempty"`
MedicalRecordNumber *string `json:"medical_record_number,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"`
Gender *string `json:"gender,omitempty"`
Address *string `json:"address,omitempty"`
Active *bool `json:"active,omitempty"`
FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id,omitempty"`
FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id,omitempty"`
FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id,omitempty"`
FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id,omitempty"`
DsSdProvinsi *string `json:"ds_sd_provinsi,omitempty"`
DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota,omitempty"`
DsSdKecamatan *string `json:"ds_sd_kecamatan,omitempty"`
DsSdKelurahan *string `json:"ds_sd_kelurahan,omitempty"`
*Alias
}{
Alias: (*Alias)(&r),
@@ -64,9 +146,6 @@ func (r Patient) MarshalJSON() ([]byte, error) {
if r.Gender.Valid {
aux.Gender = &r.Gender.String
}
if r.BirthDate.Valid {
aux.BirthDate = &r.BirthDate.Time
}
if r.Address.Valid {
aux.Address = &r.Address.String
}
@@ -129,21 +208,21 @@ type PatientGetResponse struct {
// Request struct untuk create
type PatientCreateRequest struct {
Name string `json:"name" validate:"min=1,max=100"`
MedicalRecordNumber string `json:"medical_record_number" validate:"min=1,max=20"`
PhoneNumber string `json:"phone_number" validate:"min=1,max=20"`
Gender string `json:"gender" validate:"min=1,max=20"`
BirthDate time.Time `json:"birth_date"`
Address string `json:"address" validate:"min=1,max=255"`
Active bool `json:"active"`
FKSdProvinsiID int32 `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID int32 `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID int32 `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID int32 `json:"fk_sd_kelurahan_id"`
DsSdProvinsi string `json:"ds_sd_provinsi" validate:"min=1,max=255"`
DsSdKabupatenKota string `json:"ds_sd_kabupaten_kota" validate:"min=1,max=255"`
DsSdKecamatan string `json:"ds_sd_kecamatan" validate:"min=1,max=255"`
DsSdKelurahan string `json:"ds_sd_kelurahan" validate:"min=1,max=255"`
Name string `json:"name" validate:"min=1,max=100"`
MedicalRecordNumber string `json:"medical_record_number" validate:"min=1,max=20"`
PhoneNumber string `json:"phone_number" validate:"min=1,max=20"`
Gender string `json:"gender" validate:"max=1"`
BirthDate Date `json:"birth_date"`
Address string `json:"address" validate:"min=1,max=255"`
Active bool `json:"active"`
FKSdProvinsiID int32 `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID int32 `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID int32 `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID int32 `json:"fk_sd_kelurahan_id"`
DsSdProvinsi string `json:"ds_sd_provinsi" validate:"min=1,max=255"`
DsSdKabupatenKota string `json:"ds_sd_kabupaten_kota" validate:"min=1,max=255"`
DsSdKecamatan string `json:"ds_sd_kecamatan" validate:"min=1,max=255"`
DsSdKelurahan string `json:"ds_sd_kelurahan" validate:"min=1,max=255"`
}
type PatientPostRequest struct {
@@ -158,22 +237,22 @@ type PatientCreateResponse struct {
// Update request
type PatientUpdateRequest struct {
ID int `json:"id" validate:"required,min=1"`
Name string `json:"name" validate:"min=1,max=100"`
MedicalRecordNumber string `json:"medical_record_number" validate:"min=1,max=20"`
PhoneNumber string `json:"phone_number" validate:"min=1,max=20"`
Gender string `json:"gender" validate:"min=1,max=20"`
BirthDate time.Time `json:"birth_date"`
Address string `json:"address" validate:"min=1,max=255"`
Active bool `json:"active"`
FKSdProvinsiID int32 `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID int32 `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID int32 `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID int32 `json:"fk_sd_kelurahan_id"`
DsSdProvinsi string `json:"ds_sd_provinsi" validate:"max=255"`
DsSdKabupatenKota string `json:"ds_sd_kabupaten_kota" validate:"max=255"`
DsSdKecamatan string `json:"ds_sd_kecamatan" validate:"max=255"`
DsSdKelurahan string `json:"ds_sd_kelurahan" validate:"max=255"`
ID int `json:"id" validate:"required,min=1"`
Name string `json:"name" validate:"min=1,max=100"`
MedicalRecordNumber string `json:"medical_record_number" validate:"min=1,max=20"`
PhoneNumber string `json:"phone_number" validate:"min=1,max=20"`
Gender string `json:"gender" validate:"max=1"`
BirthDate Date `json:"birth_date"`
Address string `json:"address" validate:"min=1,max=255"`
Active bool `json:"active"`
FKSdProvinsiID int32 `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID int32 `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID int32 `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID int32 `json:"fk_sd_kelurahan_id"`
DsSdProvinsi string `json:"ds_sd_provinsi" validate:"max=255"`
DsSdKabupatenKota string `json:"ds_sd_kabupaten_kota" validate:"max=255"`
DsSdKecamatan string `json:"ds_sd_kecamatan" validate:"max=255"`
DsSdKelurahan string `json:"ds_sd_kelurahan" validate:"max=255"`
}
// Response struct untuk update