crypto_alert_bot/internal/repository/postgresql/instrument.go
2026-04-28 11:42:52 +03:00

100 lines
3.3 KiB
Go

package postgresql
import (
"context"
"fmt"
"gitea.computernetthings.ru/yash/crypto_alert_bot/internal/entities"
)
const instrumentListQuery = `
select i.id, c_base.symbol, c_quote.symbol, i.is_global
from instrument i
join currency c_base on c_base.id = i.base_currency_id
join currency c_quote on c_quote.id = i.quoted_currency_id
where i.is_global = true
or exists (
select 1 from user_instrument ui
where ui.instrument_id = i.id and ui.user_id = $1
)
order by i.is_global desc, i.id asc
offset $2 limit $3`
func (p *Postgresql) InstrumentList(ctx context.Context, userID entities.UserID, offset, limit int) ([]entities.Instrument, error) {
rows, err := p.db.Query(ctx, instrumentListQuery, userID, offset, limit)
if err != nil {
return nil, fmt.Errorf("failed to exec instrumentListQuery: %w", err)
}
defer rows.Close()
var instruments []entities.Instrument
for rows.Next() {
var inst entities.Instrument
if err := rows.Scan(&inst.ID, &inst.BaseCurrency, &inst.QuoteCurrency, &inst.IsGlobal); err != nil {
return nil, fmt.Errorf("failed to scan instrument row: %w", err)
}
instruments = append(instruments, inst)
}
return instruments, nil
}
// createInstrumentQuery upserts both currency symbols then the instrument itself.
// It always returns an ID — the newly inserted row or the existing one on conflict.
const createInstrumentQuery = `
with upsert_base as (
insert into currency(symbol) values($1)
on conflict(symbol) do update set symbol = excluded.symbol
returning id
),
upsert_quote as (
insert into currency(symbol) values($2)
on conflict(symbol) do update set symbol = excluded.symbol
returning id
),
ins as (
insert into instrument(base_currency_id, quoted_currency_id, is_global)
select upsert_base.id, upsert_quote.id, false
from upsert_base, upsert_quote
on conflict (base_currency_id, quoted_currency_id) do nothing
returning id
)
select coalesce(
(select id from ins),
(select i.id from instrument i
where i.base_currency_id = (select id from upsert_base)
and i.quoted_currency_id = (select id from upsert_quote))
)`
func (p *Postgresql) CreateInstrument(ctx context.Context, instrument *entities.Instrument) (entities.InstrumentID, error) {
var id entities.InstrumentID
err := p.db.QueryRow(ctx, createInstrumentQuery, instrument.BaseCurrency, instrument.QuoteCurrency).Scan(&id)
if err != nil {
return "", fmt.Errorf("failed to exec createInstrumentQuery: %w", err)
}
return id, nil
}
const addUserInstrumentQuery = `
insert into user_instrument(user_id, instrument_id)
values ($1, $2)
on conflict (user_id, instrument_id) do nothing`
func (p *Postgresql) AddUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error {
_, err := p.db.Exec(ctx, addUserInstrumentQuery, userID, instrumentID)
if err != nil {
return fmt.Errorf("failed to exec addUserInstrumentQuery: %w", err)
}
return nil
}
const removeUserInstrumentQuery = `
delete from user_instrument where user_id = $1 and instrument_id = $2`
func (p *Postgresql) RemoveUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error {
_, err := p.db.Exec(ctx, removeUserInstrumentQuery, userID, instrumentID)
if err != nil {
return fmt.Errorf("failed to exec removeUserInstrumentQuery: %w", err)
}
return nil
}