merch-api/internal/merch/repository_merch.go

304 lines
7.8 KiB
Go
Raw Normal View History

2026-03-11 19:40:21 +03:00
package merch
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
)
type MerchRepo interface {
// createMerch creates new merch record and inserts extra data if given
createMerch(ctx context.Context, merch *Merch, extra []ExtraData) error
2026-03-16 21:09:01 +03:00
getSingleMerch(ctx context.Context, userId int64, merchUuid string) (*Merch, error)
getSingleMerchExtraData(ctx context.Context, merchId int64) ([]originLink, error)
2026-03-11 19:40:21 +03:00
// getMany returns list of only main merch record, without origins extra data
2026-03-13 16:52:39 +03:00
getMany(ctx context.Context, userId int64) ([]getMerchInternal, error)
2026-03-11 19:40:21 +03:00
getMerchIdByUuid(ctx context.Context, userId int64, uuid string) (int64, error)
getMerchUuidMap(ctx context.Context, merchUuids []string) (map[string]int64, error)
getAllUserMerch(ctx context.Context, userId int64) ([]Merch, error)
updateMerch(ctx context.Context, userId int64, merch *updateMerchDTO) (*merchDTO, error)
updateExtraData(ctx context.Context, merchId int64, insertData []ExtraData) ([]ExtraData, error)
// deleteOneMerchRecord sets deleted_at in merch + extra tables
deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string, delTime time.Time) error
}
func (r *repo) createMerch(ctx context.Context, merch *Merch, extra []ExtraData) error {
tx, err := r.db.Begin(ctx)
if err != nil {
return err
}
qMerch := `INSERT INTO merch (created_at, updated_at, deleted_at, merch_uuid, user_id, name)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`
var merchId int64
err = tx.
QueryRow(ctx, qMerch, merch.CreatedAt, merch.UpdatedAt, merch.DeletedAt, merch.MerchUuid, merch.UserId, merch.Name).
Scan(&merchId)
if err != nil {
tx.Rollback(ctx)
return err
}
if extra == nil {
return tx.Commit(ctx)
}
countArgs := 1
var insertFields []string
var insertArgs []interface{}
for _, item := range extra {
insertFields = append(insertFields, fmt.Sprintf("($%v, $%v, $%v, $%v, $%v, $%v)",
countArgs, countArgs+1, countArgs+2, countArgs+3, countArgs+4, countArgs+5))
insertArgs = append(insertArgs, item.CreatedAt, item.UpdatedAt, item.DeletedAt, merchId, item.OriginId, item.URL)
countArgs += 6
}
qExtra := fmt.Sprintf(
"INSERT INTO merch_extra_data (created_at, updated_at, deleted_at, merch_id, origin_id, url) VALUES %v",
strings.Join(insertFields, ","))
_, err = tx.Exec(ctx, qExtra, insertArgs...)
if err != nil {
tx.Rollback(ctx)
return err
}
return tx.Commit(ctx)
}
2026-03-16 21:09:01 +03:00
func (r *repo) getSingleMerch(ctx context.Context, userId int64, merchUuid string) (*Merch, error) {
q := `SELECT id, name FROM merch WHERE merch_uuid = $1 AND user_id = $2 AND deleted_at IS NULL`
var m Merch
if err := r.db.QueryRow(ctx, q, merchUuid, userId).Scan(&m.Id, &m.Name); err != nil {
return nil, err
}
return &m, nil
}
func (r *repo) getSingleMerchExtraData(ctx context.Context, merchId int64) ([]originLink, error) {
q := `
SELECT mo.name, med.url
FROM merch_extra_data AS med
JOIN merch_origins AS mo ON mo.id = med.origin_id
WHERE med.merch_id = $1
AND med.deleted_at IS NULL
AND mo.deleted_at IS NULL
`
rows, err := r.db.Query(ctx, q, merchId)
if err != nil {
return nil, err
}
var result []originLink
for rows.Next() {
var o originLink
if err = rows.Scan(&o.Origin, &o.Link); err != nil {
rows.Close()
return nil, err
}
result = append(result, o)
}
rows.Close()
if err = rows.Err(); err != nil {
return nil, err
}
return result, nil
}
2026-03-13 16:52:39 +03:00
func (r *repo) getMany(ctx context.Context, userId int64) ([]getMerchInternal, error) {
q := `SELECT id, merch_uuid, name FROM merch WHERE deleted_at IS NULL AND user_id = $1`
2026-03-11 19:40:21 +03:00
rows, err := r.db.Query(ctx, q, userId)
if err != nil {
return nil, err
}
2026-03-13 16:52:39 +03:00
var result []getMerchInternal
2026-03-11 19:40:21 +03:00
for rows.Next() {
2026-03-13 16:52:39 +03:00
var m getMerchInternal
if err = rows.Scan(&m.Id, &m.MerchUuid, &m.Name); err != nil {
2026-03-16 21:09:01 +03:00
rows.Close()
2026-03-11 19:40:21 +03:00
return nil, err
}
result = append(result, m)
}
2026-03-16 21:09:01 +03:00
rows.Close()
2026-03-11 19:40:21 +03:00
if err = rows.Err(); err != nil {
return nil, err
}
return result, nil
}
func (r *repo) getMerchIdByUuid(ctx context.Context, userId int64, uuid string) (int64, error) {
q := `SELECT id FROM merch WHERE deleted_at IS NULL AND merch_uuid = $1 AND user_id = $2`
var merchId int64
if err := r.db.QueryRow(ctx, q, uuid, userId).Scan(&merchId); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return 0, nil
} else {
return 0, err
}
}
return merchId, nil
}
func (r *repo) getMerchUuidMap(ctx context.Context, merchUuids []string) (map[string]int64, error) {
q := `SELECT merch_uuid, id FROM merch WHERE deleted_at IS NULL AND merch_uuid = ANY($1)`
rows, err := r.db.Query(ctx, q, merchUuids)
if err != nil {
return nil, err
}
var merchUuidMap map[string]int64
for rows.Next() {
var (
uuid string
id int64
)
if err = rows.Scan(&uuid, &id); err != nil {
rows.Close()
return nil, err
}
merchUuidMap[uuid] = id
}
rows.Close()
if err = rows.Err(); err != nil {
return nil, err
}
return merchUuidMap, nil
}
func (r *repo) getAllUserMerch(ctx context.Context, userId int64) ([]Merch, error) {
var userMerch []Merch
q := `SELECT id, merch_uuid, name FROM merch WHERE user_id = $1 AND deleted_at IS NULL`
rows, err := r.db.Query(ctx, q, userId)
if err != nil {
return nil, err
}
for rows.Next() {
var m Merch
if err = rows.Scan(&m.Id, &m.MerchUuid, &m.Name); err != nil {
rows.Close()
return nil, err
}
userMerch = append(userMerch, m)
}
rows.Close()
if err = rows.Err(); err != nil {
return nil, err
}
return userMerch, nil
}
func (r *repo) deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string, delTime time.Time) error {
tx, err := r.db.Begin(ctx)
if err != nil {
return err
}
var merch_id int64
qMerch := `UPDATE merch SET deleted_at = $1 WHERE merch_uuid = $2 AND user_id = $3 RETURNING id`
if err = tx.QueryRow(ctx, qMerch, delTime, merchUuid, userId).Scan(&merch_id); err != nil {
tx.Rollback(ctx)
return err
}
if merch_id != 0 {
qExtra := `UPDATE merch_extra_data SET deleted_at = $1 WHERE merch_id = $2`
_, err = tx.Exec(ctx, qExtra, delTime, merch_id)
}
return tx.Commit(ctx)
}
func (r *repo) updateMerch(ctx context.Context, userId int64, merch *updateMerchDTO) (*merchDTO, error) {
q := `UPDATE merch SET name = $1 WHERE merch_uuid = $2 AND user_id = $3
RETURNING created_at, updated_at, merch_uuid, name`
var result merchDTO
if err := r.db.
QueryRow(ctx, q, merch.Name, merch.MerchUuid, userId).
Scan(&result.CreatedAt, &result.UpdatedAt, &result.MerchUuid, &result.Name); err != nil {
return nil, err
}
return &result, nil
}
func (r *repo) updateExtraData(ctx context.Context, merchId int64, insertData []ExtraData) ([]ExtraData, error) {
q := `
INSERT INTO merch_extra_data (merch_id, origin_id, url, created_at, updated_at)
SELECT $1, src.origin_id, src.new_url, src.created_at, src.updated_at
FROM UNNEST(
$2::text[],
$3::bigint[],
$4::timestamptz[],
$5::timestamptz[])
AS src(new_url, origin_id, created_at, updated_at)
ON CONFLICT (merch_id, origin_id) DO UPDATE SET url = COALESCE(NULLIF(EXCLUDED.url, ''), merch_extra_data.url), updated_at = NOW()
RETURNING created_at, updated_at, merch_id, origin_id, url;
`
var (
urls []string
origins []int64
createdAt []time.Time
updatedAt []sql.NullTime
)
for _, data := range insertData {
urls = append(urls, data.URL)
origins = append(origins, data.OriginId)
createdAt = append(createdAt, data.CreatedAt)
updatedAt = append(updatedAt, data.UpdatedAt)
}
rows, err := r.db.Query(ctx, q, merchId, urls, origins, createdAt, updatedAt)
if err != nil {
return nil, err
}
var result []ExtraData
for rows.Next() {
var m ExtraData
if err = rows.Scan(&m.CreatedAt, &m.UpdatedAt, &m.MerchId, &m.OriginId, &m.URL); err != nil {
return nil, err
}
result = append(result, m)
}
rows.Close() //must be before rows.Err check!
if err = rows.Err(); err != nil {
return nil, err
}
return result, nil
}