[hw-4] add postgres db

This commit is contained in:
Никита Шубин
2025-06-26 12:08:46 +00:00
parent 3ebaad5558
commit 77ed9fcf85
46 changed files with 1582 additions and 369 deletions

View File

@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,5 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc

View File

@@ -0,0 +1,18 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
)
type Querier interface {
OrderAddItem(ctx context.Context, arg *OrderAddItemParams) error
OrderCreate(ctx context.Context, arg *OrderCreateParams) (int64, error)
OrderGetByID(ctx context.Context, id int64) ([]*OrderGetByIDRow, error)
OrderSetStatus(ctx context.Context, arg *OrderSetStatusParams) (int64, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,25 @@
-- name: OrderCreate :one
insert into orders (status_name, user_id)
values ($1, $2)
returning id;
-- name: OrderAddItem :exec
insert into order_items (order_id, sku, count)
select $1, $2, $3;
-- name: OrderSetStatus :execrows
update orders
set status_name = $2
where id = $1;
-- name: OrderGetByID :many
select
o.id as order_id,
o.status_name as status,
o.user_id,
oi.sku,
oi.count
from orders o
left join order_items oi on oi.order_id = o.id
where o.id = $1
order by oi.id;

View File

@@ -0,0 +1,110 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: query.sql
package sqlc
import (
"context"
)
const orderAddItem = `-- name: OrderAddItem :exec
insert into order_items (order_id, sku, count)
select $1, $2, $3
`
type OrderAddItemParams struct {
OrderID int64
Sku int64
Count int64
}
func (q *Queries) OrderAddItem(ctx context.Context, arg *OrderAddItemParams) error {
_, err := q.db.Exec(ctx, orderAddItem, arg.OrderID, arg.Sku, arg.Count)
return err
}
const orderCreate = `-- name: OrderCreate :one
insert into orders (status_name, user_id)
values ($1, $2)
returning id
`
type OrderCreateParams struct {
StatusName string
UserID int64
}
func (q *Queries) OrderCreate(ctx context.Context, arg *OrderCreateParams) (int64, error) {
row := q.db.QueryRow(ctx, orderCreate, arg.StatusName, arg.UserID)
var id int64
err := row.Scan(&id)
return id, err
}
const orderGetByID = `-- name: OrderGetByID :many
select
o.id as order_id,
o.status_name as status,
o.user_id,
oi.sku,
oi.count
from orders o
left join order_items oi on oi.order_id = o.id
where o.id = $1
order by oi.id
`
type OrderGetByIDRow struct {
OrderID int64
Status string
UserID int64
Sku *int64
Count *int64
}
func (q *Queries) OrderGetByID(ctx context.Context, id int64) ([]*OrderGetByIDRow, error) {
rows, err := q.db.Query(ctx, orderGetByID, id)
if err != nil {
return nil, err
}
defer rows.Close()
var items []*OrderGetByIDRow
for rows.Next() {
var i OrderGetByIDRow
if err := rows.Scan(
&i.OrderID,
&i.Status,
&i.UserID,
&i.Sku,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, &i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const orderSetStatus = `-- name: OrderSetStatus :execrows
update orders
set status_name = $2
where id = $1
`
type OrderSetStatusParams struct {
ID int64
StatusName string
}
func (q *Queries) OrderSetStatus(ctx context.Context, arg *OrderSetStatusParams) (int64, error) {
result, err := q.db.Exec(ctx, orderSetStatus, arg.ID, arg.StatusName)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}

View File

@@ -0,0 +1,105 @@
package sqlc
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"route256/loms/internal/domain/entity"
"route256/loms/internal/domain/model"
"route256/loms/internal/domain/service"
"route256/loms/internal/infra/postgres"
)
type orderRepo struct {
pool *pgxpool.Pool
}
func NewOrderRepository(pool *pgxpool.Pool) service.OrderRepository {
return &orderRepo{
pool: pool,
}
}
func (o *orderRepo) GetQuerier(ctx context.Context) *Queries {
tx, ok := postgres.TxFromCtx(ctx)
if ok {
return New(tx)
}
return New(o.pool)
}
func (o *orderRepo) OrderCreate(ctx context.Context, order *entity.Order) (entity.ID, error) {
querier := o.GetQuerier(ctx)
id, err := querier.OrderCreate(ctx, &OrderCreateParams{
StatusName: order.Status,
UserID: int64(order.UserID),
})
if err != nil {
return 0, fmt.Errorf("querier.OrderCreate: %w", err)
}
for _, item := range order.Items {
if err := querier.OrderAddItem(ctx, &OrderAddItemParams{
OrderID: id,
Sku: int64(item.ID),
Count: int64(item.Count),
}); err != nil {
return 0, fmt.Errorf("querier.OrderAddItem: %w", err)
}
}
return entity.ID(id), nil
}
func (o *orderRepo) OrderGetByID(ctx context.Context, orderID entity.ID) (*entity.Order, error) {
querier := o.GetQuerier(ctx)
rows, err := querier.OrderGetByID(ctx, int64(orderID))
if err != nil {
return nil, fmt.Errorf("querier.OrderGetByID: %w", err)
}
if len(rows) == 0 {
return nil, model.ErrOrderNotFound
}
items := make([]entity.OrderItem, len(rows))
for i, row := range rows {
items[i] = entity.OrderItem{
ID: entity.Sku(*row.Sku),
//nolint:gosec // will not overflow, uint32 is stored as int64
Count: uint32(*row.Count),
}
}
order := &entity.Order{
OrderID: orderID,
Status: rows[0].Status,
UserID: entity.ID(rows[0].UserID),
Items: items,
}
return order, nil
}
func (o *orderRepo) OrderSetStatus(ctx context.Context, orderID entity.ID, newStatus string) error {
querier := o.GetQuerier(ctx)
rows, err := querier.OrderSetStatus(ctx, &OrderSetStatusParams{
ID: int64(orderID),
StatusName: newStatus,
})
if err != nil {
return fmt.Errorf("querier.OrderSetStatus: %w", err)
}
if rows == 0 {
return model.ErrOrderNotFound
}
return nil
}

View File

@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,11 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
type Stock struct {
Sku int64
TotalCount int64
Reserved int64
}

View File

@@ -0,0 +1,18 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
)
type Querier interface {
StockCancel(ctx context.Context, arg *StockCancelParams) (int64, error)
StockGetByID(ctx context.Context, sku int64) (*Stock, error)
StockReserve(ctx context.Context, arg *StockReserveParams) (int64, error)
StockReserveRemove(ctx context.Context, arg *StockReserveRemoveParams) (int64, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,24 @@
-- name: StockGetByID :one
select sku, total_count, reserved
from stocks
where sku = $1
limit 1;
-- name: StockReserve :execrows
update stocks
set reserved = reserved + $2
where sku = $1
and total_count >= reserved + $2;
-- name: StockReserveRemove :execrows
update stocks
set reserved = reserved - $2,
total_count = total_count - $2
where sku = $1
and reserved >= $2 and total_count >= $2;
-- name: StockCancel :execrows
update stocks
set reserved = reserved - $2
where sku = $1
and reserved >= $2;

View File

@@ -0,0 +1,85 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: query.sql
package sqlc
import (
"context"
)
const stockCancel = `-- name: StockCancel :execrows
update stocks
set reserved = reserved - $2
where sku = $1
and reserved >= $2
`
type StockCancelParams struct {
Sku int64
Reserved int64
}
func (q *Queries) StockCancel(ctx context.Context, arg *StockCancelParams) (int64, error) {
result, err := q.db.Exec(ctx, stockCancel, arg.Sku, arg.Reserved)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const stockGetByID = `-- name: StockGetByID :one
select sku, total_count, reserved
from stocks
where sku = $1
limit 1
`
func (q *Queries) StockGetByID(ctx context.Context, sku int64) (*Stock, error) {
row := q.db.QueryRow(ctx, stockGetByID, sku)
var i Stock
err := row.Scan(&i.Sku, &i.TotalCount, &i.Reserved)
return &i, err
}
const stockReserve = `-- name: StockReserve :execrows
update stocks
set reserved = reserved + $2
where sku = $1
and total_count >= reserved + $2
`
type StockReserveParams struct {
Sku int64
Reserved int64
}
func (q *Queries) StockReserve(ctx context.Context, arg *StockReserveParams) (int64, error) {
result, err := q.db.Exec(ctx, stockReserve, arg.Sku, arg.Reserved)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const stockReserveRemove = `-- name: StockReserveRemove :execrows
update stocks
set reserved = reserved - $2,
total_count = total_count - $2
where sku = $1
and reserved >= $2 and total_count >= $2
`
type StockReserveRemoveParams struct {
Sku int64
Reserved int64
}
func (q *Queries) StockReserveRemove(ctx context.Context, arg *StockReserveRemoveParams) (int64, error) {
result, err := q.db.Exec(ctx, stockReserveRemove, arg.Sku, arg.Reserved)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}

View File

@@ -0,0 +1,137 @@
package sqlc
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"route256/loms/internal/domain/entity"
"route256/loms/internal/domain/model"
"route256/loms/internal/domain/service"
"route256/loms/internal/infra/postgres"
"route256/loms/internal/infra/tools"
)
type stockRepo struct {
read *pgxpool.Pool
write *pgxpool.Pool
}
func NewStockRepository(write, read *pgxpool.Pool) service.StockRepository {
return &stockRepo{
read: read,
write: write,
}
}
func (s *stockRepo) GetQuerier(ctx context.Context) *Queries {
tx, ok := postgres.TxFromCtx(ctx)
if ok {
return New(tx)
}
return New(s.write)
}
func (s *stockRepo) StockReserve(ctx context.Context, stock *entity.Stock) error {
querier := s.GetQuerier(ctx)
rows, err := querier.StockReserve(ctx, &StockReserveParams{
Sku: int64(stock.Item.ID),
Reserved: int64(stock.Reserved),
})
if err != nil {
return fmt.Errorf("querier.StockReserve: %w", err)
}
if rows == 0 {
return model.ErrNotEnoughStocks
}
return nil
}
func (s *stockRepo) StockReserveRemove(ctx context.Context, stock *entity.Stock) error {
querier := s.GetQuerier(ctx)
rows, err := querier.StockReserveRemove(ctx, &StockReserveRemoveParams{
Sku: int64(stock.Item.ID),
Reserved: int64(stock.Reserved),
})
if err != nil {
return fmt.Errorf("querier.StockReserveRemove: %w", err)
}
if rows > 0 {
return nil
}
_, err = querier.StockGetByID(ctx, int64(stock.Item.ID))
switch {
case errors.Is(err, pgx.ErrNoRows):
return model.ErrUnknownStock
case err != nil:
return fmt.Errorf("querier.StockGetByID: %w", err)
default:
return model.ErrNotEnoughStocks
}
}
func (s *stockRepo) StockCancel(ctx context.Context, stock *entity.Stock) error {
querier := s.GetQuerier(ctx)
rows, err := querier.StockCancel(ctx, &StockCancelParams{
Sku: int64(stock.Item.ID),
Reserved: int64(stock.Reserved),
})
if err != nil {
return fmt.Errorf("querier.StockCancel: %w", err)
}
if rows > 0 {
return nil
}
_, err = querier.StockGetByID(ctx, int64(stock.Item.ID))
switch {
case errors.Is(err, pgx.ErrNoRows):
return model.ErrUnknownStock
case err != nil:
return fmt.Errorf("querier.StockGetByID: %w", err)
default:
return model.ErrNotEnoughStocks
}
}
func (s *stockRepo) StockGetByID(ctx context.Context, sku entity.Sku) (*entity.Stock, error) {
querier := s.GetQuerier(ctx)
stock, err := querier.StockGetByID(ctx, int64(sku))
switch {
case errors.Is(err, pgx.ErrNoRows):
return nil, model.ErrUnknownStock
case err != nil:
return nil, fmt.Errorf("querier.StockGetByID: %w", err)
default:
count, castErr := tools.SafeCastInt64ToUInt32(stock.TotalCount)
if castErr != nil {
return nil, castErr
}
reserved, castErr := tools.SafeCastInt64ToUInt32(stock.Reserved)
if castErr != nil {
return nil, castErr
}
return &entity.Stock{
Item: entity.OrderItem{
ID: entity.Sku(stock.Sku),
Count: count,
},
Reserved: reserved,
}, nil
}
}