Files
service_antrean/internal/models/patient/ms_patient.go
2026-01-09 09:10:10 +07:00

485 lines
18 KiB
Go

package patient
import (
"api-service/internal/models"
"database/sql"
"encoding/json"
"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 {
ID int64 `json:"id" db:"id"`
Name sql.NullString `json:"name,omitempty" db:"name"`
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"`
Address sql.NullString `json:"address,omitempty" db:"address"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FKSdProvinsiID sql.NullInt64 `json:"fk_sd_provinsi_id,omitempty" db:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID sql.NullInt64 `json:"fk_sd_kabupaten_kota_id,omitempty" db:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID sql.NullInt64 `json:"fk_sd_kecamatan_id,omitempty" db:"fk_sd_kecamatan_id"`
FKSdKelurahanID sql.NullInt64 `json:"fk_sd_kelurahan_id,omitempty" db:"fk_sd_kelurahan_id"`
DsSdProvinsi sql.NullString `json:"ds_sd_provinsi,omitempty" db:"ds_sd_provinsi"`
DsSdKabupatenKota sql.NullString `json:"ds_sd_kabupaten_kota,omitempty" db:"ds_sd_kabupaten_kota"`
DsSdKecamatan sql.NullString `json:"ds_sd_kecamatan,omitempty" db:"ds_sd_kecamatan"`
DsSdKelurahan sql.NullString `json:"ds_sd_kelurahan,omitempty" db:"ds_sd_kelurahan"`
// ProvinsiName sql.NullString `json:"Provinsi,omitempty" db:"Provinsi"`
// KabupatenKotaName sql.NullString `json:"Kabupaten_kota,omitempty" db:"Kabupaten_kota"`
// KecamatanName sql.NullString `json:"Kecamatan,omitempty" db:"Kecamatan"`
// KelurahanName sql.NullString `json:"Desa_kelurahan,omitempty" db:"Desa_kelurahan"`
Attachments []PatientAttachment `json:"attachments,omitempty"`
PaymentTypes []PatientPaymentType `json:"payment_types,omitempty"`
Visits []PatientVisit `json:"visits,omitempty"`
}
type PatientPaymentType struct {
ID int64 `json:"id" db:"id"`
Name sql.NullString `json:"name,omitempty" db:"name"`
Number sql.NullString `json:"number,omitempty" db:"number"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FKMsPatientID models.NullableInt32 `json:"fk_ms_patient_id,omitempty" db:"fk_ms_patient_id"`
FKRefPaymentType models.NullableInt32 `json:"fk_ref_payment_type,omitempty" db:"fk_ref_payment_type"`
}
type PatientAttachment struct {
ID int64 `json:"id" db:"id"`
Name sql.NullString `json:"name,omitempty" db:"name"`
FileName sql.NullString `json:"file_name,omitempty" db:"file_name"`
Directory sql.NullString `json:"directory,omitempty" db:"directory"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FKMsPatientID models.NullableInt32 `json:"fk_ms_patient_id,omitempty" db:"fk_ms_patient_id"`
}
type PatientVisit struct {
ID int64 `json:"id" db:"id"`
Barcode models.NullableInt32 `json:"barcode,omitempty" db:"barcode"`
RegistrationDate sql.NullTime `json:"registration_date,omitempty" db:"registration_date"`
ServiceDate sql.NullTime `json:"service_date,omitempty" db:"service_date"`
CheckInDate sql.NullTime `json:"check_in_date,omitempty" db:"check_in_date"`
CheckIn sql.NullBool `json:"check_in,omitempty" db:"check_in"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FKMsPatientID models.NullableInt32 `json:"fk_ms_patient_id,omitempty" db:"fk_ms_patient_id"`
}
// Custom JSON marshaling untuk Patient agar NULL values tidak muncul di response
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 *string `json:"birth_date,omitempty"`
Address *string `json:"address,omitempty"`
Active *bool `json:"active,omitempty"`
FKSdProvinsiID *int `json:"fk_sd_provinsi_id,omitempty"`
FKSdKabupatenKotaID *int `json:"fk_sd_kabupaten_kota_id,omitempty"`
FKSdKecamatanID *int `json:"fk_sd_kecamatan_id,omitempty"`
FKSdKelurahanID *int `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),
}
if r.Name.Valid {
aux.Name = &r.Name.String
}
if r.MedicalRecordNumber.Valid {
aux.MedicalRecordNumber = &r.MedicalRecordNumber.String
}
if r.PhoneNumber.Valid {
aux.PhoneNumber = &r.PhoneNumber.String
}
if r.Gender.Valid {
aux.Gender = &r.Gender.String
}
if r.BirthDate.Valid {
birthDateStr := r.BirthDate.Time.Format("2006-01-02")
aux.BirthDate = &birthDateStr
}
if r.Address.Valid {
aux.Address = &r.Address.String
}
if r.Active.Valid {
aux.Active = &r.Active.Bool
}
if r.FKSdProvinsiID.Valid {
fksp := int(r.FKSdProvinsiID.Int64)
aux.FKSdProvinsiID = &fksp
}
if r.FKSdKabupatenKotaID.Valid {
fksk := int(r.FKSdKabupatenKotaID.Int64)
aux.FKSdKabupatenKotaID = &fksk
}
if r.FKSdKecamatanID.Valid {
fksc := int(r.FKSdKecamatanID.Int64)
aux.FKSdKecamatanID = &fksc
}
if r.FKSdKelurahanID.Valid {
fksl := int(r.FKSdKelurahanID.Int64)
aux.FKSdKelurahanID = &fksl
}
if r.DsSdProvinsi.Valid {
aux.DsSdProvinsi = &r.DsSdProvinsi.String
}
if r.DsSdKabupatenKota.Valid {
aux.DsSdKabupatenKota = &r.DsSdKabupatenKota.String
}
if r.DsSdKecamatan.Valid {
aux.DsSdKecamatan = &r.DsSdKecamatan.String
}
if r.DsSdKelurahan.Valid {
aux.DsSdKelurahan = &r.DsSdKelurahan.String
}
return json.Marshal(aux)
}
func (r PatientPaymentType) MarshalJSON() ([]byte, error) {
type Alias PatientPaymentType
aux := &struct {
*Alias
Name *string `json:"name,omitempty"`
Number *string `json:"number,omitempty"`
Active *bool `json:"active,omitempty"`
FKMsPatientID *int `json:"fk_ms_patient_id,omitempty"`
FKRefPaymentType *int `json:"fk_ref_payment_type,omitempty"`
}{
Alias: (*Alias)(&r),
}
if r.Name.Valid {
aux.Name = &r.Name.String
}
if r.Number.Valid {
aux.Number = &r.Number.String
}
if r.Active.Valid {
aux.Active = &r.Active.Bool
}
if r.FKMsPatientID.Valid {
fkmp := int(r.FKMsPatientID.Int32)
aux.FKMsPatientID = &fkmp
}
if r.FKRefPaymentType.Valid {
fkrpt := int(r.FKRefPaymentType.Int32)
aux.FKRefPaymentType = &fkrpt
}
return json.Marshal(aux)
}
func (r PatientAttachment) MarshalJSON() ([]byte, error) {
type Alias PatientAttachment
aux := &struct {
*Alias
Name *string `json:"name,omitempty"`
FileName *string `json:"file_name,omitempty"`
Directory *string `json:"directory,omitempty"`
Active *bool `json:"active,omitempty"`
FKMsPatientID *int `json:"fk_ms_patient_id,omitempty"`
}{
Alias: (*Alias)(&r),
}
if r.Name.Valid {
aux.Name = &r.Name.String
}
if r.FileName.Valid {
aux.FileName = &r.FileName.String
}
if r.Directory.Valid {
aux.Directory = &r.Directory.String
}
if r.Active.Valid {
aux.Active = &r.Active.Bool
}
if r.FKMsPatientID.Valid {
fkmp := int(r.FKMsPatientID.Int32)
aux.FKMsPatientID = &fkmp
}
return json.Marshal(aux)
}
func (r PatientVisit) MarshalJSON() ([]byte, error) {
type Alias PatientVisit
aux := &struct {
*Alias
Barcode *int `json:"barcode,omitempty"`
RegistrationDate *string `json:"registration_date,omitempty"`
ServiceDate *string `json:"service_date,omitempty"`
CheckInDate *string `json:"check_in_date,omitempty"`
CheckIn *bool `json:"check_in,omitempty"`
Active *bool `json:"active,omitempty"`
FKMsPatientID *int `json:"fk_ms_patient_id,omitempty"`
}{
Alias: (*Alias)(&r),
}
if r.Barcode.Valid {
barcode := int(r.Barcode.Int32)
aux.Barcode = &barcode
}
if r.RegistrationDate.Valid {
regDateStr := r.RegistrationDate.Time.Format(time.RFC3339)
aux.RegistrationDate = &regDateStr
}
if r.ServiceDate.Valid {
svcDateStr := r.ServiceDate.Time.Format(time.RFC3339)
aux.ServiceDate = &svcDateStr
}
if r.CheckInDate.Valid {
checkInDateStr := r.CheckInDate.Time.Format(time.RFC3339)
aux.CheckInDate = &checkInDateStr
}
if r.CheckIn.Valid {
aux.CheckIn = &r.CheckIn.Bool
}
if r.Active.Valid {
aux.Active = &r.Active.Bool
}
if r.FKMsPatientID.Valid {
fkmp := int(r.FKMsPatientID.Int32)
aux.FKMsPatientID = &fkmp
}
return json.Marshal(aux)
}
// Helper methods untuk mendapatkan nilai yang aman
func (r *Patient) GetName() string {
if r.Name.Valid {
return r.Name.String
}
return ""
}
// Response struct untuk GET by ID
type PatientGetByIDResponse struct {
Message string `json:"message"`
Data *Patient `json:"data"`
}
// Enhanced GET response dengan pagination dan aggregation
type PatientGetResponse struct {
Message string `json:"message"`
Data []Patient `json:"data"`
Meta models.MetaResponse `json:"meta"`
Summary *models.AggregateData `json:"summary,omitempty"`
}
// Request struct untuk create
type PatientCreateRequest struct {
ID *int `json:"id"`
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 *time.Time `json:"birth_date"`
Address *string `json:"address" validate:"min=1,max=255"`
Active *bool `json:"active"`
FKSdProvinsiID *int `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID *int `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID *int `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID *int `json:"fk_sd_kelurahan_id"`
DsSdProvinsi *string `json:"ds_sd_provinsi"`
DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota"`
DsSdKecamatan *string `json:"ds_sd_kecamatan"`
DsSdKelurahan *string `json:"ds_sd_kelurahan"`
Attachments []PatientAttachmentCreateRequest `json:"attachments,omitempty"`
Payments []PatientPaymentTypeCreateRequest `json:"payment_types,omitempty"`
Visits []VisitCreateRequest `json:"visits,omitempty"`
}
type VisitCreateRequest struct {
// ID *int `json:"id"`
Barcode *int32 `json:"barcode" validate:"omitempty,min=1"`
RegistrationDate *time.Time `json:"registration_date" validate:"omitempty"`
ServiceDate *time.Time `json:"service_date" validate:"omitempty"`
CheckInDate *time.Time `json:"check_in_date" validate:"omitempty"`
CheckIn *bool `json:"check_in" validate:"omitempty"`
Active *bool `json:"active" validate:"omitempty"`
}
type VisitUpdateRequest struct {
ID *int `json:"id" validate:"required"`
Barcode *int32 `json:"barcode" validate:"omitempty,min=1"`
RegistrationDate *time.Time `json:"registration_date" validate:"omitempty"`
ServiceDate *time.Time `json:"service_date" validate:"omitempty"`
CheckInDate *time.Time `json:"check_in_date" validate:"omitempty"`
CheckIn *bool `json:"check_in" validate:"omitempty"`
Active *bool `json:"active" validate:"omitempty"`
}
type PatientPostRequest struct {
// ID int64 `json:"id" validate:"required"`
MedicalRecordNumber string `json:"medical_record_number" validate:"required,min=1"`
}
// Response struct untuk create
type PatientCreateResponse struct {
Message string `json:"message"`
Data *Patient `json:"data"`
}
// Update request
type PatientUpdateRequest struct {
// ID *int `json:"-" validate:"required,min=1"`
Name *string `json:"name"`
MedicalRecordNumber *string `json:"medical_record_number" validate:"required,min=1,max=20"`
PhoneNumber *string `json:"phone_number"`
Gender *string `json:"gender"`
BirthDate *time.Time `json:"birth_date"`
Address *string `json:"address"`
Active *bool `json:"active"`
FKSdProvinsiID *int `json:"fk_sd_provinsi_id"`
FKSdKabupatenKotaID *int `json:"fk_sd_kabupaten_kota_id"`
FKSdKecamatanID *int `json:"fk_sd_kecamatan_id"`
FKSdKelurahanID *int `json:"fk_sd_kelurahan_id"`
DsSdProvinsi *string `json:"ds_sd_provinsi"`
DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota"`
DsSdKecamatan *string `json:"ds_sd_kecamatan"`
DsSdKelurahan *string `json:"ds_sd_kelurahan"`
Attachments []PatientAttachmentUpdateRequest `json:"attachments,omitempty"`
Payments []PatientPaymentTypeUpdateRequest `json:"payment_types,omitempty"`
Visits []VisitUpdateRequest `json:"visits,omitempty"`
}
type PatientAttachmentCreateRequest struct {
// ID *int `json:"id,omitempty"`
Name *string `json:"name" validate:"omitempty,min=1,max=20"`
FileName *string `json:"file_name" validate:"omitempty,min=1,max=20"`
Directory *string `json:"directory" validate:"omitempty,min=1,max=255"`
Active *bool `json:"active" validate:"omitempty"`
}
type PatientPaymentTypeCreateRequest struct {
// ID *int `json:"id,omitempty"`
Name *string `json:"name" validate:"omitempty,min=1,max=20"`
Number *string `json:"number" validate:"omitempty,min=1,max=20"`
Active *bool `json:"active" validate:"omitempty"`
FKRefPaymentType *int `json:"fk_ref_payment_type" validate:"omitempty,min=1"`
}
type PatientAttachmentUpdateRequest struct {
ID *int `json:"id" validate:"required"`
Name *string `json:"name" validate:"omitempty,min=1,max=20"`
FileName *string `json:"file_name" validate:"omitempty,min=1,max=20"`
Directory *string `json:"directory" validate:"omitempty,min=1,max=255"`
Active *bool `json:"active" validate:"omitempty"`
}
type PatientPaymentTypeUpdateRequest struct {
ID *int `json:"id" validate:"required"`
Name *string `json:"name" validate:"omitempty,min=1,max=20"`
Number *string `json:"number" validate:"omitempty,min=1,max=20"`
Active *bool `json:"active" validate:"omitempty"`
FkRefPaymentType *int `json:"fk_ref_payment_type" validate:"omitempty,min=1"`
}
// Response struct untuk update
type PatientUpdateResponse struct {
Message string `json:"message"`
Data *Patient `json:"data"`
}
// Response struct untuk delete
type PatientDeleteResponse struct {
Message string `json:"message"`
ID string `json:"id"`
MedicalRecordNumber string `json:"medical_record_number"`
}
// Filter struct untuk query parameters
type PatientFilter struct {
Status *string `json:"status,omitempty" form:"status"`
Search *string `json:"search,omitempty" form:"search"`
DateFrom *time.Time `json:"date_from,omitempty" form:"date_from"`
DateTo *time.Time `json:"date_to,omitempty" form:"date_to"`
}