package merch import ( "context" "database/sql" "fmt" "time" ) type Prices interface { insertPrices(ctx context.Context, now time.Time, updatedAt sql.NullTime, prices []Price) error getPricesWithDays(ctx context.Context, userId int64, days time.Time) ([]Price, error) getDistinctPrices(ctx context.Context, userId int64, merchUuid string, days time.Time) ([]Price, error) getZeroPrices(ctx context.Context, userId int64) ([]ZeroPrice, error) deleteZeroPricesPeriod(ctx context.Context, userId int64, start, end time.Time, now sql.NullTime) error deleteZeroPrices(ctx context.Context, userId int64, now sql.NullTime, list []int64) error } func (r *repo) insertPrices(ctx context.Context, now time.Time, updatedAt sql.NullTime, prices []Price) error { q := ` INSERT INTO merch_prices (created_at, updated_at, merch_id, origin_id, price) SELECT $1, $2, src.merch_id, src.origin_id, src.price FROM UNNEST( $3::bigint[], $4::bigint[], $5::int[] ) AS src (merch_id, origin_id, price) ` var ( merchIds []int64 originIds []int64 priceValues []int ) for _, price := range prices { merchIds = append(merchIds, price.MerchId) originIds = append(originIds, price.OriginId) priceValues = append(priceValues, price.Price) } _, err := r.db.Exec(ctx, q, now, updatedAt, merchIds, originIds, priceValues) if err != nil { return err } return nil } func (r *repo) getPricesWithDays(ctx context.Context, userId int64, days time.Time) ([]Price, error) { q := ` SELECT mp.created_at, mp.merch_id, mp.price, mp.origin_id FROM merch_prices AS mp JOIN merch AS m ON m.id = mp.merch_id WHERE m.user_id = $1 AND mp.created_at > $2 AND mp.deleted_at IS NULL AND m.deleted_at IS NULL ` rows, err := r.db.Query(ctx, q, userId, days) if err != nil { return nil, err } var result []Price for rows.Next() { var p Price if err = rows.Scan(&p.CreatedAt, &p.MerchId, &p.Price, &p.OriginId); err != nil { rows.Close() return nil, err } result = append(result, p) } rows.Close() if err = rows.Err(); err != nil { return nil, err } return result, nil } func (r *repo) getDistinctPrices(ctx context.Context, userId int64, merchUuid string, days time.Time) ([]Price, error) { q := ` SELECT price, created_at, origin_id FROM ( SELECT DISTINCT ON (price) price, created_at, origin_id FROM merch_prices WHERE merch_id = ( SELECT id FROM merch WHERE merch_uuid = $1 AND user_id = $2 AND deleted_at IS NULL ) AND deleted_at IS NULL AND created_at > $3 ) ORDER BY created_at; ` fmt.Println(merchUuid, userId, days) rows, err := r.db.Query(ctx, q, merchUuid, userId, days) if err != nil { return nil, err } var result []Price for rows.Next() { var p Price if err = rows.Scan(&p.Price, &p.CreatedAt, &p.OriginId); err != nil { rows.Close() return nil, err } result = append(result, p) } rows.Close() if err = rows.Err(); err != nil { return nil, err } return result, nil } func (r *repo) getZeroPrices(ctx context.Context, userId int64) ([]ZeroPrice, error) { q := ` WITH price_with_neighbors AS ( SELECT p.id, p.created_at, p.merch_id, p.price, p.origin_id, m.name, m.merch_uuid, LAG(price) OVER (PARTITION BY p.merch_id, p.origin_id ORDER BY p.created_at, p.id) AS prev_price, LEAD(price) OVER (PARTITION BY p.merch_id, p.origin_id ORDER BY p.created_at, p.id) AS next_price FROM merch_prices AS p JOIN merch as m ON m.id = p.merch_id WHERE p.deleted_at IS NULL AND m.deleted_at IS NULL AND m.user_id = $1) SELECT pwn.id, pwn.created_at, pwn.merch_uuid, pwn.name, mo.name FROM price_with_neighbors AS pwn JOIN merch_origins AS mo ON mo.id = pwn.origin_id WHERE pwn.price = 0 AND pwn.prev_price IS NOT NULL AND pwn.prev_price > 0 AND pwn.next_price IS NOT NULL AND pwn.next_price > 0 ORDER BY pwn.created_at DESC ` rows, err := r.db.Query(ctx, q, userId) if err != nil { return nil, err } var zeroPrices []ZeroPrice for rows.Next() { var p ZeroPrice if err = rows.Scan(&p.Id, &p.CreatedAt, &p.MerchUuid, &p.Name, &p.Origin); err != nil { rows.Close() return nil, err } zeroPrices = append(zeroPrices, p) } rows.Close() if err = rows.Err(); err != nil { return nil, err } return zeroPrices, nil } func (r *repo) deleteZeroPricesPeriod(ctx context.Context, userId int64, start, end time.Time, now sql.NullTime) error { q := ` UPDATE merch_prices SET deleted_at = $1 FROM merch WHERE merch_prices.merch_id = merch.id AND merch.user_id = $2 AND merch_prices.price = 0 AND merch_prices.deleted_at IS NULL AND merch_prices.created_at BETWEEN $3 AND $4; ` _, err := r.db.Exec(ctx, q, now, userId, start, end) if err != nil { return err } return nil } func (r *repo) deleteZeroPrices(ctx context.Context, userId int64, now sql.NullTime, list []int64) error { q := ` UPDATE merch_prices mp SET deleted_at = $1 FROM merch m WHERE mp.id = ANY($2) AND mp.merch_id = m.id AND m.user_id = $3 ` _, err := r.db.Exec(ctx, q, now, list, userId) if err != nil { return err } return nil }