package postgres import ( "context" "encoding/json" "fmt" "github.com/jackc/pgx/v5/pgxpool" "cryptoHermes/internal/entity" ) const coinglassSource = "coinglass" type CoinglassLiqHeatMapRepo struct { pool pgxPool } func NewCoinglassLiqHeatMapRepo(pool *pgxpool.Pool) *CoinglassLiqHeatMapRepo { return &CoinglassLiqHeatMapRepo{pool: pool} } const cgLiqHeatMapUpsertSQL = ` INSERT INTO coinglass_liq_heatmap ( source, symbol, interval, timestamp, price_min, price_max, raw_grid, updated_at ) VALUES ( $1, $2, $3, $4, NULLIF($5, '')::NUMERIC, NULLIF($6, '')::NUMERIC, $7::JSONB, now() ) ON CONFLICT (source, symbol, interval, timestamp) DO UPDATE SET price_min = EXCLUDED.price_min, price_max = EXCLUDED.price_max, raw_grid = EXCLUDED.raw_grid, updated_at = now() ` func (r *CoinglassLiqHeatMapRepo) UpsertMany(ctx context.Context, items []entity.CGLiqHeatMap) error { if len(items) == 0 { return nil } tx, err := r.pool.Begin(ctx) if err != nil { return fmt.Errorf("begin: %w", err) } defer tx.Rollback(ctx) for _, item := range items { if item.Symbol == "" || item.Interval == "" || item.Timestamp == 0 || !json.Valid(item.RawGrid) { continue } if _, err := tx.Exec(ctx, cgLiqHeatMapUpsertSQL, coinglassSource, item.Symbol, item.Interval, item.Timestamp, item.PriceMin, item.PriceMax, string(item.RawGrid), ); err != nil { return fmt.Errorf("upsert coinglass liq heatmap %s/%s@%d: %w", item.Symbol, item.Interval, item.Timestamp, err) } } return tx.Commit(ctx) } const cgLiqHeatMapFindSQL = ` SELECT symbol, interval, timestamp, COALESCE(price_min::text, ''), COALESCE(price_max::text, ''), raw_grid::text FROM coinglass_liq_heatmap WHERE source = $1 AND symbol = $2 AND interval = $3 ORDER BY timestamp DESC LIMIT $4 ` func (r *CoinglassLiqHeatMapRepo) FindRecent(ctx context.Context, symbol, interval string, limit int) ([]entity.CGLiqHeatMap, error) { if limit <= 0 { limit = 288 } rows, err := r.pool.Query(ctx, cgLiqHeatMapFindSQL, coinglassSource, symbol, interval, limit) if err != nil { return nil, fmt.Errorf("query: %w", err) } defer rows.Close() out := make([]entity.CGLiqHeatMap, 0, limit) for rows.Next() { var ( item entity.CGLiqHeatMap raw string ) if err := rows.Scan(&item.Symbol, &item.Interval, &item.Timestamp, &item.PriceMin, &item.PriceMax, &raw); err != nil { return nil, fmt.Errorf("scan: %w", err) } item.RawGrid = json.RawMessage(raw) out = append(out, item) } 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() }