feat: add postgres repository for training
This commit is contained in:
parent
8c277ef692
commit
ff7e320481
19 changed files with 1277 additions and 414 deletions
270
pkg/training/inmemory_repository.go
Normal file
270
pkg/training/inmemory_repository.go
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
package training
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InMemoryTrainingRepository struct {
|
||||
trainings map[TrainingID]Training
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingRepository() *InMemoryTrainingRepository {
|
||||
return &InMemoryTrainingRepository{
|
||||
trainings: make(map[TrainingID]Training),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Create(training *Training) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
training.ID = NewTrainingID()
|
||||
r.trainings[training.ID] = *training
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) FindByID(id TrainingID) (*Training, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
training, ok := r.trainings[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingNotFound
|
||||
}
|
||||
return &training, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) FindAll() ([]Training, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
trainings := make([]Training, 0, len(r.trainings))
|
||||
for _, training := range r.trainings {
|
||||
trainings = append(trainings, training)
|
||||
}
|
||||
return trainings, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Update(training *Training) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
r.trainings[training.ID] = *training
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Delete(id TrainingID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
_, ok := r.trainings[id]
|
||||
if !ok {
|
||||
return ErrTrainingNotFound
|
||||
}
|
||||
|
||||
delete(r.trainings, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
type InMemoryTrainingDateRepository struct {
|
||||
trainingDates map[TrainingDateID]TrainingDate
|
||||
trainingToDates map[TrainingID][]TrainingDateID
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingDateRepository() *InMemoryTrainingDateRepository {
|
||||
return &InMemoryTrainingDateRepository{
|
||||
trainingDates: make(map[TrainingDateID]TrainingDate),
|
||||
trainingToDates: make(map[TrainingID][]TrainingDateID),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Create(trainingID TrainingID, trainingDate *TrainingDate) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
trainingDate.ID = NewTrainingDateID()
|
||||
trainingDate.trainingID = trainingID
|
||||
|
||||
r.trainingDates[trainingDate.ID] = *trainingDate
|
||||
r.trainingToDates[trainingID] = append(r.trainingToDates[trainingID], trainingDate.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindByID(id TrainingDateID) (*TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
date, ok := r.trainingDates[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingDateNotFound
|
||||
}
|
||||
return &date, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindAll() ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
dates := make([]TrainingDate, len(r.trainingDates))
|
||||
for _, date := range r.trainingDates {
|
||||
dates = append(dates, date)
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindAllByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
dates := make([]TrainingDate, 0)
|
||||
for _, id := range r.trainingToDates[trainingID] {
|
||||
dates = append(dates, r.trainingDates[id])
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindUpcomingByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
now := time.Now()
|
||||
var dates []TrainingDate
|
||||
for _, id := range r.trainingToDates[trainingID] {
|
||||
date := r.trainingDates[id]
|
||||
if date.Date.After(now) {
|
||||
dates = append(dates, date)
|
||||
}
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Update(trainingDate *TrainingDate) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
oldDate := r.trainingDates[trainingDate.ID]
|
||||
trainingDate.trainingID = oldDate.trainingID
|
||||
r.trainingDates[trainingDate.ID] = *trainingDate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Delete(id TrainingDateID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
date, ok := r.trainingDates[id]
|
||||
if !ok {
|
||||
return ErrTrainingDateNotFound
|
||||
}
|
||||
|
||||
delete(r.trainingDates, id)
|
||||
dates := r.trainingToDates[date.trainingID]
|
||||
for idx, d := range dates {
|
||||
if d == id {
|
||||
r.trainingToDates[date.trainingID] = append(dates[:idx], dates[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type InMemoryTrainingDateAttendeeRepository struct {
|
||||
attendees map[TrainingDateAttendeeID]TrainingDateAttendee
|
||||
dateToAttendees map[TrainingDateID][]TrainingDateAttendeeID
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingDateAttendeeRepository() *InMemoryTrainingDateAttendeeRepository {
|
||||
return &InMemoryTrainingDateAttendeeRepository{
|
||||
attendees: make(map[TrainingDateAttendeeID]TrainingDateAttendee),
|
||||
dateToAttendees: make(map[TrainingDateID][]TrainingDateAttendeeID),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Create(trainingDateID TrainingDateID, attendee *TrainingDateAttendee) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
attendee.ID = NewTrainingDateAttendeeID()
|
||||
attendee.trainingDateID = trainingDateID
|
||||
|
||||
r.attendees[attendee.ID] = *attendee
|
||||
r.dateToAttendees[trainingDateID] = append(r.dateToAttendees[trainingDateID], attendee.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindByID(id TrainingDateAttendeeID) (*TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendee, ok := r.attendees[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingDateAttendeeNotFound
|
||||
}
|
||||
return &attendee, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindAll() ([]TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendees := make([]TrainingDateAttendee, len(r.attendees))
|
||||
for _, attendee := range r.attendees {
|
||||
attendees = append(attendees, attendee)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindAllByTrainingDateID(trainingDateID TrainingDateID) ([]TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendees := make([]TrainingDateAttendee, 0)
|
||||
for _, id := range r.dateToAttendees[trainingDateID] {
|
||||
attendees = append(attendees, r.attendees[id])
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Update(attendee *TrainingDateAttendee) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
oldAttendee := r.attendees[attendee.ID]
|
||||
attendee.trainingDateID = oldAttendee.trainingDateID
|
||||
r.attendees[attendee.ID] = *attendee
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Delete(id TrainingDateAttendeeID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
attendee, ok := r.attendees[id]
|
||||
if !ok {
|
||||
return ErrTrainingDateAttendeeNotFound
|
||||
}
|
||||
|
||||
delete(r.attendees, id)
|
||||
attendees := r.dateToAttendees[attendee.trainingDateID]
|
||||
for idx, a := range attendees {
|
||||
if a == id {
|
||||
r.dateToAttendees[attendee.trainingDateID] = append(attendees[:idx], attendees[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package training
|
||||
|
||||
import (
|
||||
"github.com/shopspring/decimal"
|
||||
"gitlab.mareshq.com/hq/yggdrasil/internal/money"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
"gitlab.mareshq.com/hq/yggdrasil/internal/money"
|
||||
)
|
||||
|
||||
type TrainingID = uuid.UUID
|
||||
|
|
@ -19,7 +19,7 @@ type Training struct {
|
|||
Name string
|
||||
Days int8
|
||||
Description string
|
||||
Pricing []TrainingPrice
|
||||
Pricing []TrainingPrice `db:"-"`
|
||||
}
|
||||
|
||||
type TrainingPrice struct {
|
||||
|
|
@ -44,15 +44,16 @@ func NewTrainingDateID() TrainingDateID {
|
|||
type TrainingDate struct {
|
||||
trainingID TrainingID
|
||||
|
||||
ID TrainingDateID
|
||||
Date time.Time
|
||||
StartTime time.Time
|
||||
Days int8
|
||||
IsOnline bool
|
||||
Location string
|
||||
Address string
|
||||
Capacity int8
|
||||
Price money.Price
|
||||
ID TrainingDateID
|
||||
Date time.Time
|
||||
StartTime time.Time
|
||||
Days int8
|
||||
IsOnline bool
|
||||
Location string
|
||||
Address string
|
||||
Capacity int8
|
||||
PriceAmount decimal.Decimal `db:"price_amount"`
|
||||
PriceCurrency money.Currency `db:"price_currency"`
|
||||
}
|
||||
|
||||
type TrainingDateAttendeeID = uuid.UUID
|
||||
|
|
@ -64,14 +65,15 @@ func NewTrainingDateAttendeeID() TrainingDateAttendeeID {
|
|||
type TrainingDateAttendee struct {
|
||||
trainingDateID TrainingDateID
|
||||
|
||||
ID TrainingDateAttendeeID
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
Company string
|
||||
Position string
|
||||
Bill money.Price
|
||||
IsStudent bool
|
||||
HasPaid bool
|
||||
HasAttended bool
|
||||
ID TrainingDateAttendeeID
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
Company string
|
||||
Position string
|
||||
IsStudent bool
|
||||
HasPaid bool
|
||||
HasAttended bool
|
||||
BillAmount decimal.Decimal `db:"bill_amount"`
|
||||
BillCurrency money.Currency `db:"bill_currency"`
|
||||
}
|
||||
|
|
|
|||
561
pkg/training/postgres_repository.go
Normal file
561
pkg/training/postgres_repository.go
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
package training
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PostgresTrainingRepository struct {
|
||||
pg *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresTrainingRepository(pg *pgxpool.Pool) *PostgresTrainingRepository {
|
||||
return &PostgresTrainingRepository{pg: pg}
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingRepository) Create(training *Training) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
training.ID = NewTrainingID()
|
||||
|
||||
tx, err := r.pg.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO training.trainings (id, name, description, days)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`, training.ID, training.Name, training.Description, training.Days)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryBatch := &pgx.Batch{}
|
||||
|
||||
for _, price := range training.Pricing {
|
||||
queryBatch.Queue(`
|
||||
INSERT INTO training.prices (training_id, amount, currency, type)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`, training.ID, price.Amount, price.Currency, price.Type)
|
||||
}
|
||||
|
||||
batch := tx.SendBatch(ctx, queryBatch)
|
||||
defer func() {
|
||||
if e := batch.Close(); e != nil {
|
||||
err = e
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
} else {
|
||||
if e := tx.Commit(ctx); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = batch.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingRepository) FindByID(id TrainingID) (*Training, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var training Training
|
||||
err := r.pg.QueryRow(ctx, `
|
||||
SELECT id, name, description, days
|
||||
FROM training.trainings
|
||||
WHERE id = $1
|
||||
`, id).Scan(&training.ID, &training.Name, &training.Description, &training.Days)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := r.pg.Query(ctx, `
|
||||
SELECT amount, currency, type
|
||||
FROM training.prices
|
||||
WHERE training_id = $1
|
||||
`, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
training.Pricing = make([]TrainingPrice, 0)
|
||||
training.Pricing, err = pgx.CollectRows[TrainingPrice](rows, func(row pgx.CollectableRow) (TrainingPrice, error) {
|
||||
var price TrainingPrice
|
||||
err := row.Scan(&price.Amount, &price.Currency, &price.Type)
|
||||
if err != nil {
|
||||
return TrainingPrice{}, err
|
||||
}
|
||||
return price, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &training, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingRepository) FindAll() ([]Training, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `
|
||||
SELECT *
|
||||
FROM training.trainings
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
trainings, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (Training, error) {
|
||||
var training Training
|
||||
scanErr := row.Scan(&training.ID, &training.Name, &training.Description, &training.Days)
|
||||
if scanErr != nil {
|
||||
return Training{}, scanErr
|
||||
}
|
||||
|
||||
priceRows, queryErr := r.pg.Query(ctx, `SELECT amount, currency, type FROM training.prices WHERE training_id = $1`, training.ID)
|
||||
if queryErr != nil {
|
||||
return Training{}, queryErr
|
||||
}
|
||||
|
||||
training.Pricing, scanErr = pgx.CollectRows(priceRows, pgx.RowToStructByName[TrainingPrice])
|
||||
if scanErr != nil {
|
||||
return Training{}, scanErr
|
||||
}
|
||||
|
||||
return training, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return trainings, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingRepository) Update(training *Training) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
tx, err := r.pg.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, `
|
||||
UPDATE training.trainings
|
||||
SET name = $1, description = $2, days = $3
|
||||
WHERE id = $4
|
||||
`, training.Name, training.Description, training.Days, training.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryBatch := &pgx.Batch{}
|
||||
|
||||
for _, price := range training.Pricing {
|
||||
queryBatch.Queue(`
|
||||
INSERT INTO training.prices (training_id, amount, currency, type)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`, training.ID, price.Amount, price.Currency, price.Type)
|
||||
}
|
||||
|
||||
batch := tx.SendBatch(ctx, queryBatch)
|
||||
defer func() {
|
||||
if e := batch.Close(); e != nil {
|
||||
err = e
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
} else {
|
||||
if e := tx.Commit(ctx); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = batch.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingRepository) Delete(id TrainingID) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
tx, err := r.pg.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(context.Background(), `
|
||||
DELETE FROM training.prices
|
||||
WHERE training_id = $1
|
||||
`, id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(context.Background(), `
|
||||
DELETE FROM training.trainings
|
||||
WHERE id = $1
|
||||
`, id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostgresTrainingDateRepository struct {
|
||||
pg *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresTrainingDateRepository(pg *pgxpool.Pool) *PostgresTrainingDateRepository {
|
||||
return &PostgresTrainingDateRepository{pg: pg}
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) Create(trainingID TrainingID, trainingDate *TrainingDate) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
trainingDate.ID = NewTrainingDateID()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `
|
||||
INSERT INTO training.dates (id, training_id, date, start_time, days, is_online, location, address, capacity, price_amount, price_currency)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
||||
trainingDate.ID,
|
||||
trainingID,
|
||||
trainingDate.Date,
|
||||
trainingDate.StartTime,
|
||||
trainingDate.Days,
|
||||
trainingDate.IsOnline,
|
||||
trainingDate.Location,
|
||||
trainingDate.Address,
|
||||
trainingDate.Capacity,
|
||||
trainingDate.PriceAmount,
|
||||
trainingDate.PriceCurrency,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) FindByID(id TrainingDateID) (*TrainingDate, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var trainingDate TrainingDate
|
||||
err := r.pg.QueryRow(ctx, `SELECT * FROM training.dates WHERE id = $1`, id).
|
||||
Scan(
|
||||
&trainingDate.ID,
|
||||
&trainingDate.trainingID,
|
||||
&trainingDate.Date,
|
||||
&trainingDate.StartTime,
|
||||
&trainingDate.Days,
|
||||
&trainingDate.IsOnline,
|
||||
&trainingDate.Location,
|
||||
&trainingDate.Address,
|
||||
&trainingDate.Capacity,
|
||||
&trainingDate.PriceAmount,
|
||||
&trainingDate.PriceCurrency,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &trainingDate, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) FindAll() ([]TrainingDate, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `SELECT * FROM training.dates`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var trainingDates []TrainingDate
|
||||
for rows.Next() {
|
||||
var trainingDate TrainingDate
|
||||
err := rows.Scan(
|
||||
&trainingDate.ID,
|
||||
&trainingDate.trainingID,
|
||||
&trainingDate.Date,
|
||||
&trainingDate.StartTime,
|
||||
&trainingDate.Days,
|
||||
&trainingDate.IsOnline,
|
||||
&trainingDate.Location,
|
||||
&trainingDate.Address,
|
||||
&trainingDate.Capacity,
|
||||
&trainingDate.PriceAmount,
|
||||
&trainingDate.PriceCurrency,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trainingDates = append(trainingDates, trainingDate)
|
||||
}
|
||||
|
||||
return trainingDates, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) FindAllByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `SELECT * FROM training.dates WHERE training_id = $1`, trainingID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var trainingDates []TrainingDate
|
||||
for rows.Next() {
|
||||
var trainingDate TrainingDate
|
||||
err := rows.Scan(
|
||||
&trainingDate.ID,
|
||||
&trainingDate.trainingID,
|
||||
&trainingDate.Date,
|
||||
&trainingDate.StartTime,
|
||||
&trainingDate.Days,
|
||||
&trainingDate.IsOnline,
|
||||
&trainingDate.Location,
|
||||
&trainingDate.Address,
|
||||
&trainingDate.Capacity,
|
||||
&trainingDate.PriceAmount,
|
||||
&trainingDate.PriceCurrency,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trainingDates = append(trainingDates, trainingDate)
|
||||
}
|
||||
|
||||
return trainingDates, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) FindUpcomingByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `
|
||||
SELECT * FROM training.dates
|
||||
WHERE training_id = $1 AND date > $2
|
||||
`, trainingID, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var trainingDates []TrainingDate
|
||||
for rows.Next() {
|
||||
var trainingDate TrainingDate
|
||||
err := rows.Scan(
|
||||
&trainingDate.ID,
|
||||
&trainingDate.trainingID,
|
||||
&trainingDate.Date,
|
||||
&trainingDate.StartTime,
|
||||
&trainingDate.Days,
|
||||
&trainingDate.IsOnline,
|
||||
&trainingDate.Location,
|
||||
&trainingDate.Address,
|
||||
&trainingDate.Capacity,
|
||||
&trainingDate.PriceAmount,
|
||||
&trainingDate.PriceCurrency,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trainingDates = append(trainingDates, trainingDate)
|
||||
}
|
||||
|
||||
return trainingDates, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) Update(trainingDate *TrainingDate) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `
|
||||
UPDATE training.dates
|
||||
SET date = $1, start_time = $2, days = $3, is_online = $4, location = $5, address = $6, capacity = $7, price_amount = $8, price_currency = $9
|
||||
WHERE id = $10
|
||||
`, trainingDate.Date, trainingDate.StartTime, trainingDate.Days, trainingDate.IsOnline, trainingDate.Location, trainingDate.Address, trainingDate.Capacity, trainingDate.PriceAmount, trainingDate.PriceCurrency, trainingDate.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateRepository) Delete(id TrainingDateID) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `DELETE FROM training.dates WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostgresTrainingDateAttendeeRepository struct {
|
||||
pg *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresTrainingDateAttendeeRepository(pg *pgxpool.Pool) *PostgresTrainingDateAttendeeRepository {
|
||||
return &PostgresTrainingDateAttendeeRepository{pg: pg}
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) Create(trainingDateID TrainingDateID, attendee *TrainingDateAttendee) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
attendee.ID = NewTrainingDateAttendeeID()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `
|
||||
INSERT INTO training.date_attendees (id, date_id, name, email, phone, company, position, is_student, has_paid, has_attended, bill_amount, bill_currency)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
`, attendee.ID, trainingDateID, attendee.Name, attendee.Email, attendee.Phone, attendee.Company, attendee.Position, attendee.IsStudent, attendee.HasPaid, attendee.HasAttended, attendee.BillAmount, attendee.BillCurrency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) FindByID(id TrainingDateAttendeeID) (*TrainingDateAttendee, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var attendee TrainingDateAttendee
|
||||
err := r.pg.QueryRow(ctx, `
|
||||
SELECT id, date_id, name, email, phone, company, position, is_student, has_paid, has_attended, bill_amount, bill_currency
|
||||
FROM training.date_attendees
|
||||
WHERE id = $1
|
||||
`, id).Scan(&attendee.ID, &attendee.trainingDateID, &attendee.Name, &attendee.Email, &attendee.Phone, &attendee.Company, &attendee.Position, &attendee.IsStudent, &attendee.HasPaid, &attendee.HasAttended, &attendee.BillAmount, &attendee.BillCurrency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &attendee, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) FindAll() ([]TrainingDateAttendee, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `
|
||||
SELECT id, date_id, name, email, phone, company, position, is_student, has_paid, has_attended, bill_amount, bill_currency
|
||||
FROM training.date_attendees
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var attendees []TrainingDateAttendee
|
||||
for rows.Next() {
|
||||
var attendee TrainingDateAttendee
|
||||
err := rows.Scan(&attendee.ID, &attendee.trainingDateID, &attendee.Name, &attendee.Email, &attendee.Phone, &attendee.Company, &attendee.Position, &attendee.IsStudent, &attendee.HasPaid, &attendee.HasAttended, &attendee.BillAmount, &attendee.BillCurrency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attendees = append(attendees, attendee)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) FindAllByTrainingDateID(trainingDateID TrainingDateID) ([]TrainingDateAttendee, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := r.pg.Query(ctx, `
|
||||
SELECT id, date_id, name, email, phone, company, position, is_student, has_paid, has_attended, bill_amount, bill_currency
|
||||
FROM training.date_attendees
|
||||
WHERE date_id = $1
|
||||
`, trainingDateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var attendees []TrainingDateAttendee
|
||||
for rows.Next() {
|
||||
var attendee TrainingDateAttendee
|
||||
err := rows.Scan(&attendee.ID, &attendee.trainingDateID, &attendee.Name, &attendee.Email, &attendee.Phone, &attendee.Company, &attendee.Position, &attendee.IsStudent, &attendee.HasPaid, &attendee.HasAttended, &attendee.BillAmount, &attendee.BillCurrency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attendees = append(attendees, attendee)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) Update(attendee *TrainingDateAttendee) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `
|
||||
UPDATE training.date_attendees
|
||||
SET name = $1, email = $2, phone = $3, company = $4, position = $5, is_student = $6, has_paid = $7, has_attended = $8, bill_amount = $9, bill_currency = $10
|
||||
WHERE id = $10
|
||||
`, attendee.Name, attendee.Email, attendee.Phone, attendee.Company, attendee.Position, attendee.IsStudent, attendee.HasPaid, attendee.HasAttended, attendee.BillAmount, attendee.BillCurrency, attendee.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresTrainingDateAttendeeRepository) Delete(id TrainingDateAttendeeID) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := r.pg.Exec(ctx, `DELETE FROM training.date_attendees WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
package training
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrainingRepository interface {
|
||||
Create(training *Training) error
|
||||
FindByID(id TrainingID) (*Training, error)
|
||||
|
|
@ -13,69 +8,6 @@ type TrainingRepository interface {
|
|||
Delete(id TrainingID) error
|
||||
}
|
||||
|
||||
type InMemoryTrainingRepository struct {
|
||||
trainings map[TrainingID]Training
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingRepository() *InMemoryTrainingRepository {
|
||||
return &InMemoryTrainingRepository{
|
||||
trainings: make(map[TrainingID]Training),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Create(training *Training) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
training.ID = NewTrainingID()
|
||||
r.trainings[training.ID] = *training
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) FindByID(id TrainingID) (*Training, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
training, ok := r.trainings[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingNotFound
|
||||
}
|
||||
return &training, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) FindAll() ([]Training, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
trainings := make([]Training, 0, len(r.trainings))
|
||||
for _, training := range r.trainings {
|
||||
trainings = append(trainings, training)
|
||||
}
|
||||
return trainings, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Update(training *Training) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
r.trainings[training.ID] = *training
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingRepository) Delete(id TrainingID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
_, ok := r.trainings[id]
|
||||
if !ok {
|
||||
return ErrTrainingNotFound
|
||||
}
|
||||
|
||||
delete(r.trainings, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
type TrainingDateRepository interface {
|
||||
Create(trainingID TrainingID, trainingDate *TrainingDate) error
|
||||
FindByID(id TrainingDateID) (*TrainingDate, error)
|
||||
|
|
@ -86,114 +18,6 @@ type TrainingDateRepository interface {
|
|||
Delete(id TrainingDateID) error
|
||||
}
|
||||
|
||||
type InMemoryTrainingDateRepository struct {
|
||||
trainingDates map[TrainingDateID]TrainingDate
|
||||
trainingToDates map[TrainingID][]TrainingDateID
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingDateRepository() *InMemoryTrainingDateRepository {
|
||||
return &InMemoryTrainingDateRepository{
|
||||
trainingDates: make(map[TrainingDateID]TrainingDate),
|
||||
trainingToDates: make(map[TrainingID][]TrainingDateID),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Create(trainingID TrainingID, trainingDate *TrainingDate) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
trainingDate.ID = NewTrainingDateID()
|
||||
trainingDate.trainingID = trainingID
|
||||
|
||||
r.trainingDates[trainingDate.ID] = *trainingDate
|
||||
r.trainingToDates[trainingID] = append(r.trainingToDates[trainingID], trainingDate.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindByID(id TrainingDateID) (*TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
date, ok := r.trainingDates[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingDateNotFound
|
||||
}
|
||||
return &date, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindAll() ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
dates := make([]TrainingDate, len(r.trainingDates))
|
||||
for _, date := range r.trainingDates {
|
||||
dates = append(dates, date)
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindAllByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
dates := make([]TrainingDate, 0)
|
||||
for _, id := range r.trainingToDates[trainingID] {
|
||||
dates = append(dates, r.trainingDates[id])
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) FindUpcomingByTrainingID(trainingID TrainingID) ([]TrainingDate, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
now := time.Now()
|
||||
var dates []TrainingDate
|
||||
for _, id := range r.trainingToDates[trainingID] {
|
||||
date := r.trainingDates[id]
|
||||
if date.Date.After(now) {
|
||||
dates = append(dates, date)
|
||||
}
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Update(trainingDate *TrainingDate) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
oldDate := r.trainingDates[trainingDate.ID]
|
||||
trainingDate.trainingID = oldDate.trainingID
|
||||
r.trainingDates[trainingDate.ID] = *trainingDate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateRepository) Delete(id TrainingDateID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
date, ok := r.trainingDates[id]
|
||||
if !ok {
|
||||
return ErrTrainingDateNotFound
|
||||
}
|
||||
|
||||
delete(r.trainingDates, id)
|
||||
dates := r.trainingToDates[date.trainingID]
|
||||
for idx, d := range dates {
|
||||
if d == id {
|
||||
r.trainingToDates[date.trainingID] = append(dates[:idx], dates[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TrainingDateAttendeeRepository interface {
|
||||
Create(trainingDateID TrainingDateID, attendee *TrainingDateAttendee) error
|
||||
FindByID(id TrainingDateAttendeeID) (*TrainingDateAttendee, error)
|
||||
|
|
@ -202,96 +26,3 @@ type TrainingDateAttendeeRepository interface {
|
|||
Update(attendee *TrainingDateAttendee) error
|
||||
Delete(id TrainingDateAttendeeID) error
|
||||
}
|
||||
|
||||
type InMemoryTrainingDateAttendeeRepository struct {
|
||||
attendees map[TrainingDateAttendeeID]TrainingDateAttendee
|
||||
dateToAttendees map[TrainingDateID][]TrainingDateAttendeeID
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewInMemoryTrainingDateAttendeeRepository() *InMemoryTrainingDateAttendeeRepository {
|
||||
return &InMemoryTrainingDateAttendeeRepository{
|
||||
attendees: make(map[TrainingDateAttendeeID]TrainingDateAttendee),
|
||||
dateToAttendees: make(map[TrainingDateID][]TrainingDateAttendeeID),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Create(trainingDateID TrainingDateID, attendee *TrainingDateAttendee) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
attendee.ID = NewTrainingDateAttendeeID()
|
||||
attendee.trainingDateID = trainingDateID
|
||||
|
||||
r.attendees[attendee.ID] = *attendee
|
||||
r.dateToAttendees[trainingDateID] = append(r.dateToAttendees[trainingDateID], attendee.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindByID(id TrainingDateAttendeeID) (*TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendee, ok := r.attendees[id]
|
||||
if !ok {
|
||||
return nil, ErrTrainingDateAttendeeNotFound
|
||||
}
|
||||
return &attendee, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindAll() ([]TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendees := make([]TrainingDateAttendee, len(r.attendees))
|
||||
for _, attendee := range r.attendees {
|
||||
attendees = append(attendees, attendee)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) FindAllByTrainingDateID(trainingDateID TrainingDateID) ([]TrainingDateAttendee, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
attendees := make([]TrainingDateAttendee, 0)
|
||||
for _, id := range r.dateToAttendees[trainingDateID] {
|
||||
attendees = append(attendees, r.attendees[id])
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Update(attendee *TrainingDateAttendee) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
oldAttendee := r.attendees[attendee.ID]
|
||||
attendee.trainingDateID = oldAttendee.trainingDateID
|
||||
r.attendees[attendee.ID] = *attendee
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryTrainingDateAttendeeRepository) Delete(id TrainingDateAttendeeID) error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
attendee, ok := r.attendees[id]
|
||||
if !ok {
|
||||
return ErrTrainingDateAttendeeNotFound
|
||||
}
|
||||
|
||||
delete(r.attendees, id)
|
||||
attendees := r.dateToAttendees[attendee.trainingDateID]
|
||||
for idx, a := range attendees {
|
||||
if a == id {
|
||||
r.dateToAttendees[attendee.trainingDateID] = append(attendees[:idx], attendees[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue