111 lines
2.5 KiB
Go
111 lines
2.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/kjannette/trahn-backend/internal/models"
|
|
)
|
|
|
|
type PriceRepo struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewPriceRepo(pool *pgxpool.Pool) *PriceRepo {
|
|
return &PriceRepo{pool: pool}
|
|
}
|
|
|
|
func (r *PriceRepo) Record(ctx context.Context, price float64, ts time.Time) (*models.PricePoint, error) {
|
|
td := TradingDay(ts)
|
|
row := r.pool.QueryRow(ctx,
|
|
`INSERT INTO price_history (timestamp, price, trading_day, source)
|
|
VALUES ($1, $2, $3, $4) RETURNING *`,
|
|
ts, price, td, "coingecko",
|
|
)
|
|
return scanPrice(row)
|
|
}
|
|
|
|
func (r *PriceRepo) GetByDay(ctx context.Context, tradingDay string) ([]models.PricePoint, error) {
|
|
rows, err := r.pool.Query(ctx,
|
|
`SELECT * FROM price_history WHERE trading_day = $1 ORDER BY timestamp ASC`,
|
|
tradingDay,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
return collectPrices(rows)
|
|
}
|
|
|
|
func (r *PriceRepo) GetAvailableDays(ctx context.Context) ([]string, error) {
|
|
rows, err := r.pool.Query(ctx,
|
|
`SELECT DISTINCT trading_day FROM price_history ORDER BY trading_day DESC LIMIT 30`,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var days []string
|
|
for rows.Next() {
|
|
var d time.Time
|
|
if err := rows.Scan(&d); err != nil {
|
|
return nil, err
|
|
}
|
|
days = append(days, d.Format("2006-01-02"))
|
|
}
|
|
return days, rows.Err()
|
|
}
|
|
|
|
func (r *PriceRepo) GetLatest(ctx context.Context) (*models.PricePoint, error) {
|
|
row := r.pool.QueryRow(ctx,
|
|
`SELECT * FROM price_history ORDER BY timestamp DESC LIMIT 1`,
|
|
)
|
|
p, err := scanPrice(row)
|
|
if err != nil {
|
|
if err.Error() == "no rows in result set" {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// --- scan helpers ---
|
|
|
|
type scannable interface {
|
|
Scan(dest ...any) error
|
|
}
|
|
|
|
func scanPrice(row scannable) (*models.PricePoint, error) {
|
|
var p models.PricePoint
|
|
var td time.Time
|
|
err := row.Scan(&p.ID, &p.Timestamp, &p.Price, &td, &p.Source, &p.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.TradingDay = td.Format("2006-01-02")
|
|
return &p, nil
|
|
}
|
|
|
|
type rowsIter interface {
|
|
Next() bool
|
|
Scan(dest ...any) error
|
|
Err() error
|
|
}
|
|
|
|
func collectPrices(rows rowsIter) ([]models.PricePoint, error) {
|
|
var out []models.PricePoint
|
|
for rows.Next() {
|
|
var p models.PricePoint
|
|
var td time.Time
|
|
if err := rows.Scan(&p.ID, &p.Timestamp, &p.Price, &td, &p.Source, &p.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
p.TradingDay = td.Format("2006-01-02")
|
|
out = append(out, p)
|
|
}
|
|
return out, rows.Err()
|
|
}
|