package entity import ( "time" ) // ============================================= // DATABASE ENTITIES // ============================================= type UndianEventDB struct { ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` Title string `gorm:"size:255;not null" json:"title"` Description *string `gorm:"type:text" json:"description"` ImageURL *string `gorm:"size:500" json:"image_url"` Status string `gorm:"size:20;not null;default:upcoming" json:"status"` // upcoming, active, completed, cancelled // Event timing StartDate time.Time `gorm:"not null" json:"start_date"` EndDate time.Time `gorm:"not null" json:"end_date"` DrawDate time.Time `gorm:"not null" json:"draw_date"` // Configuration MinimumPurchase float64 `gorm:"type:decimal(10,2);default:50000" json:"minimum_purchase"` // Draw status DrawCompleted bool `gorm:"default:false" json:"draw_completed"` DrawCompletedAt *time.Time `json:"draw_completed_at"` // Metadata TermsAndConditions *string `gorm:"type:text" json:"terms_and_conditions"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` } func (UndianEventDB) TableName() string { return "undian_events" } type UndianPrizeDB struct { ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` UndianEventID int64 `gorm:"not null" json:"undian_event_id"` Rank int `gorm:"not null" json:"rank"` PrizeName string `gorm:"size:255;not null" json:"prize_name"` PrizeValue *float64 `gorm:"type:decimal(15,2)" json:"prize_value"` PrizeDescription *string `gorm:"type:text" json:"prize_description"` PrizeType string `gorm:"size:50;default:voucher" json:"prize_type"` // gold, voucher, cash, product, service PrizeImageURL *string `gorm:"size:500" json:"prize_image_url"` // Winner information (filled after draw) WinningVoucherID *int64 `json:"winning_voucher_id"` WinnerUserID *int64 `json:"winner_user_id"` // Relations UndianEvent UndianEventDB `gorm:"foreignKey:UndianEventID" json:"undian_event,omitempty"` } func (UndianPrizeDB) TableName() string { return "undian_prizes" } type UndianVoucherDB struct { ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` UndianEventID int64 `gorm:"not null" json:"undian_event_id"` CustomerID int64 `gorm:"not null" json:"customer_id"` OrderID *int64 `json:"order_id"` // Voucher identification VoucherCode string `gorm:"size:50;not null;uniqueIndex" json:"voucher_code"` VoucherNumber *int `json:"voucher_number"` // Winner status IsWinner bool `gorm:"default:false" json:"is_winner"` PrizeRank *int `json:"prize_rank"` WonAt *time.Time `json:"won_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` // Relations UndianEvent UndianEventDB `gorm:"foreignKey:UndianEventID" json:"undian_event,omitempty"` } func (UndianVoucherDB) TableName() string { return "undian_vouchers" } // ============================================= // REQUEST/RESPONSE ENTITIES // ============================================= type UndianEventSearch struct { Status string `json:"status"` IsActive bool `json:"is_active"` IsCompleted bool `json:"is_completed"` Search string `json:"search"` StartDateFrom time.Time `json:"start_date_from"` StartDateTo time.Time `json:"start_date_to"` Limit int `json:"limit"` Offset int `json:"offset"` } type CreateUndianEventRequest struct { Title string `json:"title" validate:"required,min=3,max=255"` Description *string `json:"description"` ImageURL *string `json:"image_url"` StartDate time.Time `json:"start_date" validate:"required"` EndDate time.Time `json:"end_date" validate:"required"` DrawDate time.Time `json:"draw_date" validate:"required"` MinimumPurchase float64 `json:"minimum_purchase" validate:"min=0"` TermsAndConditions *string `json:"terms_and_conditions"` Prizes []CreateUndianPrizeRequest `json:"prizes" validate:"required,min=1"` } type UpdateUndianEventRequest struct { ID int64 `json:"id" validate:"required"` Title string `json:"title" validate:"required,min=3,max=255"` Description *string `json:"description"` ImageURL *string `json:"image_url"` StartDate time.Time `json:"start_date" validate:"required"` EndDate time.Time `json:"end_date" validate:"required"` DrawDate time.Time `json:"draw_date" validate:"required"` MinimumPurchase float64 `json:"minimum_purchase" validate:"min=0"` TermsAndConditions *string `json:"terms_and_conditions"` Status string `json:"status" validate:"oneof=upcoming active completed cancelled"` } type CreateUndianPrizeRequest struct { Rank int `json:"rank" validate:"required,min=1"` PrizeName string `json:"prize_name" validate:"required,min=3,max=255"` PrizeValue *float64 `json:"prize_value"` PrizeDescription *string `json:"prize_description"` PrizeType string `json:"prize_type" validate:"oneof=gold voucher cash product service"` PrizeImageURL *string `json:"prize_image_url"` } type UndianEventResponse struct { ID int64 `json:"id"` Title string `json:"title"` Description *string `json:"description"` ImageURL *string `json:"image_url"` Status string `json:"status"` StartDate time.Time `json:"start_date"` EndDate time.Time `json:"end_date"` DrawDate time.Time `json:"draw_date"` MinimumPurchase float64 `json:"minimum_purchase"` DrawCompleted bool `json:"draw_completed"` DrawCompletedAt *time.Time `json:"draw_completed_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Statistics (populated when needed) TotalVouchers int64 `json:"total_vouchers,omitempty"` TotalParticipants int64 `json:"total_participants,omitempty"` TotalPrizes int64 `json:"total_prizes,omitempty"` // Relations (populated when needed) Prizes []UndianPrizeResponse `json:"prizes,omitempty"` } type UndianPrizeResponse struct { ID int64 `json:"id"` UndianEventID int64 `json:"undian_event_id"` Rank int `json:"rank"` PrizeName string `json:"prize_name"` PrizeValue *float64 `json:"prize_value"` PrizeDescription *string `json:"prize_description"` PrizeType string `json:"prize_type"` PrizeImageURL *string `json:"prize_image_url"` WinningVoucherID *int64 `json:"winning_voucher_id,omitempty"` WinnerUserID *int64 `json:"winner_user_id,omitempty"` } type UndianVoucherResponse struct { ID int64 `json:"id"` UndianEventID int64 `json:"undian_event_id"` CustomerID int64 `json:"customer_id"` OrderID *int64 `json:"order_id"` VoucherCode string `json:"voucher_code"` VoucherNumber *int `json:"voucher_number"` IsWinner bool `json:"is_winner"` PrizeRank *int `json:"prize_rank"` WonAt *time.Time `json:"won_at"` CreatedAt time.Time `json:"created_at"` // Relations (populated when needed) UndianEvent *UndianEventResponse `json:"undian_event,omitempty"` Prize *UndianPrizeResponse `json:"prize,omitempty"` } // ============================================= // COMPOSITE ENTITIES // ============================================= type UndianEventWithStats struct { Event UndianEventDB `json:"event"` TotalVouchers int64 `json:"total_vouchers"` TotalParticipants int64 `json:"total_participants"` TotalPrizes int64 `json:"total_prizes"` } type UndianEventWithPrizes struct { Event UndianEventDB `json:"event"` Prizes []*UndianPrizeDB `json:"prizes"` } type CustomerUndianSummary struct { CustomerID int64 `json:"customer_id"` EventID int64 `json:"event_id"` TotalVouchers int64 `json:"total_vouchers"` WinningVouchers int64 `json:"winning_vouchers"` IsParticipating bool `json:"is_participating"` } // ============================================= // API RESPONSE ENTITIES // ============================================= type UndianEventListResponse struct { Events []UndianEventResponse `json:"events"` Total int `json:"total"` Page int `json:"page"` Limit int `json:"limit"` } type CustomerUndianListResponse struct { Events []CustomerUndianEventResponse `json:"events"` Total int `json:"total"` } type CustomerUndianEventResponse struct { Event UndianEventResponse `json:"event"` UserVouchers []UndianVoucherResponse `json:"user_vouchers"` TotalVouchers int `json:"total_vouchers"` WinningVouchers int `json:"winning_vouchers"` IsParticipating bool `json:"is_participating"` } type UndianEventDetailResponse struct { Event UndianEventResponse `json:"event"` Prizes []UndianPrizeResponse `json:"prizes"` UserVouchers []UndianVoucherResponse `json:"user_vouchers,omitempty"` TotalVouchers int64 `json:"total_vouchers"` TotalParticipants int64 `json:"total_participants"` UserParticipating bool `json:"user_participating"` UserVoucherCount int `json:"user_voucher_count"` } type DrawResultResponse struct { EventID int64 `json:"event_id"` EventTitle string `json:"event_title"` DrawDate time.Time `json:"draw_date"` DrawCompleted bool `json:"draw_completed"` Winners []DrawWinnerResponse `json:"winners"` } type DrawWinnerResponse struct { Rank int `json:"rank"` PrizeName string `json:"prize_name"` PrizeValue *float64 `json:"prize_value"` VoucherCode string `json:"voucher_code"` WinnerUserID *int64 `json:"winner_user_id,omitempty"` } // ============================================= // ERROR RESPONSES // ============================================= type UndianError struct { Code string `json:"code"` Message string `json:"message"` Details string `json:"details,omitempty"` } func (e UndianError) Error() string { return e.Message } // Common error codes var ( ErrUndianEventNotFound = UndianError{ Code: "UNDIAN_EVENT_NOT_FOUND", Message: "Undian event not found", } ErrUndianEventNotActive = UndianError{ Code: "UNDIAN_EVENT_NOT_ACTIVE", Message: "Undian event is not active", } ErrDrawAlreadyCompleted = UndianError{ Code: "DRAW_ALREADY_COMPLETED", Message: "Draw has already been completed for this event", } ErrDrawDateNotReached = UndianError{ Code: "DRAW_DATE_NOT_REACHED", Message: "Draw date has not been reached yet", } ErrInsufficientOrderAmount = UndianError{ Code: "INSUFFICIENT_ORDER_AMOUNT", Message: "Order amount does not meet minimum purchase requirement", } ErrVoucherNotFound = UndianError{ Code: "VOUCHER_NOT_FOUND", Message: "Voucher not found", } ErrVoucherAlreadyExists = UndianError{ Code: "VOUCHER_ALREADY_EXISTS", Message: "Voucher with this code already exists", } ErrInvalidDateRange = UndianError{ Code: "INVALID_DATE_RANGE", Message: "Invalid date range: start_date must be before end_date, and end_date must be before draw_date", } ) // ============================================= // CONVERSION METHODS // ============================================= func (e *UndianEventDB) ToResponse() UndianEventResponse { return UndianEventResponse{ ID: e.ID, Title: e.Title, Description: e.Description, ImageURL: e.ImageURL, Status: e.Status, StartDate: e.StartDate, EndDate: e.EndDate, DrawDate: e.DrawDate, MinimumPurchase: e.MinimumPurchase, DrawCompleted: e.DrawCompleted, DrawCompletedAt: e.DrawCompletedAt, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, } } func (p *UndianPrizeDB) ToResponse() UndianPrizeResponse { return UndianPrizeResponse{ ID: p.ID, UndianEventID: p.UndianEventID, Rank: p.Rank, PrizeName: p.PrizeName, PrizeValue: p.PrizeValue, PrizeDescription: p.PrizeDescription, PrizeType: p.PrizeType, PrizeImageURL: p.PrizeImageURL, WinningVoucherID: p.WinningVoucherID, WinnerUserID: p.WinnerUserID, } } func (v *UndianVoucherDB) ToResponse() UndianVoucherResponse { return UndianVoucherResponse{ ID: v.ID, UndianEventID: v.UndianEventID, CustomerID: v.CustomerID, OrderID: v.OrderID, VoucherCode: v.VoucherCode, VoucherNumber: v.VoucherNumber, IsWinner: v.IsWinner, PrizeRank: v.PrizeRank, WonAt: v.WonAt, CreatedAt: v.CreatedAt, } } // Helper method to convert slice of DB entities to response entities func UndianEventsToResponse(events []*UndianEventDB) []UndianEventResponse { responses := make([]UndianEventResponse, len(events)) for i, event := range events { responses[i] = event.ToResponse() } return responses } func UndianPrizesToResponse(prizes []*UndianPrizeDB) []UndianPrizeResponse { responses := make([]UndianPrizeResponse, len(prizes)) for i, prize := range prizes { responses[i] = prize.ToResponse() } return responses } func UndianVouchersToResponse(vouchers []*UndianVoucherDB) []UndianVoucherResponse { responses := make([]UndianVoucherResponse, len(vouchers)) for i, voucher := range vouchers { responses[i] = voucher.ToResponse() } return responses } // ============================================= // VALIDATION HELPERS // ============================================= func (r *CreateUndianEventRequest) Validate() error { if r.StartDate.After(r.EndDate) { return ErrInvalidDateRange } if r.EndDate.After(r.DrawDate) { return ErrInvalidDateRange } if r.MinimumPurchase < 0 { return UndianError{ Code: "INVALID_MINIMUM_PURCHASE", Message: "Minimum purchase must be greater than or equal to 0", } } return nil } func (r *UpdateUndianEventRequest) Validate() error { if r.StartDate.After(r.EndDate) { return ErrInvalidDateRange } if r.EndDate.After(r.DrawDate) { return ErrInvalidDateRange } if r.MinimumPurchase < 0 { return UndianError{ Code: "INVALID_MINIMUM_PURCHASE", Message: "Minimum purchase must be greater than or equal to 0", } } return nil } // ============================================= // STATUS CONSTANTS // ============================================= const ( UndianStatusUpcoming = "upcoming" UndianStatusActive = "active" UndianStatusCompleted = "completed" UndianStatusCancelled = "cancelled" ) const ( PrizeTypeGold = "gold" PrizeTypeVoucher = "voucher" PrizeTypeCash = "cash" PrizeTypeProduct = "product" PrizeTypeService = "service" )