2025-11-17 12:17:44 +07:00

152 lines
5.0 KiB
Go

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,
}
}