bet and ticket crud

This commit is contained in:
Samuel Tariku 2025-03-29 19:11:31 +03:00
parent 731343feef
commit 60887accd2
18 changed files with 469 additions and 36 deletions

View File

@ -73,6 +73,7 @@ DROP TYPE IF EXISTS ua_pin_status;
DROP TYPE IF EXISTS ua_status;
DROP TYPE IF EXISTS ua_registaration_type;
-- Drop Ticket
DROP TABLE IF EXIST ticket;
-- Drop FortuneBet
DROP TABLE IF EXIST tickets;
DROP TABLE IF EXIST bets;

View File

@ -7,14 +7,43 @@ CREATE TABLE users (
password TEXT NOT NULL,
role VARCHAR(50) NOT NULL,
verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP ,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bets (
id BIGSERIAL PRIMARY KEY,
amount BIGINT NOT NULL,
total_odds REAL NOT NULL,
status INT NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
branch_id BIGINT,
user_id BIGINT,
cashed_out BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
CHECK (user_id IS NOT NULL OR branch_id IS NOT NULL)
);
CREATE TABLE IF NOT EXISTS tickets (
id BIGSERIAL PRIMARY KEY,
amount DECIMAL(12,2) NULL,
total_odds DECIMAL(12,2) NOT NULL,
amount BIGINT NULL,
total_odds REAL NOT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- CREATE TABLE IF NOT EXISTS bet_outcomes (
-- id BIGSERIAL PRIMARY KEY,
-- bet_id BIGINT NOT NULL,
-- outcome_id BIGINT NOT NULL,
-- );
-- CREATE TABLE IF NOT EXISTS ticket_outcomes (
-- id BIGSERIAL PRIMARY KEY,
-- ticket_id BIGINT NOT NULL,
-- outcome_id BIGINT NOT NULL,
-- );

16
db/query/bet.sql Normal file
View File

@ -0,0 +1,16 @@
-- name: CreateBet :one
INSERT INTO bets (amount, total_odds, status, full_name, phone_number, branch_id, user_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *;
-- name: GetAllBets :many
SELECT * FROM bets;
-- name: GetBetByID :one
SELECT * FROM bets WHERE id = $1;
-- name: UpdateCashOut :exec
UPDATE bets SET cashed_out = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1;
-- name: DeleteBet :exec
DELETE FROM bets WHERE id = $1;

View File

@ -1,6 +1,6 @@
-- name: CreateTicket :one
INSERT INTO tickets (id, amount, total_odds)
VALUES ($1, $2, $3)
INSERT INTO tickets (amount, total_odds)
VALUES ($1, $2)
RETURNING *;
-- name: GetAllTickets :many

137
gen/db/bet.sql.go Normal file
View File

@ -0,0 +1,137 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
// source: bet.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateBet = `-- name: CreateBet :one
INSERT INTO bets (amount, total_odds, status, full_name, phone_number, branch_id, user_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, created_at, updated_at
`
type CreateBetParams struct {
Amount int64
TotalOdds float32
Status int32
FullName string
PhoneNumber string
BranchID pgtype.Int8
UserID pgtype.Int8
}
func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) {
row := q.db.QueryRow(ctx, CreateBet,
arg.Amount,
arg.TotalOdds,
arg.Status,
arg.FullName,
arg.PhoneNumber,
arg.BranchID,
arg.UserID,
)
var i Bet
err := row.Scan(
&i.ID,
&i.Amount,
&i.TotalOdds,
&i.Status,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.UserID,
&i.CashedOut,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const DeleteBet = `-- name: DeleteBet :exec
DELETE FROM bets WHERE id = $1
`
func (q *Queries) DeleteBet(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteBet, id)
return err
}
const GetAllBets = `-- name: GetAllBets :many
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, created_at, updated_at FROM bets
`
func (q *Queries) GetAllBets(ctx context.Context) ([]Bet, error) {
rows, err := q.db.Query(ctx, GetAllBets)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Bet
for rows.Next() {
var i Bet
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.TotalOdds,
&i.Status,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.UserID,
&i.CashedOut,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBetByID = `-- name: GetBetByID :one
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, created_at, updated_at FROM bets WHERE id = $1
`
func (q *Queries) GetBetByID(ctx context.Context, id int64) (Bet, error) {
row := q.db.QueryRow(ctx, GetBetByID, id)
var i Bet
err := row.Scan(
&i.ID,
&i.Amount,
&i.TotalOdds,
&i.Status,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.UserID,
&i.CashedOut,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const UpdateCashOut = `-- name: UpdateCashOut :exec
UPDATE bets SET cashed_out = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1
`
type UpdateCashOutParams struct {
ID int64
CashedOut pgtype.Bool
}
func (q *Queries) UpdateCashOut(ctx context.Context, arg UpdateCashOutParams) error {
_, err := q.db.Exec(ctx, UpdateCashOut, arg.ID, arg.CashedOut)
return err
}

View File

@ -8,10 +8,24 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
type Bet struct {
ID int64
Amount int64
TotalOdds float32
Status int32
FullName string
PhoneNumber string
BranchID pgtype.Int8
UserID pgtype.Int8
CashedOut pgtype.Bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type Ticket struct {
ID int64
Amount pgtype.Numeric
TotalOdds pgtype.Numeric
Amount pgtype.Int8
TotalOdds float32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}

View File

@ -12,19 +12,18 @@ import (
)
const CreateTicket = `-- name: CreateTicket :one
INSERT INTO tickets (id, amount, total_odds)
VALUES ($1, $2, $3)
INSERT INTO tickets (amount, total_odds)
VALUES ($1, $2)
RETURNING id, amount, total_odds, created_at, updated_at
`
type CreateTicketParams struct {
ID int64
Amount pgtype.Numeric
TotalOdds pgtype.Numeric
Amount pgtype.Int8
TotalOdds float32
}
func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Ticket, error) {
row := q.db.QueryRow(ctx, CreateTicket, arg.ID, arg.Amount, arg.TotalOdds)
row := q.db.QueryRow(ctx, CreateTicket, arg.Amount, arg.TotalOdds)
var i Ticket
err := row.Scan(
&i.ID,

27
internal/domain/bet.go Normal file
View File

@ -0,0 +1,27 @@
package domain
type BetStatus int
const (
BET_STATUS_PENDING BetStatus = iota
BET_STATUS_WIN
BET_STATUS_LOSS
BET_STATUS_ERROR
)
type Bet struct {
ID int64
Outcome []Outcome
Amount Currency
TotalOdds float32
Status BetStatus
FullName string
PhoneNumber string
BranchID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
CashedOut bool
}
func (b BetStatus) String() string {
return []string{"Pending", "Win", "Loss", "Error"}[b]
}

13
internal/domain/branch.go Normal file
View File

@ -0,0 +1,13 @@
package domain
type Branch struct {
ID int64
Name string
Location string
BranchManagerID int64
IsSelfOwned bool
IsSupportingSportBook bool
IsSupportingVirtual bool
IsSupportingGameZone bool
}

29
internal/domain/common.go Normal file
View File

@ -0,0 +1,29 @@
package domain
import "fmt"
type ValidInt64 struct {
Value int64
Valid bool
}
type Currency int64
// ToCurrency converts a float32 to Currency
func ToCurrency(f float32) Currency {
return Currency((f * 100) + 0.5)
}
// Float64 converts a Currency to float32
func (m Currency) Float64() float32 {
x := float32(m)
x = x / 100
return x
}
// String returns a formatted Currency value
func (m Currency) String() string {
x := float32(m)
x = x / 100
return fmt.Sprintf("$%.2f", x)
}

6
internal/domain/event.go Normal file
View File

@ -0,0 +1,6 @@
package domain
type Event struct {}
type Outcome struct {}

View File

@ -1,9 +1,9 @@
package domain
// TODO: Adding Outcome Array Once the event is done
// ID will serve as the fast code since this doesn't need to be secure
type Ticket struct {
ID int64
Amount int32
TotalOdds int32
Outcome []Outcome
Amount Currency
TotalOdds float32
}

118
internal/repository/bet.go Normal file
View File

@ -0,0 +1,118 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateBet(ctx context.Context, bet domain.Bet) (domain.Bet, error) {
newBet, err := s.queries.CreateBet(ctx, dbgen.CreateBetParams{
Amount: int64(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: int32(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: pgtype.Int8{
Int64: bet.BranchID.Value,
Valid: bet.BranchID.Valid,
},
UserID: pgtype.Int8{
Int64: bet.UserID.Value,
Valid: bet.UserID.Valid,
},
})
if err != nil {
return domain.Bet{}, err
}
return domain.Bet{
ID: newBet.ID,
Amount: domain.Currency(newBet.Amount),
TotalOdds: newBet.TotalOdds,
Status: domain.BetStatus(newBet.Status),
FullName: newBet.FullName,
PhoneNumber: newBet.PhoneNumber,
BranchID: domain.ValidInt64{
Value: newBet.BranchID.Int64,
Valid: newBet.BranchID.Valid,
},
UserID: domain.ValidInt64{
Value: newBet.UserID.Int64,
Valid: newBet.UserID.Valid,
},
}, err
}
func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.Bet, error) {
bet, err := s.queries.GetBetByID(ctx, id)
if err != nil {
return domain.Bet{}, err
}
return domain.Bet{
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.BetStatus(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: domain.ValidInt64{
Value: bet.BranchID.Int64,
Valid: bet.BranchID.Valid,
},
UserID: domain.ValidInt64{
Value: bet.UserID.Int64,
Valid: bet.UserID.Valid,
},
}, nil
}
func (s *Store) GetAllBets(ctx context.Context) ([]domain.Bet, error) {
bets, err := s.queries.GetAllBets(ctx)
if err != nil {
return nil, err
}
var result []domain.Bet
for _, bet := range bets {
result = append(result, domain.Bet{
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.BetStatus(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: domain.ValidInt64{
Value: bet.BranchID.Int64,
Valid: bet.BranchID.Valid,
},
UserID: domain.ValidInt64{
Value: bet.UserID.Int64,
Valid: bet.UserID.Valid,
},
})
}
return result, nil
}
func (s *Store) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error {
err := s.queries.UpdateCashOut(ctx, dbgen.UpdateCashOutParams{
ID: id,
CashedOut: pgtype.Bool{
Bool: cashedOut,
},
})
return err
}
func (s *Store) DeleteBet(ctx context.Context, id int64) error {
return s.queries.DeleteBet(ctx, id)
}

View File

@ -8,15 +8,13 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateTicket(ctx context.Context, amount int32, totalOdds int32) (domain.Ticket, error) {
func (s *Store) CreateTicket(ctx context.Context, amount domain.Currency, totalOdds float32) (domain.Ticket, error) {
ticket, err := s.queries.CreateTicket(ctx, dbgen.CreateTicketParams{
Amount: pgtype.Numeric{
Exp: amount,
},
TotalOdds: pgtype.Numeric{
Exp: totalOdds,
Amount: pgtype.Int8{
Int64: int64(amount),
},
TotalOdds: totalOdds,
})
if err != nil {
@ -25,8 +23,8 @@ func (s *Store) CreateTicket(ctx context.Context, amount int32, totalOdds int32)
return domain.Ticket{
ID: ticket.ID,
Amount: ticket.Amount.Exp,
TotalOdds: ticket.TotalOdds.Exp,
Amount: amount,
TotalOdds: totalOdds,
}, err
}
@ -39,8 +37,8 @@ func (s *Store) GetTicketByID(ctx context.Context, id int64) (domain.Ticket, err
return domain.Ticket{
ID: ticket.ID,
Amount: ticket.Amount.Exp,
TotalOdds: ticket.TotalOdds.Exp,
Amount: domain.Currency(ticket.Amount.Int64),
TotalOdds: ticket.TotalOdds,
}, nil
}
@ -55,8 +53,8 @@ func (s *Store) GetAllTickets(ctx context.Context) ([]domain.Ticket, error) {
for _, ticket := range tickets {
result = append(result, domain.Ticket{
ID: ticket.ID,
Amount: ticket.Amount.Exp,
TotalOdds: ticket.TotalOdds.Exp,
Amount: domain.Currency(ticket.Amount.Int64),
TotalOdds: ticket.TotalOdds,
})
}

View File

@ -0,0 +1,15 @@
package ticket
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type BetStore interface {
CreateBet(ctx context.Context, bet domain.Bet) (domain.Bet, error)
GetBetByID(ctx context.Context, id int64) (domain.Bet, error)
GetAllBets(ctx context.Context) ([]domain.Bet, error)
UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error
DeleteBet(ctx context.Context, id int64) error
}

View File

@ -0,0 +1,35 @@
package ticket
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type Service struct {
betStore BetStore
}
func NewService(betStore BetStore) *Service {
return &Service{
betStore: betStore,
}
}
func (s *Service) CreateBet(ctx context.Context, bet domain.Bet) (domain.Bet, error) {
return s.betStore.CreateBet(ctx, bet)
}
func (s *Service) GetBetByID(ctx context.Context, id int64) (domain.Bet, error) {
return s.betStore.GetBetByID(ctx, id)
}
func (s *Service) GetAllBets(ctx context.Context) ([]domain.Bet, error) {
return s.betStore.GetAllBets(ctx)
}
func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error {
return s.betStore.UpdateCashOut(ctx, id, cashedOut)
}
func (s *Service) DeleteBet(ctx context.Context, id int64) error {
return s.betStore.DeleteBet(ctx, id)
}

View File

@ -7,7 +7,7 @@ import (
)
type TicketStore interface {
CreateTicket(ctx context.Context, amount int32, totalOdds int32) (domain.Ticket, error)
CreateTicket(ctx context.Context, amount domain.Currency, totalOdds float32) (domain.Ticket, error)
GetTicketByID(ctx context.Context, id int64) (domain.Ticket, error)
GetAllTickets(ctx context.Context) ([]domain.Ticket, error)
DeleteOldTickets(ctx context.Context) error

View File

@ -16,7 +16,7 @@ func NewService(ticketStore TicketStore) *Service {
}
}
func (s *Service) CreateUser(ctx context.Context, amount int32, totalOdds int32) (domain.Ticket, error) {
func (s *Service) CreateTicket(ctx context.Context, amount domain.Currency, totalOdds float32) (domain.Ticket, error) {
return s.ticketStore.CreateTicket(ctx, amount, totalOdds)
}
func (s *Service) GetTicketByID(ctx context.Context, id int64) (domain.Ticket, error) {
@ -28,7 +28,3 @@ func (s *Service) GetAllTickets(ctx context.Context) ([]domain.Ticket, error) {
func (s *Service) DeleteTicket(ctx context.Context, id int64) error {
return s.ticketStore.DeleteTicket(ctx, id)
}
func (s *Service) DeleteOldTickets(ctx context.Context) error {
return s.ticketStore.DeleteOldTickets(ctx)
}