merch-api/internal/merch/repository_prices.go
2026-04-02 17:57:48 +03:00

210 lines
5.2 KiB
Go

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
}