package postgres import ( "context" "fmt" "github.com/jackc/pgx/v5/pgxpool" "cryptoHermes/internal/entity" ) type OpenInterestRepo struct { pool *pgxpool.Pool } func NewOpenInterestRepo(pool *pgxpool.Pool) *OpenInterestRepo { return &OpenInterestRepo{pool: pool} } const oiUpsertSQL = ` INSERT INTO open_interest (source, symbol, period, timestamp, open_interest, open_interest_value) VALUES ($1, $2, $3, $4, $5, NULLIF($6, '')::NUMERIC) ON CONFLICT (source, symbol, period, timestamp) DO UPDATE SET open_interest = EXCLUDED.open_interest, open_interest_value = EXCLUDED.open_interest_value ` func (r *OpenInterestRepo) UpsertMany(ctx context.Context, items []entity.OpenInterest) error { if len(items) == 0 { return nil } tx, err := r.pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) for _, o := range items { if o.OpenInterest == "" || o.Timestamp == 0 { continue } if _, err := tx.Exec(ctx, oiUpsertSQL, o.Source, o.Symbol, o.Period, o.Timestamp, o.OpenInterest, o.OpenInterestValue, ); err != nil { return fmt.Errorf("upsert oi %s/%s@%d: %w", o.Symbol, o.Period, o.Timestamp, err) } } return tx.Commit(ctx) } const oiFindSQL = ` SELECT source, symbol, period, timestamp, open_interest::text, COALESCE(open_interest_value::text, '') FROM open_interest WHERE symbol = $1 AND period = $2 ORDER BY timestamp DESC LIMIT $3 ` func (r *OpenInterestRepo) FindRecent(ctx context.Context, symbol, period string, limit int) ([]entity.OpenInterest, error) { if limit <= 0 { limit = 100 } rows, err := r.pool.Query(ctx, oiFindSQL, symbol, period, limit) if err != nil { return nil, err } defer rows.Close() out := make([]entity.OpenInterest, 0, limit) for rows.Next() { var o entity.OpenInterest if err := rows.Scan(&o.Source, &o.Symbol, &o.Period, &o.Timestamp, &o.OpenInterest, &o.OpenInterestValue); err != nil { return nil, err } out = append(out, o) } for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 { out[i], out[j] = out[j], out[i] } return out, rows.Err() }