fix: result log and notification
This commit is contained in:
parent
7d8d824a94
commit
3fb3da6cc8
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ logs/
|
||||||
app_logs/
|
app_logs/
|
||||||
backup/
|
backup/
|
||||||
reports/
|
reports/
|
||||||
|
exports/
|
||||||
|
|
@ -264,6 +264,7 @@ CREATE TABLE events (
|
||||||
fetched_at TIMESTAMP DEFAULT now(),
|
fetched_at TIMESTAMP DEFAULT now(),
|
||||||
source TEXT DEFAULT 'b365api',
|
source TEXT DEFAULT 'b365api',
|
||||||
is_featured BOOLEAN NOT NULL DEFAULT FALSE,
|
is_featured BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
is_monitorred BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
);
|
);
|
||||||
CREATE TABLE odds (
|
CREATE TABLE odds (
|
||||||
|
|
@ -287,6 +288,22 @@ CREATE TABLE odds (
|
||||||
UNIQUE (event_id, market_id, name, handicap),
|
UNIQUE (event_id, market_id, name, handicap),
|
||||||
UNIQUE (event_id, market_id)
|
UNIQUE (event_id, market_id)
|
||||||
);
|
);
|
||||||
|
CREATE TABLE result_log (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
status_not_finished_count INT NOT NULL,
|
||||||
|
status_not_finished_bets INT NOT NULL,
|
||||||
|
status_to_be_fixed_count INT NOT NULL,
|
||||||
|
status_to_be_fixed_bets INT NOT NULL,
|
||||||
|
status_postponed_count INT NOT NULL,
|
||||||
|
status_postponed_bets INT NOT NULL,
|
||||||
|
status_ended_count INT NOT NULL,
|
||||||
|
status_ended_bets INT NOT NULL,
|
||||||
|
status_removed_count INT NOT NULL,
|
||||||
|
status_removed_bets INT NOT NULL,
|
||||||
|
removed_count INT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
CREATE TABLE companies (
|
CREATE TABLE companies (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,55 @@ WHERE (
|
||||||
)
|
)
|
||||||
GROUP BY month
|
GROUP BY month
|
||||||
ORDER BY month;
|
ORDER BY month;
|
||||||
|
|
||||||
|
-- name: GetLeagueEventStat :many
|
||||||
|
SELECT leagues.id,
|
||||||
|
leagues.name,
|
||||||
|
COUNT(*) AS total_events,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'pending'
|
||||||
|
) AS pending,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'in_play'
|
||||||
|
) AS in_play,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'to_be_fixed'
|
||||||
|
) AS to_be_fixed,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'ended'
|
||||||
|
) AS ended,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'postponed'
|
||||||
|
) AS postponed,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'cancelled'
|
||||||
|
) AS cancelled,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'walkover'
|
||||||
|
) AS walkover,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'interrupted'
|
||||||
|
) AS interrupted,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'abandoned'
|
||||||
|
) AS abandoned,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'retired'
|
||||||
|
) AS retired,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'suspended'
|
||||||
|
) AS suspended,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'decided_by_fa'
|
||||||
|
) AS decided_by_fa,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'removed'
|
||||||
|
) AS removed
|
||||||
|
FROM leagues
|
||||||
|
JOIN events ON leagues.id = events.league_id
|
||||||
|
WHERE (
|
||||||
|
leagues.is_featured = sqlc.narg('is_league_featured')
|
||||||
|
OR sqlc.narg('is_league_featured') IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY leagues.id,
|
||||||
|
leagues.name;
|
||||||
28
db/query/result_log.sql
Normal file
28
db/query/result_log.sql
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-- name: CreateResultLog :one
|
||||||
|
INSERT INTO result_log (
|
||||||
|
status_not_finished_count,
|
||||||
|
status_not_finished_bets,
|
||||||
|
status_to_be_fixed_count,
|
||||||
|
status_to_be_fixed_bets,
|
||||||
|
status_postponed_count,
|
||||||
|
status_postponed_bets,
|
||||||
|
status_ended_count,
|
||||||
|
status_ended_bets,
|
||||||
|
status_removed_count,
|
||||||
|
status_removed_bets,
|
||||||
|
removed_count
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
RETURNING *;
|
||||||
|
-- name: GetAllResultLog :many
|
||||||
|
SELECT *
|
||||||
|
FROM result_log
|
||||||
|
WHERE (
|
||||||
|
created_at < sqlc.narg('created_before')
|
||||||
|
OR sqlc.narg('created_before') IS NULL
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
created_at > sqlc.narg('created_after')
|
||||||
|
OR sqlc.narg('created_after') IS NULL
|
||||||
|
)
|
||||||
|
ORDER BY created_at DESC;
|
||||||
|
|
@ -18,6 +18,7 @@ services:
|
||||||
retries: 5
|
retries: 5
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./exports:/exports
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
container_name: fortunebet-mongo
|
container_name: fortunebet-mongo
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ func (q *Queries) DeleteEvent(ctx context.Context, id string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetAllUpcomingEvents = `-- name: GetAllUpcomingEvents :many
|
const GetAllUpcomingEvents = `-- name: GetAllUpcomingEvents :many
|
||||||
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_active
|
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_monitorred, is_active
|
||||||
FROM events
|
FROM events
|
||||||
WHERE start_time > now()
|
WHERE start_time > now()
|
||||||
AND is_live = false
|
AND is_live = false
|
||||||
|
|
@ -63,6 +63,7 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) {
|
||||||
&i.FetchedAt,
|
&i.FetchedAt,
|
||||||
&i.Source,
|
&i.Source,
|
||||||
&i.IsFeatured,
|
&i.IsFeatured,
|
||||||
|
&i.IsMonitorred,
|
||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -76,7 +77,7 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many
|
const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many
|
||||||
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_active,
|
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_monitorred, events.is_active,
|
||||||
leagues.country_code as league_cc
|
leagues.country_code as league_cc
|
||||||
FROM events
|
FROM events
|
||||||
LEFT JOIN leagues ON leagues.id = league_id
|
LEFT JOIN leagues ON leagues.id = league_id
|
||||||
|
|
@ -112,6 +113,7 @@ type GetExpiredUpcomingEventsRow struct {
|
||||||
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
Source pgtype.Text `json:"source"`
|
Source pgtype.Text `json:"source"`
|
||||||
IsFeatured bool `json:"is_featured"`
|
IsFeatured bool `json:"is_featured"`
|
||||||
|
IsMonitorred bool `json:"is_monitorred"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
|
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +151,7 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
|
||||||
&i.FetchedAt,
|
&i.FetchedAt,
|
||||||
&i.Source,
|
&i.Source,
|
||||||
&i.IsFeatured,
|
&i.IsFeatured,
|
||||||
|
&i.IsMonitorred,
|
||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.LeagueCc_2,
|
&i.LeagueCc_2,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
@ -163,7 +166,7 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many
|
const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many
|
||||||
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_active,
|
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_monitorred, events.is_active,
|
||||||
leagues.country_code as league_cc
|
leagues.country_code as league_cc
|
||||||
FROM events
|
FROM events
|
||||||
LEFT JOIN leagues ON leagues.id = league_id
|
LEFT JOIN leagues ON leagues.id = league_id
|
||||||
|
|
@ -239,6 +242,7 @@ type GetPaginatedUpcomingEventsRow struct {
|
||||||
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
Source pgtype.Text `json:"source"`
|
Source pgtype.Text `json:"source"`
|
||||||
IsFeatured bool `json:"is_featured"`
|
IsFeatured bool `json:"is_featured"`
|
||||||
|
IsMonitorred bool `json:"is_monitorred"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
|
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
|
||||||
}
|
}
|
||||||
|
|
@ -286,6 +290,7 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
|
||||||
&i.FetchedAt,
|
&i.FetchedAt,
|
||||||
&i.Source,
|
&i.Source,
|
||||||
&i.IsFeatured,
|
&i.IsFeatured,
|
||||||
|
&i.IsMonitorred,
|
||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.LeagueCc_2,
|
&i.LeagueCc_2,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
@ -362,7 +367,7 @@ func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetUpcomingByID = `-- name: GetUpcomingByID :one
|
const GetUpcomingByID = `-- name: GetUpcomingByID :one
|
||||||
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_active
|
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_monitorred, is_active
|
||||||
FROM events
|
FROM events
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
AND is_live = false
|
AND is_live = false
|
||||||
|
|
@ -397,6 +402,7 @@ func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (Event, error)
|
||||||
&i.FetchedAt,
|
&i.FetchedAt,
|
||||||
&i.Source,
|
&i.Source,
|
||||||
&i.IsFeatured,
|
&i.IsFeatured,
|
||||||
|
&i.IsMonitorred,
|
||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,115 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const GetLeagueEventStat = `-- name: GetLeagueEventStat :many
|
||||||
|
SELECT leagues.id,
|
||||||
|
leagues.name,
|
||||||
|
COUNT(*) AS total_events,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'pending'
|
||||||
|
) AS pending,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'in_play'
|
||||||
|
) AS in_play,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'to_be_fixed'
|
||||||
|
) AS to_be_fixed,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'ended'
|
||||||
|
) AS ended,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'postponed'
|
||||||
|
) AS postponed,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'cancelled'
|
||||||
|
) AS cancelled,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'walkover'
|
||||||
|
) AS walkover,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'interrupted'
|
||||||
|
) AS interrupted,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'abandoned'
|
||||||
|
) AS abandoned,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'retired'
|
||||||
|
) AS retired,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'suspended'
|
||||||
|
) AS suspended,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'decided_by_fa'
|
||||||
|
) AS decided_by_fa,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE events.status = 'removed'
|
||||||
|
) AS removed
|
||||||
|
FROM leagues
|
||||||
|
JOIN events ON leagues.id = events.league_id
|
||||||
|
WHERE (
|
||||||
|
leagues.is_featured = $1
|
||||||
|
OR $1 IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY leagues.id,
|
||||||
|
leagues.name
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetLeagueEventStatRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TotalEvents int64 `json:"total_events"`
|
||||||
|
Pending int64 `json:"pending"`
|
||||||
|
InPlay int64 `json:"in_play"`
|
||||||
|
ToBeFixed int64 `json:"to_be_fixed"`
|
||||||
|
Ended int64 `json:"ended"`
|
||||||
|
Postponed int64 `json:"postponed"`
|
||||||
|
Cancelled int64 `json:"cancelled"`
|
||||||
|
Walkover int64 `json:"walkover"`
|
||||||
|
Interrupted int64 `json:"interrupted"`
|
||||||
|
Abandoned int64 `json:"abandoned"`
|
||||||
|
Retired int64 `json:"retired"`
|
||||||
|
Suspended int64 `json:"suspended"`
|
||||||
|
DecidedByFa int64 `json:"decided_by_fa"`
|
||||||
|
Removed int64 `json:"removed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetLeagueEventStat(ctx context.Context, isLeagueFeatured pgtype.Bool) ([]GetLeagueEventStatRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetLeagueEventStat, isLeagueFeatured)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetLeagueEventStatRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetLeagueEventStatRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.TotalEvents,
|
||||||
|
&i.Pending,
|
||||||
|
&i.InPlay,
|
||||||
|
&i.ToBeFixed,
|
||||||
|
&i.Ended,
|
||||||
|
&i.Postponed,
|
||||||
|
&i.Cancelled,
|
||||||
|
&i.Walkover,
|
||||||
|
&i.Interrupted,
|
||||||
|
&i.Abandoned,
|
||||||
|
&i.Retired,
|
||||||
|
&i.Suspended,
|
||||||
|
&i.DecidedByFa,
|
||||||
|
&i.Removed,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const GetTotalMontlyEventStat = `-- name: GetTotalMontlyEventStat :many
|
const GetTotalMontlyEventStat = `-- name: GetTotalMontlyEventStat :many
|
||||||
SELECT DATE_TRUNC('month', start_time) AS month,
|
SELECT DATE_TRUNC('month', start_time) AS month,
|
||||||
COUNT(*) AS event_count
|
COUNT(*) AS event_count
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,7 @@ type Event struct {
|
||||||
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
Source pgtype.Text `json:"source"`
|
Source pgtype.Text `json:"source"`
|
||||||
IsFeatured bool `json:"is_featured"`
|
IsFeatured bool `json:"is_featured"`
|
||||||
|
IsMonitorred bool `json:"is_monitorred"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,6 +409,23 @@ type Result struct {
|
||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResultLog struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
StatusNotFinishedCount int32 `json:"status_not_finished_count"`
|
||||||
|
StatusNotFinishedBets int32 `json:"status_not_finished_bets"`
|
||||||
|
StatusToBeFixedCount int32 `json:"status_to_be_fixed_count"`
|
||||||
|
StatusToBeFixedBets int32 `json:"status_to_be_fixed_bets"`
|
||||||
|
StatusPostponedCount int32 `json:"status_postponed_count"`
|
||||||
|
StatusPostponedBets int32 `json:"status_postponed_bets"`
|
||||||
|
StatusEndedCount int32 `json:"status_ended_count"`
|
||||||
|
StatusEndedBets int32 `json:"status_ended_bets"`
|
||||||
|
StatusRemovedCount int32 `json:"status_removed_count"`
|
||||||
|
StatusRemovedBets int32 `json:"status_removed_bets"`
|
||||||
|
RemovedCount int32 `json:"removed_count"`
|
||||||
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type Setting struct {
|
type Setting struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
|
|
|
||||||
132
gen/db/result_log.sql.go
Normal file
132
gen/db/result_log.sql.go
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
// source: result_log.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CreateResultLog = `-- name: CreateResultLog :one
|
||||||
|
INSERT INTO result_log (
|
||||||
|
status_not_finished_count,
|
||||||
|
status_not_finished_bets,
|
||||||
|
status_to_be_fixed_count,
|
||||||
|
status_to_be_fixed_bets,
|
||||||
|
status_postponed_count,
|
||||||
|
status_postponed_bets,
|
||||||
|
status_ended_count,
|
||||||
|
status_ended_bets,
|
||||||
|
status_removed_count,
|
||||||
|
status_removed_bets,
|
||||||
|
removed_count
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
RETURNING id, status_not_finished_count, status_not_finished_bets, status_to_be_fixed_count, status_to_be_fixed_bets, status_postponed_count, status_postponed_bets, status_ended_count, status_ended_bets, status_removed_count, status_removed_bets, removed_count, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateResultLogParams struct {
|
||||||
|
StatusNotFinishedCount int32 `json:"status_not_finished_count"`
|
||||||
|
StatusNotFinishedBets int32 `json:"status_not_finished_bets"`
|
||||||
|
StatusToBeFixedCount int32 `json:"status_to_be_fixed_count"`
|
||||||
|
StatusToBeFixedBets int32 `json:"status_to_be_fixed_bets"`
|
||||||
|
StatusPostponedCount int32 `json:"status_postponed_count"`
|
||||||
|
StatusPostponedBets int32 `json:"status_postponed_bets"`
|
||||||
|
StatusEndedCount int32 `json:"status_ended_count"`
|
||||||
|
StatusEndedBets int32 `json:"status_ended_bets"`
|
||||||
|
StatusRemovedCount int32 `json:"status_removed_count"`
|
||||||
|
StatusRemovedBets int32 `json:"status_removed_bets"`
|
||||||
|
RemovedCount int32 `json:"removed_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateResultLog(ctx context.Context, arg CreateResultLogParams) (ResultLog, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateResultLog,
|
||||||
|
arg.StatusNotFinishedCount,
|
||||||
|
arg.StatusNotFinishedBets,
|
||||||
|
arg.StatusToBeFixedCount,
|
||||||
|
arg.StatusToBeFixedBets,
|
||||||
|
arg.StatusPostponedCount,
|
||||||
|
arg.StatusPostponedBets,
|
||||||
|
arg.StatusEndedCount,
|
||||||
|
arg.StatusEndedBets,
|
||||||
|
arg.StatusRemovedCount,
|
||||||
|
arg.StatusRemovedBets,
|
||||||
|
arg.RemovedCount,
|
||||||
|
)
|
||||||
|
var i ResultLog
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.StatusNotFinishedCount,
|
||||||
|
&i.StatusNotFinishedBets,
|
||||||
|
&i.StatusToBeFixedCount,
|
||||||
|
&i.StatusToBeFixedBets,
|
||||||
|
&i.StatusPostponedCount,
|
||||||
|
&i.StatusPostponedBets,
|
||||||
|
&i.StatusEndedCount,
|
||||||
|
&i.StatusEndedBets,
|
||||||
|
&i.StatusRemovedCount,
|
||||||
|
&i.StatusRemovedBets,
|
||||||
|
&i.RemovedCount,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetAllResultLog = `-- name: GetAllResultLog :many
|
||||||
|
SELECT id, status_not_finished_count, status_not_finished_bets, status_to_be_fixed_count, status_to_be_fixed_bets, status_postponed_count, status_postponed_bets, status_ended_count, status_ended_bets, status_removed_count, status_removed_bets, removed_count, created_at, updated_at
|
||||||
|
FROM result_log
|
||||||
|
WHERE (
|
||||||
|
created_at < $1
|
||||||
|
OR $1 IS NULL
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
created_at > $2
|
||||||
|
OR $2 IS NULL
|
||||||
|
)
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetAllResultLogParams struct {
|
||||||
|
CreatedBefore pgtype.Timestamp `json:"created_before"`
|
||||||
|
CreatedAfter pgtype.Timestamp `json:"created_after"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetAllResultLog(ctx context.Context, arg GetAllResultLogParams) ([]ResultLog, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetAllResultLog, arg.CreatedBefore, arg.CreatedAfter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ResultLog
|
||||||
|
for rows.Next() {
|
||||||
|
var i ResultLog
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.StatusNotFinishedCount,
|
||||||
|
&i.StatusNotFinishedBets,
|
||||||
|
&i.StatusToBeFixedCount,
|
||||||
|
&i.StatusToBeFixedBets,
|
||||||
|
&i.StatusPostponedCount,
|
||||||
|
&i.StatusPostponedBets,
|
||||||
|
&i.StatusEndedCount,
|
||||||
|
&i.StatusEndedBets,
|
||||||
|
&i.StatusRemovedCount,
|
||||||
|
&i.StatusRemovedBets,
|
||||||
|
&i.RemovedCount,
|
||||||
|
&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
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,8 @@ package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MarketConfig struct {
|
type MarketConfig struct {
|
||||||
|
|
@ -83,22 +85,78 @@ const (
|
||||||
TIME_STATUS_REMOVED TimeStatus = 99
|
TIME_STATUS_REMOVED TimeStatus = 99
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultStatusCounts struct {
|
type ResultLog struct {
|
||||||
IsNotFinished int `json:"is_not_finished"`
|
ID int64 `json:"id"`
|
||||||
IsNotFinishedBets int `json:"is_not_finished_bets"`
|
StatusNotFinishedCount int `json:"status_not_finished_count"`
|
||||||
IsToBeFixed int `json:"is_to_be_fixed"`
|
StatusNotFinishedBets int `json:"status_not_finished_bets"`
|
||||||
IsToBeFixedBets int `json:"is_to_be_fixed_bets"`
|
StatusToBeFixedCount int `json:"status_to_be_fixed_count"`
|
||||||
IsPostponed int `json:"is_postponed"`
|
StatusToBeFixedBets int `json:"status_to_be_fixed_bets"`
|
||||||
IsPostponedBets int `json:"is_postponed_bets"`
|
StatusPostponedCount int `json:"status_postponed_count"`
|
||||||
IsEnded int `json:"is_ended"`
|
StatusPostponedBets int `json:"status_postponed_bets"`
|
||||||
IsEndedBets int `json:"is_ended_bets"`
|
StatusEndedCount int `json:"status_ended_count"`
|
||||||
IsRemoved int `json:"is_removed"`
|
StatusEndedBets int `json:"status_ended_bets"`
|
||||||
IsRemovedBets int `json:"is_removed_bets"`
|
StatusRemovedCount int `json:"status_removed_count"`
|
||||||
|
StatusRemovedBets int `json:"status_removed_bets"`
|
||||||
|
RemovedCount int `json:"removed"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateResultLog struct {
|
||||||
|
StatusNotFinishedCount int `json:"status_not_finished_count"`
|
||||||
|
StatusNotFinishedBets int `json:"status_not_finished_bets"`
|
||||||
|
StatusToBeFixedCount int `json:"status_to_be_fixed_count"`
|
||||||
|
StatusToBeFixedBets int `json:"status_to_be_fixed_bets"`
|
||||||
|
StatusPostponedCount int `json:"status_postponed_count"`
|
||||||
|
StatusPostponedBets int `json:"status_postponed_bets"`
|
||||||
|
StatusEndedCount int `json:"status_ended_count"`
|
||||||
|
StatusEndedBets int `json:"status_ended_bets"`
|
||||||
|
StatusRemovedCount int `json:"status_removed_count"`
|
||||||
|
StatusRemovedBets int `json:"status_removed_bets"`
|
||||||
|
RemovedCount int `json:"removed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultFilter struct {
|
||||||
|
CreatedBefore ValidTime
|
||||||
|
CreatedAfter ValidTime
|
||||||
}
|
}
|
||||||
type ResultStatusBets struct {
|
type ResultStatusBets struct {
|
||||||
IsNotFinished []int64 `json:"is_not_finished"`
|
StatusNotFinished []int64 `json:"status_not_finished"`
|
||||||
IsToBeFixed []int64 `json:"is_to_be_fixed"`
|
StatusToBeFixed []int64 `json:"status_to_be_fixed"`
|
||||||
IsPostponed []int64 `json:"is_postponed"`
|
StatusPostponed []int64 `json:"status_postponed"`
|
||||||
IsEnded []int64 `json:"is_ended"`
|
StatusEnded []int64 `json:"status_ended"`
|
||||||
IsRemoved []int64 `json:"is_removed"`
|
StatusRemoved []int64 `json:"status_removed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBResultLog(result dbgen.ResultLog) ResultLog {
|
||||||
|
return ResultLog{
|
||||||
|
ID: result.ID,
|
||||||
|
StatusNotFinishedCount: int(result.StatusNotFinishedCount),
|
||||||
|
StatusNotFinishedBets: int(result.StatusNotFinishedBets),
|
||||||
|
StatusToBeFixedCount: int(result.StatusToBeFixedCount),
|
||||||
|
StatusToBeFixedBets: int(result.StatusToBeFixedBets),
|
||||||
|
StatusPostponedCount: int(result.StatusPostponedCount),
|
||||||
|
StatusPostponedBets: int(result.StatusPostponedBets),
|
||||||
|
StatusEndedCount: int(result.StatusEndedCount),
|
||||||
|
StatusEndedBets: int(result.StatusEndedBets),
|
||||||
|
StatusRemovedCount: int(result.StatusRemovedCount),
|
||||||
|
StatusRemovedBets: int(result.StatusRemovedBets),
|
||||||
|
RemovedCount: int(result.RemovedCount),
|
||||||
|
CreatedAt: result.CreatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertCreateResultLog(result CreateResultLog) dbgen.CreateResultLogParams {
|
||||||
|
return dbgen.CreateResultLogParams{
|
||||||
|
StatusNotFinishedCount: int32(result.StatusNotFinishedCount),
|
||||||
|
StatusNotFinishedBets: int32(result.StatusNotFinishedBets),
|
||||||
|
StatusToBeFixedCount: int32(result.StatusToBeFixedCount),
|
||||||
|
StatusToBeFixedBets: int32(result.StatusToBeFixedBets),
|
||||||
|
StatusPostponedCount: int32(result.StatusPostponedCount),
|
||||||
|
StatusPostponedBets: int32(result.StatusPostponedBets),
|
||||||
|
StatusEndedCount: int32(result.StatusEndedCount),
|
||||||
|
StatusEndedBets: int32(result.StatusEndedBets),
|
||||||
|
StatusRemovedCount: int32(result.StatusRemovedCount),
|
||||||
|
StatusRemovedBets: int32(result.StatusRemovedBets),
|
||||||
|
RemovedCount: int32(result.RemovedCount),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,93 +8,34 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func convertDBResult(result dbgen.Result) domain.Result {
|
|
||||||
scores := make(map[string]domain.Score)
|
|
||||||
return domain.Result{
|
|
||||||
ID: result.ID,
|
|
||||||
BetOutcomeID: result.BetOutcomeID,
|
|
||||||
EventID: result.EventID,
|
|
||||||
OddID: result.OddID,
|
|
||||||
MarketID: result.MarketID,
|
|
||||||
Status: domain.OutcomeStatus(result.Status),
|
|
||||||
Score: result.Score.String,
|
|
||||||
FullTimeScore: result.FullTimeScore.String,
|
|
||||||
HalfTimeScore: result.HalfTimeScore.String,
|
|
||||||
SS: result.Ss.String,
|
|
||||||
Scores: scores,
|
|
||||||
CreatedAt: result.CreatedAt.Time,
|
|
||||||
UpdatedAt: result.UpdatedAt.Time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertCreateResult(result domain.CreateResult) dbgen.CreateResultParams {
|
|
||||||
return dbgen.CreateResultParams{
|
|
||||||
BetOutcomeID: result.BetOutcomeID,
|
|
||||||
EventID: result.EventID,
|
|
||||||
OddID: result.OddID,
|
|
||||||
MarketID: result.MarketID,
|
|
||||||
Status: int32(result.Status),
|
|
||||||
Score: pgtype.Text{String: result.Score},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertResult(result domain.Result) dbgen.InsertResultParams {
|
|
||||||
return dbgen.InsertResultParams{
|
|
||||||
BetOutcomeID: result.BetOutcomeID,
|
|
||||||
EventID: result.EventID,
|
|
||||||
OddID: result.OddID,
|
|
||||||
MarketID: result.MarketID,
|
|
||||||
Status: int32(result.Status),
|
|
||||||
Score: pgtype.Text{String: result.Score},
|
|
||||||
FullTimeScore: pgtype.Text{String: result.FullTimeScore},
|
|
||||||
HalfTimeScore: pgtype.Text{String: result.HalfTimeScore},
|
|
||||||
Ss: pgtype.Text{String: result.SS},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) CreateResult(ctx context.Context, result domain.CreateResult) (domain.Result, error) {
|
func (s *Store) CreateResultLog(ctx context.Context, result domain.CreateResultLog) (domain.ResultLog, error) {
|
||||||
dbResult, err := s.queries.CreateResult(ctx, convertCreateResult(result))
|
dbResult, err := s.queries.CreateResultLog(ctx, domain.ConvertCreateResultLog(result))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.Result{}, err
|
return domain.ResultLog{}, err
|
||||||
}
|
}
|
||||||
return convertDBResult(dbResult), nil
|
return domain.ConvertDBResultLog(dbResult), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) InsertResult(ctx context.Context, result domain.Result) error {
|
func (s *Store) GetAllResultLog(ctx context.Context, filter domain.ResultFilter) ([]domain.ResultLog, error) {
|
||||||
return s.queries.InsertResult(ctx, convertResult(result))
|
dbResultLogs, err := s.queries.GetAllResultLog(ctx, dbgen.GetAllResultLogParams{
|
||||||
}
|
CreatedBefore: pgtype.Timestamp{
|
||||||
|
Time: filter.CreatedBefore.Value,
|
||||||
func (s *Store) GetResultByBetOutcomeID(ctx context.Context, betOutcomeID int64) (domain.Result, error) {
|
Valid: filter.CreatedBefore.Valid,
|
||||||
dbResult, err := s.queries.GetResultByBetOutcomeID(ctx, betOutcomeID)
|
},
|
||||||
if err != nil {
|
CreatedAfter: pgtype.Timestamp{
|
||||||
return domain.Result{}, err
|
Time: filter.CreatedAfter.Value,
|
||||||
}
|
Valid: filter.CreatedAfter.Valid,
|
||||||
return convertDBResult(dbResult), nil
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
func (s *Store) GetPendingBetOutcomes(ctx context.Context) ([]domain.BetOutcome, error) {
|
|
||||||
dbOutcomes, err := s.queries.GetPendingBetOutcomes(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outcomes := make([]domain.BetOutcome, 0, len(dbOutcomes))
|
result := make([]domain.ResultLog, 0, len(dbResultLogs))
|
||||||
for _, dbOutcome := range dbOutcomes {
|
for _, dbResultLog := range dbResultLogs {
|
||||||
outcomes = append(outcomes, domain.BetOutcome{
|
result = append(result, domain.ConvertDBResultLog(dbResultLog))
|
||||||
ID: dbOutcome.ID,
|
|
||||||
BetID: dbOutcome.BetID,
|
|
||||||
EventID: dbOutcome.EventID,
|
|
||||||
OddID: dbOutcome.OddID,
|
|
||||||
HomeTeamName: dbOutcome.HomeTeamName,
|
|
||||||
AwayTeamName: dbOutcome.AwayTeamName,
|
|
||||||
MarketID: dbOutcome.MarketID,
|
|
||||||
MarketName: dbOutcome.MarketName,
|
|
||||||
Odd: dbOutcome.Odd,
|
|
||||||
OddName: dbOutcome.OddName,
|
|
||||||
OddHeader: dbOutcome.OddHeader,
|
|
||||||
OddHandicap: dbOutcome.OddHandicap,
|
|
||||||
Status: domain.OutcomeStatus(dbOutcome.Status),
|
|
||||||
Expires: dbOutcome.Expires.Time,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return outcomes, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -987,6 +987,8 @@ func (s *Service) SendWinningStatusNotification(ctx context.Context, status doma
|
||||||
|
|
||||||
betNotification := &domain.Notification{
|
betNotification := &domain.Notification{
|
||||||
RecipientID: userID,
|
RecipientID: userID,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
||||||
Level: domain.NotificationLevelSuccess,
|
Level: domain.NotificationLevelSuccess,
|
||||||
Reciever: domain.NotificationRecieverSideCustomer,
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
|
@ -1028,6 +1030,8 @@ func (s *Service) SendLosingStatusNotification(ctx context.Context, status domai
|
||||||
|
|
||||||
betNotification := &domain.Notification{
|
betNotification := &domain.Notification{
|
||||||
RecipientID: userID,
|
RecipientID: userID,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
||||||
Level: domain.NotificationLevelSuccess,
|
Level: domain.NotificationLevelSuccess,
|
||||||
Reciever: domain.NotificationRecieverSideCustomer,
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
|
@ -1070,6 +1074,8 @@ func (s *Service) SendErrorStatusNotification(ctx context.Context, status domain
|
||||||
|
|
||||||
betNotification := &domain.Notification{
|
betNotification := &domain.Notification{
|
||||||
RecipientID: userID,
|
RecipientID: userID,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
||||||
Level: domain.NotificationLevelSuccess,
|
Level: domain.NotificationLevelSuccess,
|
||||||
Reciever: domain.NotificationRecieverSideCustomer,
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
|
@ -1104,11 +1110,15 @@ func (s *Service) SendAdminErrorAlertNotification(ctx context.Context, status do
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
|
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
|
||||||
headline = "There was an error with your bet"
|
headline = "There was an error processing bet"
|
||||||
message = "We have encounter an error with your bet. We will fix it as soon as we can"
|
message = "We have encounter an error with bet. We will fix it as soon as we can"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorSeverity := domain.NotificationErrorSeverityHigh
|
||||||
betNotification := &domain.Notification{
|
betNotification := &domain.Notification{
|
||||||
|
ErrorSeverity: &errorSeverity,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
|
||||||
Level: domain.NotificationLevelSuccess,
|
Level: domain.NotificationLevelSuccess,
|
||||||
Reciever: domain.NotificationRecieverSideCustomer,
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,16 @@ package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultService interface {
|
type ResultService interface {
|
||||||
FetchAndProcessResults(ctx context.Context) error
|
FetchAndProcessResults(ctx context.Context) error
|
||||||
FetchAndStoreResult(ctx context.Context, eventID string) error
|
FetchAndStoreResult(ctx context.Context, eventID string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResultLogStore interface {
|
||||||
|
CreateResultLog(ctx context.Context, result domain.CreateResultLog) (domain.ResultLog, error)
|
||||||
|
GetAllResultLog(ctx context.Context, filter domain.ResultFilter) ([]domain.ResultLog, error)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,9 +264,8 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
removed := 0
|
|
||||||
empty_sport_id := make([]int64, 0)
|
empty_sport_id := make([]int64, 0)
|
||||||
var resultStatusCounts domain.ResultStatusCounts
|
var resultLog domain.CreateResultLog
|
||||||
var resultStatusBets domain.ResultStatusBets
|
var resultStatusBets domain.ResultStatusBets
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
|
|
||||||
|
|
@ -283,7 +282,6 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
result, err := s.fetchResult(ctx, eventID)
|
result, err := s.fetchResult(ctx, eventID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ErrEventIsNotActive {
|
if err == ErrEventIsNotActive {
|
||||||
s.logger.Warn("Event is not active", "event_id", eventID, "error", err)
|
|
||||||
s.mongoLogger.Warn(
|
s.mongoLogger.Warn(
|
||||||
"Event is not active",
|
"Event is not active",
|
||||||
zap.Int64("eventID", eventID),
|
zap.Int64("eventID", eventID),
|
||||||
|
|
@ -329,25 +327,42 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
// Admin users will be able to review the events
|
// Admin users will be able to review the events
|
||||||
switch timeStatusParsed {
|
switch timeStatusParsed {
|
||||||
case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY):
|
case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY):
|
||||||
resultStatusCounts.IsNotFinished += 1
|
resultLog.StatusNotFinishedCount += 1
|
||||||
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resultStatusCounts.IsNotFinishedBets = len(bets)
|
resultLog.StatusNotFinishedBets = len(bets)
|
||||||
for k := range bets {
|
for k := range bets {
|
||||||
resultStatusBets.IsNotFinished = append(resultStatusBets.IsNotFinished, k)
|
resultStatusBets.StatusNotFinished = append(resultStatusBets.StatusNotFinished, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
case int64(domain.TIME_STATUS_TO_BE_FIXED):
|
case int64(domain.TIME_STATUS_TO_BE_FIXED):
|
||||||
resultStatusCounts.IsToBeFixed += 1
|
totalBetsRefunded, err := s.RefundAllOutcomes(ctx, eventID)
|
||||||
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
|
||||||
|
err = s.repo.DeleteEvent(ctx, event.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.mongoLogger.Error(
|
||||||
|
"Failed to remove event",
|
||||||
|
zap.Int64("eventID", eventID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resultStatusCounts.IsToBeFixedBets = len(bets)
|
err = s.repo.DeleteOddsForEvent(ctx, event.ID)
|
||||||
for k := range bets {
|
if err != nil {
|
||||||
resultStatusBets.IsNotFinished = append(resultStatusBets.IsNotFinished, k)
|
s.mongoLogger.Error(
|
||||||
|
"Failed to remove odds for event",
|
||||||
|
zap.Int64("eventID", eventID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resultLog.RemovedCount += 1
|
||||||
|
resultLog.StatusToBeFixedCount += 1
|
||||||
|
resultLog.StatusToBeFixedBets = len(totalBetsRefunded)
|
||||||
|
for k := range totalBetsRefunded {
|
||||||
|
resultStatusBets.StatusToBeFixed = append(resultStatusBets.StatusToBeFixed, k)
|
||||||
}
|
}
|
||||||
// s.mongoLogger.Warn(
|
// s.mongoLogger.Warn(
|
||||||
// "Event needs to be rescheduled or corrected",
|
// "Event needs to be rescheduled or corrected",
|
||||||
|
|
@ -355,14 +370,16 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
// zap.Error(err),
|
// zap.Error(err),
|
||||||
// )
|
// )
|
||||||
case int64(domain.TIME_STATUS_POSTPONED), int64(domain.TIME_STATUS_SUSPENDED):
|
case int64(domain.TIME_STATUS_POSTPONED), int64(domain.TIME_STATUS_SUSPENDED):
|
||||||
resultStatusCounts.IsPostponed += 1
|
|
||||||
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resultStatusCounts.IsPostponed = len(bets)
|
|
||||||
|
resultLog.StatusPostponedCount += 1
|
||||||
|
resultLog.StatusPostponedBets = len(bets)
|
||||||
for k := range bets {
|
for k := range bets {
|
||||||
resultStatusBets.IsNotFinished = append(resultStatusBets.IsNotFinished, k)
|
resultStatusBets.StatusPostponed = append(resultStatusBets.StatusPostponed, k)
|
||||||
}
|
}
|
||||||
// s.mongoLogger.Warn(
|
// s.mongoLogger.Warn(
|
||||||
// "Event has been temporarily postponed",
|
// "Event has been temporarily postponed",
|
||||||
|
|
@ -409,15 +426,15 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
removed += 1
|
resultLog.RemovedCount += 1
|
||||||
resultStatusCounts.IsEnded += 1
|
resultLog.StatusEndedCount += 1
|
||||||
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
bets, err := s.GetTotalBetsForEvents(ctx, eventID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resultStatusCounts.IsEndedBets = len(bets)
|
resultLog.StatusEndedBets = len(bets)
|
||||||
for k := range bets {
|
for k := range bets {
|
||||||
resultStatusBets.IsNotFinished = append(resultStatusBets.IsNotFinished, k)
|
resultStatusBets.StatusEnded = append(resultStatusBets.StatusEnded, k)
|
||||||
}
|
}
|
||||||
case int64(domain.TIME_STATUS_ABANDONED), int64(domain.TIME_STATUS_CANCELLED), int64(domain.TIME_STATUS_REMOVED):
|
case int64(domain.TIME_STATUS_ABANDONED), int64(domain.TIME_STATUS_CANCELLED), int64(domain.TIME_STATUS_REMOVED):
|
||||||
// s.SendAdminResultStatusErrorNotification(
|
// s.SendAdminResultStatusErrorNotification(
|
||||||
|
|
@ -451,59 +468,126 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
removed += 1
|
resultLog.RemovedCount += 1
|
||||||
resultStatusCounts.IsRemoved += 1
|
resultLog.StatusRemovedCount += 1
|
||||||
resultStatusCounts.IsRemovedBets = len(totalBetsRefunded)
|
resultLog.StatusRemovedBets = len(totalBetsRefunded)
|
||||||
for k := range totalBetsRefunded {
|
for k := range totalBetsRefunded {
|
||||||
resultStatusBets.IsNotFinished = append(resultStatusBets.IsNotFinished, k)
|
resultStatusBets.StatusRemoved = append(resultStatusBets.StatusRemoved, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SendAdminResultStatusErrorNotification(
|
// This will be used to send daily notifications, since events will be removed
|
||||||
ctx,
|
_, err = s.repo.CreateResultLog(ctx, resultLog)
|
||||||
resultStatusCounts,
|
if err != nil {
|
||||||
|
s.mongoLogger.Warn(
|
||||||
|
"Failed to store result log",
|
||||||
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var logMessage string
|
var logMessage string
|
||||||
if resultStatusCounts.IsNotFinished != 0 || resultStatusCounts.IsPostponed != 0 ||
|
if resultLog.StatusNotFinishedCount != 0 || resultLog.StatusPostponedCount != 0 ||
|
||||||
resultStatusCounts.IsRemoved != 0 || resultStatusCounts.IsToBeFixed != 0 {
|
resultLog.StatusRemovedCount != 0 || resultLog.StatusToBeFixedCount != 0 {
|
||||||
logMessage = "Completed processed results with issues"
|
logMessage = "Completed processing results with issues"
|
||||||
} else {
|
} else {
|
||||||
logMessage = "Successfully processed results with no issues"
|
logMessage = "Successfully processed results with no issues"
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mongoLogger.Info(
|
s.mongoLogger.Info(
|
||||||
logMessage,
|
logMessage,
|
||||||
zap.Int("number_of_removed_events", removed),
|
zap.Int("number_of_removed_events", resultLog.RemovedCount),
|
||||||
zap.Int("total_expired_events", len(events)),
|
zap.Int("total_expired_events", len(events)),
|
||||||
zap.Any("events_with_empty_sport_id", empty_sport_id),
|
zap.Any("events_with_empty_sport_id", empty_sport_id),
|
||||||
zap.Any("result status counts", resultStatusCounts),
|
zap.Any("result status counts", resultLog),
|
||||||
zap.Any("bets by event status", resultStatusBets),
|
zap.Any("bets by event status", resultStatusBets),
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHeadlineAndMessage(counts domain.ResultStatusCounts) (string, string) {
|
func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error {
|
||||||
totalIssues := counts.IsNotFinished + counts.IsToBeFixed + counts.IsPostponed + counts.IsRemoved
|
|
||||||
|
resultLog, err := s.repo.GetAllResultLog(ctx, domain.ResultFilter{
|
||||||
|
CreatedAfter: domain.ValidTime{
|
||||||
|
Value: createdAfter,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error(
|
||||||
|
"Failed to get result log",
|
||||||
|
zap.Time("CreatedAfter", createdAfter),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resultLog) == 0 {
|
||||||
|
s.mongoLogger.Info(
|
||||||
|
"No results found for check and send result notification",
|
||||||
|
zap.Time("CreatedAfter", createdAfter),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
totalResultLog := domain.ResultLog{
|
||||||
|
StatusNotFinishedCount: resultLog[0].StatusNotFinishedCount,
|
||||||
|
StatusPostponedCount: resultLog[0].StatusPostponedCount,
|
||||||
|
}
|
||||||
|
for _, log := range resultLog {
|
||||||
|
// Add all the bets
|
||||||
|
totalResultLog.StatusNotFinishedBets += log.StatusNotFinishedBets
|
||||||
|
totalResultLog.StatusPostponedBets += log.StatusPostponedBets
|
||||||
|
totalResultLog.StatusToBeFixedBets += log.StatusToBeFixedBets
|
||||||
|
totalResultLog.StatusRemovedBets += log.StatusRemovedBets
|
||||||
|
totalResultLog.StatusEndedBets += log.StatusEndedBets
|
||||||
|
|
||||||
|
totalResultLog.StatusToBeFixedCount += log.StatusToBeFixedCount
|
||||||
|
totalResultLog.StatusRemovedCount += log.StatusRemovedCount
|
||||||
|
totalResultLog.StatusEndedCount += log.StatusEndedCount
|
||||||
|
totalResultLog.RemovedCount += log.RemovedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SendAdminResultStatusErrorNotification(ctx, totalResultLog)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error(
|
||||||
|
"Failed to send admin result status notification",
|
||||||
|
zap.Time("CreatedAfter", createdAfter),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHeadlineAndMessage(counts domain.ResultLog) (string, string) {
|
||||||
|
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
|
||||||
|
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
|
||||||
if totalIssues == 0 {
|
if totalIssues == 0 {
|
||||||
return "✅ Event Results Processed", "All event results were processed successfully. No issues detected."
|
return "✅ Successfully Processed Event Results", fmt.Sprintf(
|
||||||
|
"%d total ended events with %d total bets. No issues detected", counts.StatusEndedCount, totalBets,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := []string{}
|
parts := []string{}
|
||||||
if counts.IsNotFinished > 0 {
|
if counts.StatusNotFinishedCount > 0 {
|
||||||
parts = append(parts, fmt.Sprintf("%d unfinished", counts.IsNotFinished))
|
parts = append(parts, fmt.Sprintf("%d unfinished with %d bets", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
|
||||||
}
|
}
|
||||||
if counts.IsToBeFixed > 0 {
|
if counts.StatusToBeFixedCount > 0 {
|
||||||
parts = append(parts, fmt.Sprintf("%d to-fix", counts.IsToBeFixed))
|
parts = append(parts, fmt.Sprintf("%d to-fix with %d bets", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
|
||||||
}
|
}
|
||||||
if counts.IsPostponed > 0 {
|
if counts.StatusPostponedCount > 0 {
|
||||||
parts = append(parts, fmt.Sprintf("%d postponed", counts.IsPostponed))
|
parts = append(parts, fmt.Sprintf("%d postponed with %d bets", counts.StatusPostponedCount, counts.StatusPostponedBets))
|
||||||
}
|
}
|
||||||
if counts.IsRemoved > 0 {
|
if counts.StatusRemovedCount > 0 {
|
||||||
parts = append(parts, fmt.Sprintf("%d removed", counts.IsRemoved))
|
parts = append(parts, fmt.Sprintf("%d removed with %d bets", counts.StatusRemovedCount, counts.StatusRemovedBets))
|
||||||
|
}
|
||||||
|
if counts.StatusEndedCount > 0 {
|
||||||
|
parts = append(parts, fmt.Sprintf("%d ended with %d bets", counts.StatusEndedCount, counts.StatusEndedBets))
|
||||||
}
|
}
|
||||||
|
|
||||||
headline := "⚠️ Issues Found Processing Event Results"
|
headline := "⚠️ Issues Found Processing Event Results"
|
||||||
|
|
@ -513,7 +597,7 @@ func buildHeadlineAndMessage(counts domain.ResultStatusCounts) (string, string)
|
||||||
|
|
||||||
func (s *Service) SendAdminResultStatusErrorNotification(
|
func (s *Service) SendAdminResultStatusErrorNotification(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
counts domain.ResultStatusCounts,
|
counts domain.ResultLog,
|
||||||
) error {
|
) error {
|
||||||
|
|
||||||
superAdmins, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
|
superAdmins, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
|
||||||
|
|
@ -558,6 +642,14 @@ func (s *Service) SendAdminResultStatusErrorNotification(
|
||||||
)
|
)
|
||||||
sendErrors = append(sendErrors, err)
|
sendErrors = append(sendErrors, err)
|
||||||
}
|
}
|
||||||
|
notification.DeliveryChannel = domain.DeliveryChannelEmail
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
||||||
|
s.mongoLogger.Error("failed to send admin email notification",
|
||||||
|
zap.Int64("admin_id", user.ID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
sendErrors = append(sendErrors, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sendErrors) > 0 {
|
if len(sendErrors) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -51,19 +51,19 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// {
|
{
|
||||||
// spec: "0 */5 * * * *", // Every 5 Minutes
|
spec: "0 */5 * * * *", // Every 5 Minutes
|
||||||
// task: func() {
|
task: func() {
|
||||||
// mongoLogger.Info("Began updating all expired events status")
|
mongoLogger.Info("Began updating all expired events status")
|
||||||
// if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil {
|
if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil {
|
||||||
// mongoLogger.Error("Failed to update expired events status",
|
mongoLogger.Error("Failed to update expired events status",
|
||||||
// zap.Error(err),
|
zap.Error(err),
|
||||||
// )
|
)
|
||||||
// } else {
|
} else {
|
||||||
// mongoLogger.Info("Successfully updated expired events")
|
mongoLogger.Info("Successfully updated expired events")
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
spec: "0 */15 * * * *", // Every 15 Minutes
|
spec: "0 */15 * * * *", // Every 15 Minutes
|
||||||
task: func() {
|
task: func() {
|
||||||
|
|
@ -77,6 +77,19 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
spec: "0 0 * * * *", // Every Day
|
||||||
|
task: func() {
|
||||||
|
mongoLogger.Info("Send daily result notification")
|
||||||
|
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-24*time.Hour)); err != nil {
|
||||||
|
mongoLogger.Error("Failed to process result",
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mongoLogger.Info("Successfully processed all event result outcomes")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, job := range schedule {
|
for _, job := range schedule {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user