package repository import ( "gorm.io/gorm" "wails-app/internal/dao" "wails-app/internal/global" ) type TicketRepository struct{} func (r *TicketRepository) Create(ticket *dao.DigitalTicket) error { return r.CreateTx(global.GVA_DB, ticket) } func (r *TicketRepository) CreateTx(db *gorm.DB, ticket *dao.DigitalTicket) error { return db.Create(ticket).Error } func (r *TicketRepository) GetByTicketNo(ticketNo string) (*dao.DigitalTicket, error) { var t dao.DigitalTicket err := global.GVA_DB.Where("ticket_no = ?", ticketNo).First(&t).Error if err != nil { return nil, err } return &t, nil } func (r *TicketRepository) GetByID(id uint) (*dao.DigitalTicket, error) { return r.GetByIDTx(global.GVA_DB, id) } func (r *TicketRepository) GetByIDTx(db *gorm.DB, id uint) (*dao.DigitalTicket, error) { var t dao.DigitalTicket err := db.First(&t, id).Error if err != nil { return nil, err } return &t, nil } func (r *TicketRepository) UpdateState(id uint, state string, updates map[string]interface{}) error { return r.UpdateStateTx(global.GVA_DB, id, state, updates) } func (r *TicketRepository) GetByVehicleRecordID(recordID uint) (*dao.DigitalTicket, error) { return r.GetByVehicleRecordIDTx(global.GVA_DB, recordID) } func (r *TicketRepository) GetByVehicleRecordIDTx(db *gorm.DB, recordID uint) (*dao.DigitalTicket, error) { var t dao.DigitalTicket err := db.Where("vehicle_record_id = ?", recordID).Order("id DESC").First(&t).Error if err != nil { return nil, err } return &t, nil } func (r *TicketRepository) UpdateStateTx(db *gorm.DB, id uint, state string, updates map[string]interface{}) error { updates["state"] = state return db.Model(&dao.DigitalTicket{}).Where("id = ?", id).Updates(updates).Error } type TicketListQuery struct { PlateNumber string `form:"plate_number"` State string `form:"state"` Page int `form:"page"` PageSize int `form:"page_size"` } type TicketSummary struct { Total int64 `json:"total"` Pending int64 `json:"pending"` Paid int64 `json:"paid"` Exited int64 `json:"exited"` Expired int64 `json:"expired"` } func (r *TicketRepository) List(q TicketListQuery) ([]dao.DigitalTicket, int64, error) { db := ticketQuery(q, true) var total int64 db.Count(&total) if q.Page <= 0 { q.Page = 1 } if q.PageSize <= 0 { q.PageSize = 10 } var list []dao.DigitalTicket err := db.Order("digital_ticket.id DESC").Offset((q.Page - 1) * q.PageSize).Limit(q.PageSize).Find(&list).Error return list, total, err } // Summary returns counts for the complete filtered result set. Pagination and // the selected state tab are intentionally ignored so the dashboard totals do // not change when the user moves between pages. func (r *TicketRepository) Summary(q TicketListQuery) (TicketSummary, error) { db := ticketQuery(q, false) var rows []struct { State string Count int64 } if err := db.Select("digital_ticket.state, COUNT(*) AS count").Group("digital_ticket.state").Scan(&rows).Error; err != nil { return TicketSummary{}, err } var summary TicketSummary for _, row := range rows { summary.Total += row.Count switch row.State { case "created", "pending_payment": summary.Pending += row.Count case "paid": summary.Paid += row.Count case "exited": summary.Exited += row.Count case "expired": summary.Expired += row.Count } } return summary, nil } // ticketQuery applies filters shared by the paged list and the unpaged // summary. A left join keeps old tickets searchable even when their copied // plate_number column is empty and the vehicle record still has the plate. func ticketQuery(q TicketListQuery, includeState bool) *gorm.DB { db := global.GVA_DB.Model(&dao.DigitalTicket{}) if q.PlateNumber != "" { like := "%" + q.PlateNumber + "%" db = db.Joins("LEFT JOIN vehicle_record ON vehicle_record.id = digital_ticket.vehicle_record_id"). Where("(digital_ticket.plate_number LIKE ? OR vehicle_record.plate_number LIKE ?)", like, like) } if includeState && q.State != "" { if q.State == "pending_payment" { db = db.Where("digital_ticket.state IN ?", []string{"created", "pending_payment"}) } else { db = db.Where("digital_ticket.state = ?", q.State) } } return db }