-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Expand file tree
/
Copy pathconcert_booking_system.go
More file actions
131 lines (111 loc) · 2.97 KB
/
concert_booking_system.go
File metadata and controls
131 lines (111 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package concertbookingsystem
import (
"fmt"
"sync"
"time"
)
type ConcertTicketBookingSystem struct {
concerts map[string]*Concert
bookings map[string]*Booking
mu sync.Mutex
}
var (
instance *ConcertTicketBookingSystem
once sync.Once
)
func GetBookingSystem() *ConcertTicketBookingSystem {
once.Do(func() {
instance = &ConcertTicketBookingSystem{
concerts: make(map[string]*Concert),
bookings: make(map[string]*Booking),
}
})
return instance
}
func (bs *ConcertTicketBookingSystem) AddConcert(concert *Concert) {
bs.mu.Lock()
defer bs.mu.Unlock()
bs.concerts[concert.ID] = concert
}
func (bs *ConcertTicketBookingSystem) GetConcert(concertID string) *Concert {
bs.mu.Lock()
defer bs.mu.Unlock()
return bs.concerts[concertID]
}
func (bs *ConcertTicketBookingSystem) SearchConcerts(artist, venue string, dateTime time.Time) []*Concert {
bs.mu.Lock()
defer bs.mu.Unlock()
var results []*Concert
for _, concert := range bs.concerts {
if concert.Artist == artist &&
concert.Venue == venue &&
concert.DateTime.Equal(dateTime) {
results = append(results, concert)
}
}
return results
}
func (bs *ConcertTicketBookingSystem) BookTickets(user *User, concert *Concert, seats []*Seat) (*Booking, error) {
bs.mu.Lock()
defer bs.mu.Unlock()
// Check seat availability
for _, seat := range seats {
if seat.GetStatus() != StatusAvailable {
return nil, NewSeatNotAvailableError(fmt.Sprintf("Seat %s is not available", seat.SeatNumber))
}
}
// Hold seats for the user
for _, seat := range seats {
if err := seat.Hold(); err != nil {
// Rollback previous bookings
for _, s := range seats {
if s == seat {
break
}
s.Release()
}
return nil, err
}
concert.LockManager.AddSeatLock(seat, 5*time.Minute)
}
// Create booking
bookingID := fmt.Sprintf("BKG-%d", time.Now().UnixNano())
booking := NewBooking(bookingID, user, concert, seats)
// Process payment (mock)
bs.processPayment(booking)
// Confirm booking
err := booking.ConfirmBooking()
if err != nil {
// Rollback seat bookings to reserved, ensuring failures do not free seats, so they can be retried
for _, seat := range seats {
seat.status = StatusReserved
}
return nil, err
}
bs.bookings[bookingID] = booking
fmt.Printf("Booking %s - %d seats booked\n", booking.ID, len(booking.Seats))
return booking, nil
}
func (bs *ConcertTicketBookingSystem) CancelBooking(bookingID string) {
bs.mu.Lock()
defer bs.mu.Unlock()
if booking, exists := bs.bookings[bookingID]; exists {
booking.CancelBooking()
delete(bs.bookings, bookingID)
fmt.Printf("Booking %s cancelled\n", bookingID)
}
}
func (bs *ConcertTicketBookingSystem) processPayment(booking *Booking) {
// Mock payment processing
}
func (bs *ConcertTicketBookingSystem) StartLockReleaser(concertId string) {
go func() {
ticker := time.NewTicker(time.Second)
for range ticker.C {
concert := bs.concerts[concertId]
if concert != nil {
concert.LockManager.ReleaseExpiredLocks()
}
}
}()
}