package entities import ( "database/sql/driver" "encoding/json" "fmt" "time" "github.com/google/uuid" "gorm.io/gorm" ) type RewardType string const ( RewardTypeVoucher RewardType = "VOUCHER" RewardTypePhysical RewardType = "PHYSICAL" RewardTypeDigital RewardType = "DIGITAL" RewardTypeBalance RewardType = "BALANCE" ) // StringSlice is a custom type for []string that implements sql.Scanner and driver.Valuer type StringSlice []string // Scan implements the sql.Scanner interface for StringSlice func (s *StringSlice) Scan(value interface{}) error { if value == nil { *s = StringSlice{} return nil } var bytes []byte switch v := value.(type) { case []byte: bytes = v case string: bytes = []byte(v) default: return fmt.Errorf("cannot scan %T into StringSlice", value) } return json.Unmarshal(bytes, s) } // Value implements the driver.Valuer interface for StringSlice func (s StringSlice) Value() (driver.Value, error) { if s == nil { return nil, nil } return json.Marshal(s) } type TermsAndConditions struct { Sections []TncSection `json:"sections"` ExpiryDays int `json:"expiry_days"` } // Scan implements the sql.Scanner interface for TermsAndConditions func (t *TermsAndConditions) Scan(value interface{}) error { if value == nil { *t = TermsAndConditions{} return nil } var bytes []byte switch v := value.(type) { case []byte: bytes = v case string: bytes = []byte(v) default: return fmt.Errorf("cannot scan %T into TermsAndConditions", value) } return json.Unmarshal(bytes, t) } // Value implements the driver.Valuer interface for TermsAndConditions func (t TermsAndConditions) Value() (driver.Value, error) { return json.Marshal(t) } type TncSection struct { Title string `json:"title"` Rules []string `json:"rules"` } type Reward struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` Name string `gorm:"type:varchar(150);not null" json:"name"` RewardType RewardType `gorm:"type:varchar(50);not null" json:"reward_type"` CostPoints int64 `gorm:"type:bigint;not null" json:"cost_points"` Stock *int `gorm:"type:int" json:"stock,omitempty"` MaxPerCustomer int `gorm:"type:int;default:1" json:"max_per_customer"` Tnc *TermsAndConditions `gorm:"type:jsonb" json:"tnc,omitempty"` Metadata *map[string]interface{} `gorm:"type:jsonb" json:"metadata,omitempty"` Images *StringSlice `gorm:"type:jsonb" json:"images,omitempty"` CreatedAt time.Time `gorm:"type:timestamp;default:now()" json:"created_at"` UpdatedAt time.Time `gorm:"type:timestamp;default:now()" json:"updated_at"` } func (Reward) TableName() string { return "rewards" } func (r *Reward) BeforeCreate(tx *gorm.DB) error { if r.ID == uuid.Nil { r.ID = uuid.New() } return nil } func (r *Reward) BeforeUpdate(tx *gorm.DB) error { r.UpdatedAt = time.Now() return nil } type ListRewardsRequest struct { Page int `form:"page" binding:"min=1"` Limit int `form:"limit" binding:"min=1,max=100"` Search string `form:"search"` RewardType string `form:"reward_type"` MinPoints *int64 `form:"min_points"` MaxPoints *int64 `form:"max_points"` HasStock *bool `form:"has_stock"` }