forked from rachmadiyanti.annisa.3004/service_antrean
485 lines
18 KiB
Go
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 = ®DateStr
|
|
}
|
|
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"`
|
|
}
|