2025-09-17 23:55:11 +07:00
|
|
|
package entities
|
|
|
|
|
|
|
|
|
|
import (
|
2025-09-18 01:38:03 +07:00
|
|
|
"database/sql/driver"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2025-09-17 23:55:11 +07:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type RewardType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
RewardTypeVoucher RewardType = "VOUCHER"
|
|
|
|
|
RewardTypePhysical RewardType = "PHYSICAL"
|
|
|
|
|
RewardTypeDigital RewardType = "DIGITAL"
|
|
|
|
|
RewardTypeBalance RewardType = "BALANCE"
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-18 01:38:03 +07:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 23:55:11 +07:00
|
|
|
type TermsAndConditions struct {
|
|
|
|
|
Sections []TncSection `json:"sections"`
|
|
|
|
|
ExpiryDays int `json:"expiry_days"`
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-18 01:38:03 +07:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 23:55:11 +07:00
|
|
|
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"`
|
2025-09-18 01:38:03 +07:00
|
|
|
Images *StringSlice `gorm:"type:jsonb" json:"images,omitempty"`
|
2025-09-17 23:55:11 +07:00
|
|
|
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"`
|
|
|
|
|
}
|