| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- 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) {
- return r.GetByTicketNoTx(global.GVA_DB, ticketNo)
- }
- func (r *TicketRepository) GetByTicketNoTx(db *gorm.DB, ticketNo string) (*dao.DigitalTicket, error) {
- var t dao.DigitalTicket
- err := 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
- }
- // UpdateStateIfCurrentTx changes a ticket only when it is still in the state
- // observed by the caller. This makes payment and exit transitions safe when
- // two requests race for the same ticket.
- func (r *TicketRepository) UpdateStateIfCurrentTx(db *gorm.DB, id uint, fromState, toState string, updates map[string]interface{}) (bool, error) {
- if updates == nil {
- updates = make(map[string]interface{})
- }
- updates["state"] = toState
- result := db.Model(&dao.DigitalTicket{}).
- Where("id = ? AND state = ?", id, fromState).
- Updates(updates)
- return result.RowsAffected == 1, result.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
- }
|