package models import ( "time" "gorm.io/gorm" ) // Item represents a found item type Item struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"type:varchar(100);not null" json:"name"` CategoryID uint `gorm:"not null" json:"category_id"` Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` PhotoURL string `gorm:"type:varchar(255)" json:"photo_url"` Location string `gorm:"type:varchar(200);not null" json:"location"` Description string `gorm:"type:text;not null" json:"description"` // Keunikan (rahasia) DateFound time.Time `gorm:"not null" json:"date_found"` Status string `gorm:"type:varchar(50);default:'unclaimed'" json:"status"` // unclaimed, pending_claim, verified, case_closed, expired ReporterID uint `gorm:"not null" json:"reporter_id"` Reporter User `gorm:"foreignKey:ReporterID" json:"reporter,omitempty"` ReporterName string `gorm:"type:varchar(100);not null" json:"reporter_name"` ReporterContact string `gorm:"type:varchar(50);not null" json:"reporter_contact"` ExpiresAt *time.Time `json:"expires_at"` // Auto-expire after 90 days CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // Relationships Claims []Claim `gorm:"foreignKey:ItemID" json:"claims,omitempty"` MatchResults []MatchResult `gorm:"foreignKey:ItemID" json:"match_results,omitempty"` RevisionLogs []RevisionLog `gorm:"foreignKey:ItemID" json:"revision_logs,omitempty"` } // TableName specifies the table name for Item model func (Item) TableName() string { return "items" } // Status constants const ( ItemStatusUnclaimed = "unclaimed" ItemStatusPendingClaim = "pending_claim" ItemStatusVerified = "verified" ItemStatusCaseClosed = "case_closed" ItemStatusExpired = "expired" ) // BeforeCreate hook to set expiration date func (i *Item) BeforeCreate(tx *gorm.DB) error { // Set default status if i.Status == "" { i.Status = ItemStatusUnclaimed } // Set expiration date (90 days from date found) if i.ExpiresAt == nil { expiresAt := i.DateFound.AddDate(0, 0, 90) // Add 90 days i.ExpiresAt = &expiresAt } return nil } // IsExpired checks if item has expired func (i *Item) IsExpired() bool { if i.ExpiresAt == nil { return false } return time.Now().After(*i.ExpiresAt) } // CanBeClaimed checks if item can be claimed func (i *Item) CanBeClaimed() bool { return i.Status == ItemStatusUnclaimed && !i.IsExpired() } // CanBeEdited checks if item can be edited func (i *Item) CanBeEdited() bool { // Cannot edit if case is closed or expired return i.Status != ItemStatusCaseClosed && i.Status != ItemStatusExpired } // ItemPublicResponse represents item data for public view (without sensitive info) type ItemPublicResponse struct { ID uint `json:"id"` Name string `json:"name"` Category string `json:"category"` PhotoURL string `json:"photo_url"` Location string `json:"location"` DateFound time.Time `json:"date_found"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } // ToPublicResponse converts Item to ItemPublicResponse (hides description, reporter details) func (i *Item) ToPublicResponse() ItemPublicResponse { categoryName := "" if i.Category.ID != 0 { categoryName = i.Category.Name } return ItemPublicResponse{ ID: i.ID, Name: i.Name, Category: categoryName, PhotoURL: i.PhotoURL, Location: i.Location, DateFound: i.DateFound, Status: i.Status, CreatedAt: i.CreatedAt, } } // ItemDetailResponse represents full item data for authorized users type ItemDetailResponse struct { ID uint `json:"id"` Name string `json:"name"` Category string `json:"category"` PhotoURL string `json:"photo_url"` Location string `json:"location"` Description string `json:"description"` DateFound time.Time `json:"date_found"` Status string `json:"status"` ReporterName string `json:"reporter_name"` ReporterContact string `json:"reporter_contact"` ExpiresAt *time.Time `json:"expires_at"` CreatedAt time.Time `json:"created_at"` } // ToDetailResponse converts Item to ItemDetailResponse (includes all info) func (i *Item) ToDetailResponse() ItemDetailResponse { categoryName := "" if i.Category.ID != 0 { categoryName = i.Category.Name } return ItemDetailResponse{ ID: i.ID, Name: i.Name, Category: categoryName, PhotoURL: i.PhotoURL, Location: i.Location, Description: i.Description, DateFound: i.DateFound, Status: i.Status, ReporterName: i.ReporterName, ReporterContact: i.ReporterContact, ExpiresAt: i.ExpiresAt, CreatedAt: i.CreatedAt, } }