first commit
This commit is contained in:
31
internal/models/auth/auth.go
Normal file
31
internal/models/auth/auth.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
// LoginRequest represents the login request payload
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// TokenResponse represents the token response
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
}
|
||||
|
||||
// JWTClaims represents the JWT claims
|
||||
type JWTClaims struct {
|
||||
UserID string `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
// User represents a user for authentication
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"-"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
221
internal/models/models.go
Normal file
221
internal/models/models.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NullableInt32 - your existing implementation
|
||||
type NullableInt32 struct {
|
||||
Int32 int32 `json:"int32,omitempty"`
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface for NullableInt32
|
||||
func (n *NullableInt32) Scan(value interface{}) error {
|
||||
var ni sql.NullInt32
|
||||
if err := ni.Scan(value); err != nil {
|
||||
return err
|
||||
}
|
||||
n.Int32 = ni.Int32
|
||||
n.Valid = ni.Valid
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface for NullableInt32
|
||||
func (n NullableInt32) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Int32, nil
|
||||
}
|
||||
|
||||
// NullableString provides consistent nullable string handling
|
||||
type NullableString struct {
|
||||
String string `json:"string,omitempty"`
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface for NullableString
|
||||
func (n *NullableString) Scan(value interface{}) error {
|
||||
var ns sql.NullString
|
||||
if err := ns.Scan(value); err != nil {
|
||||
return err
|
||||
}
|
||||
n.String = ns.String
|
||||
n.Valid = ns.Valid
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface for NullableString
|
||||
func (n NullableString) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.String, nil
|
||||
}
|
||||
|
||||
// NullableTime provides consistent nullable time handling
|
||||
type NullableTime struct {
|
||||
Time time.Time `json:"time,omitempty"`
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface for NullableTime
|
||||
func (n *NullableTime) Scan(value interface{}) error {
|
||||
var nt sql.NullTime
|
||||
if err := nt.Scan(value); err != nil {
|
||||
return err
|
||||
}
|
||||
n.Time = nt.Time
|
||||
n.Valid = nt.Valid
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface for NullableTime
|
||||
func (n NullableTime) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Time, nil
|
||||
}
|
||||
|
||||
// Metadata untuk pagination - dioptimalkan
|
||||
type MetaResponse struct {
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
Total int `json:"total"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
CurrentPage int `json:"current_page"`
|
||||
HasNext bool `json:"has_next"`
|
||||
HasPrev bool `json:"has_prev"`
|
||||
}
|
||||
|
||||
// Aggregate data untuk summary
|
||||
type AggregateData struct {
|
||||
TotalActive int `json:"total_active"`
|
||||
TotalDraft int `json:"total_draft"`
|
||||
TotalInactive int `json:"total_inactive"`
|
||||
ByStatus map[string]int `json:"by_status"`
|
||||
ByDinas map[string]int `json:"by_dinas,omitempty"`
|
||||
ByJenis map[string]int `json:"by_jenis,omitempty"`
|
||||
LastUpdated *time.Time `json:"last_updated,omitempty"`
|
||||
CreatedToday int `json:"created_today"`
|
||||
UpdatedToday int `json:"updated_today"`
|
||||
}
|
||||
|
||||
// Error response yang konsisten
|
||||
type ErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// BaseRequest contains common fields for all BPJS requests
|
||||
type BaseRequest struct {
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// BaseResponse contains common response fields
|
||||
type BaseResponse struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// ErrorResponse represents error response structure
|
||||
type ErrorResponseBpjs struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Errors map[string]interface{} `json:"errors,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// PaginationRequest contains pagination parameters
|
||||
type PaginationRequest struct {
|
||||
Page int `json:"page" validate:"min=1"`
|
||||
Limit int `json:"limit" validate:"min=1,max=100"`
|
||||
SortBy string `json:"sort_by,omitempty"`
|
||||
SortDir string `json:"sort_dir,omitempty" validate:"omitempty,oneof=asc desc"`
|
||||
}
|
||||
|
||||
// PaginationResponse contains pagination metadata
|
||||
type PaginationResponse struct {
|
||||
CurrentPage int `json:"current_page"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
TotalItems int64 `json:"total_items"`
|
||||
ItemsPerPage int `json:"items_per_page"`
|
||||
HasNext bool `json:"has_next"`
|
||||
HasPrev bool `json:"has_previous"`
|
||||
}
|
||||
|
||||
// MetaInfo contains additional metadata
|
||||
type MetaInfo struct {
|
||||
Version string `json:"version"`
|
||||
Environment string `json:"environment"`
|
||||
ServerTime string `json:"server_time"`
|
||||
}
|
||||
|
||||
func GetStatusCodeFromMeta(metaCode interface{}) int {
|
||||
statusCode := http.StatusOK
|
||||
|
||||
if metaCode != nil {
|
||||
switch v := metaCode.(type) {
|
||||
case string:
|
||||
if code, err := strconv.Atoi(v); err == nil {
|
||||
if code >= 100 && code <= 599 {
|
||||
statusCode = code
|
||||
} else {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
} else {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
case int:
|
||||
if v >= 100 && v <= 599 {
|
||||
statusCode = v
|
||||
} else {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
case float64:
|
||||
code := int(v)
|
||||
if code >= 100 && code <= 599 {
|
||||
statusCode = code
|
||||
} else {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
default:
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
return statusCode
|
||||
}
|
||||
|
||||
// Validation constants
|
||||
const (
|
||||
StatusDraft = "draft"
|
||||
StatusActive = "active"
|
||||
StatusInactive = "inactive"
|
||||
StatusDeleted = "deleted"
|
||||
)
|
||||
|
||||
// ValidStatuses untuk validasi
|
||||
var ValidStatuses = []string{StatusDraft, StatusActive, StatusInactive}
|
||||
|
||||
// IsValidStatus helper function
|
||||
func IsValidStatus(status string) bool {
|
||||
for _, validStatus := range ValidStatuses {
|
||||
if status == validStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
228
internal/models/retribusi/retribusi.go
Normal file
228
internal/models/retribusi/retribusi.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package retribusi
|
||||
|
||||
import (
|
||||
"api-service/internal/models"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retribusi represents the data structure for the retribusi table
|
||||
// with proper null handling and optimized JSON marshaling
|
||||
type Retribusi struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
Status string `json:"status" db:"status"`
|
||||
Sort models.NullableInt32 `json:"sort,omitempty" db:"sort"`
|
||||
UserCreated models.NullableString `json:"user_created,omitempty" db:"user_created"`
|
||||
DateCreated models.NullableTime `json:"date_created,omitempty" db:"date_created"`
|
||||
UserUpdated models.NullableString `json:"user_updated,omitempty" db:"user_updated"`
|
||||
DateUpdated models.NullableTime `json:"date_updated,omitempty" db:"date_updated"`
|
||||
Jenis models.NullableString `json:"jenis,omitempty" db:"Jenis"`
|
||||
Pelayanan models.NullableString `json:"pelayanan,omitempty" db:"Pelayanan"`
|
||||
Dinas models.NullableString `json:"dinas,omitempty" db:"Dinas"`
|
||||
KelompokObyek models.NullableString `json:"kelompok_obyek,omitempty" db:"Kelompok_obyek"`
|
||||
KodeTarif models.NullableString `json:"kode_tarif,omitempty" db:"Kode_tarif"`
|
||||
Tarif models.NullableString `json:"tarif,omitempty" db:"Tarif"`
|
||||
Satuan models.NullableString `json:"satuan,omitempty" db:"Satuan"`
|
||||
TarifOvertime models.NullableString `json:"tarif_overtime,omitempty" db:"Tarif_overtime"`
|
||||
SatuanOvertime models.NullableString `json:"satuan_overtime,omitempty" db:"Satuan_overtime"`
|
||||
RekeningPokok models.NullableString `json:"rekening_pokok,omitempty" db:"Rekening_pokok"`
|
||||
RekeningDenda models.NullableString `json:"rekening_denda,omitempty" db:"Rekening_denda"`
|
||||
Uraian1 models.NullableString `json:"uraian_1,omitempty" db:"Uraian_1"`
|
||||
Uraian2 models.NullableString `json:"uraian_2,omitempty" db:"Uraian_2"`
|
||||
Uraian3 models.NullableString `json:"uraian_3,omitempty" db:"Uraian_3"`
|
||||
}
|
||||
|
||||
// Custom JSON marshaling untuk Retribusi agar NULL values tidak muncul di response
|
||||
func (r Retribusi) MarshalJSON() ([]byte, error) {
|
||||
type Alias Retribusi
|
||||
aux := &struct {
|
||||
Sort *int `json:"sort,omitempty"`
|
||||
UserCreated *string `json:"user_created,omitempty"`
|
||||
DateCreated *time.Time `json:"date_created,omitempty"`
|
||||
UserUpdated *string `json:"user_updated,omitempty"`
|
||||
DateUpdated *time.Time `json:"date_updated,omitempty"`
|
||||
Jenis *string `json:"jenis,omitempty"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty"`
|
||||
Dinas *string `json:"dinas,omitempty"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty"`
|
||||
Satuan *string `json:"satuan,omitempty"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(&r),
|
||||
}
|
||||
|
||||
// Convert NullableInt32 to pointer
|
||||
if r.Sort.Valid {
|
||||
sort := int(r.Sort.Int32)
|
||||
aux.Sort = &sort
|
||||
}
|
||||
if r.UserCreated.Valid {
|
||||
aux.UserCreated = &r.UserCreated.String
|
||||
}
|
||||
if r.DateCreated.Valid {
|
||||
aux.DateCreated = &r.DateCreated.Time
|
||||
}
|
||||
if r.UserUpdated.Valid {
|
||||
aux.UserUpdated = &r.UserUpdated.String
|
||||
}
|
||||
if r.DateUpdated.Valid {
|
||||
aux.DateUpdated = &r.DateUpdated.Time
|
||||
}
|
||||
if r.Jenis.Valid {
|
||||
aux.Jenis = &r.Jenis.String
|
||||
}
|
||||
if r.Pelayanan.Valid {
|
||||
aux.Pelayanan = &r.Pelayanan.String
|
||||
}
|
||||
if r.Dinas.Valid {
|
||||
aux.Dinas = &r.Dinas.String
|
||||
}
|
||||
if r.KelompokObyek.Valid {
|
||||
aux.KelompokObyek = &r.KelompokObyek.String
|
||||
}
|
||||
if r.KodeTarif.Valid {
|
||||
aux.KodeTarif = &r.KodeTarif.String
|
||||
}
|
||||
if r.Tarif.Valid {
|
||||
aux.Tarif = &r.Tarif.String
|
||||
}
|
||||
if r.Satuan.Valid {
|
||||
aux.Satuan = &r.Satuan.String
|
||||
}
|
||||
if r.TarifOvertime.Valid {
|
||||
aux.TarifOvertime = &r.TarifOvertime.String
|
||||
}
|
||||
if r.SatuanOvertime.Valid {
|
||||
aux.SatuanOvertime = &r.SatuanOvertime.String
|
||||
}
|
||||
if r.RekeningPokok.Valid {
|
||||
aux.RekeningPokok = &r.RekeningPokok.String
|
||||
}
|
||||
if r.RekeningDenda.Valid {
|
||||
aux.RekeningDenda = &r.RekeningDenda.String
|
||||
}
|
||||
if r.Uraian1.Valid {
|
||||
aux.Uraian1 = &r.Uraian1.String
|
||||
}
|
||||
if r.Uraian2.Valid {
|
||||
aux.Uraian2 = &r.Uraian2.String
|
||||
}
|
||||
if r.Uraian3.Valid {
|
||||
aux.Uraian3 = &r.Uraian3.String
|
||||
}
|
||||
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// Helper methods untuk mendapatkan nilai yang aman
|
||||
func (r *Retribusi) GetJenis() string {
|
||||
if r.Jenis.Valid {
|
||||
return r.Jenis.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Retribusi) GetDinas() string {
|
||||
if r.Dinas.Valid {
|
||||
return r.Dinas.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Retribusi) GetTarif() string {
|
||||
if r.Tarif.Valid {
|
||||
return r.Tarif.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response struct untuk GET by ID - diperbaiki struktur
|
||||
type RetribusiGetByIDResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Request struct untuk create - dioptimalkan dengan validasi
|
||||
type RetribusiCreateRequest struct {
|
||||
Status string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"`
|
||||
Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
}
|
||||
|
||||
// Response struct untuk create
|
||||
type RetribusiCreateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Update request - sama seperti create tapi dengan ID
|
||||
type RetribusiUpdateRequest struct {
|
||||
ID string `json:"-" validate:"required,uuid4"` // ID dari URL path
|
||||
Status string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
Jenis *string `json:"jenis,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Pelayanan *string `json:"pelayanan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Dinas *string `json:"dinas,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
KodeTarif *string `json:"kode_tarif,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Uraian1 *string `json:"uraian_1,omitempty"`
|
||||
Uraian2 *string `json:"uraian_2,omitempty"`
|
||||
Uraian3 *string `json:"uraian_3,omitempty"`
|
||||
Tarif *string `json:"tarif,omitempty" validate:"omitempty,numeric"`
|
||||
Satuan *string `json:"satuan,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
TarifOvertime *string `json:"tarif_overtime,omitempty" validate:"omitempty,numeric"`
|
||||
SatuanOvertime *string `json:"satuan_overtime,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningPokok *string `json:"rekening_pokok,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
RekeningDenda *string `json:"rekening_denda,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
}
|
||||
|
||||
// Response struct untuk update
|
||||
type RetribusiUpdateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Retribusi `json:"data"`
|
||||
}
|
||||
|
||||
// Response struct untuk delete
|
||||
type RetribusiDeleteResponse struct {
|
||||
Message string `json:"message"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Enhanced GET response dengan pagination dan aggregation
|
||||
type RetribusiGetResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data []Retribusi `json:"data"`
|
||||
Meta models.MetaResponse `json:"meta"`
|
||||
Summary *models.AggregateData `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
// Filter struct untuk query parameters
|
||||
type RetribusiFilter struct {
|
||||
Status *string `json:"status,omitempty" form:"status"`
|
||||
Jenis *string `json:"jenis,omitempty" form:"jenis"`
|
||||
Dinas *string `json:"dinas,omitempty" form:"dinas"`
|
||||
KelompokObyek *string `json:"kelompok_obyek,omitempty" form:"kelompok_obyek"`
|
||||
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"`
|
||||
}
|
||||
106
internal/models/validation.go
Normal file
106
internal/models/validation.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// CustomValidator wraps the validator
|
||||
type CustomValidator struct {
|
||||
Validator *validator.Validate
|
||||
}
|
||||
|
||||
// Validate validates struct
|
||||
func (cv *CustomValidator) Validate(i interface{}) error {
|
||||
return cv.Validator.Struct(i)
|
||||
}
|
||||
|
||||
// RegisterCustomValidations registers custom validation rules
|
||||
func RegisterCustomValidations(v *validator.Validate) {
|
||||
// Validate Indonesian phone number
|
||||
v.RegisterValidation("indonesian_phone", validateIndonesianPhone)
|
||||
|
||||
// Validate BPJS card number format
|
||||
v.RegisterValidation("bpjs_card", validateBPJSCard)
|
||||
|
||||
// Validate Indonesian NIK
|
||||
v.RegisterValidation("indonesian_nik", validateIndonesianNIK)
|
||||
|
||||
// Validate date format YYYY-MM-DD
|
||||
v.RegisterValidation("date_format", validateDateFormat)
|
||||
|
||||
// Validate ICD-10 code format
|
||||
v.RegisterValidation("icd10", validateICD10)
|
||||
|
||||
// Validate ICD-9-CM procedure code
|
||||
v.RegisterValidation("icd9cm", validateICD9CM)
|
||||
}
|
||||
|
||||
func validateIndonesianPhone(fl validator.FieldLevel) bool {
|
||||
phone := fl.Field().String()
|
||||
if phone == "" {
|
||||
return true // Optional field
|
||||
}
|
||||
|
||||
// Indonesian phone number pattern: +62, 62, 08, or 8
|
||||
pattern := `^(\+?62|0?8)[1-9][0-9]{7,11}$`
|
||||
matched, _ := regexp.MatchString(pattern, phone)
|
||||
return matched
|
||||
}
|
||||
|
||||
func validateBPJSCard(fl validator.FieldLevel) bool {
|
||||
card := fl.Field().String()
|
||||
if len(card) != 13 {
|
||||
return false
|
||||
}
|
||||
|
||||
// BPJS card should be numeric
|
||||
pattern := `^\d{13}$`
|
||||
matched, _ := regexp.MatchString(pattern, card)
|
||||
return matched
|
||||
}
|
||||
|
||||
func validateIndonesianNIK(fl validator.FieldLevel) bool {
|
||||
nik := fl.Field().String()
|
||||
if len(nik) != 16 {
|
||||
return false
|
||||
}
|
||||
|
||||
// NIK should be numeric
|
||||
pattern := `^\d{16}$`
|
||||
matched, _ := regexp.MatchString(pattern, nik)
|
||||
return matched
|
||||
}
|
||||
|
||||
func validateDateFormat(fl validator.FieldLevel) bool {
|
||||
dateStr := fl.Field().String()
|
||||
_, err := time.Parse("2006-01-02", dateStr)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func validateICD10(fl validator.FieldLevel) bool {
|
||||
code := fl.Field().String()
|
||||
if code == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Basic ICD-10 pattern: Letter followed by 2 digits, optional dot and more digits
|
||||
pattern := `^[A-Z]\d{2}(\.\d+)?$`
|
||||
matched, _ := regexp.MatchString(pattern, strings.ToUpper(code))
|
||||
return matched
|
||||
}
|
||||
|
||||
func validateICD9CM(fl validator.FieldLevel) bool {
|
||||
code := fl.Field().String()
|
||||
if code == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Basic ICD-9-CM procedure pattern: 2-4 digits with optional decimal
|
||||
pattern := `^\d{2,4}(\.\d+)?$`
|
||||
matched, _ := regexp.MatchString(pattern, code)
|
||||
return matched
|
||||
}
|
||||
Reference in New Issue
Block a user