chore: add qe and integrations with rabbit mq
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/skip2/go-qrcode"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type BaleTypeHandlers struct {
|
||||
Repo *storage.Repository
|
||||
}
|
||||
|
||||
func (h *BaleTypeHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
g.GET("/bale-types", h.GetBaleTypes)
|
||||
g.GET("/bale-types/qr/:id", h.GetBaleTypeQR)
|
||||
g.GET("/bale-types/:id", h.GetBaleTypeByID)
|
||||
g.POST("/bale-types", h.CreateBaleType)
|
||||
g.PUT("/bale-types/:id", h.UpdateBaleType)
|
||||
g.DELETE("/bale-types/:id", h.DeleteBaleType)
|
||||
}
|
||||
|
||||
// GetBaleTypes Получить список всех типов тюков
|
||||
// @Summary Получить все типы тюков
|
||||
// @Description Возвращает список всех типов тюков
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} storage.BaleType
|
||||
// @Router /bale-types [get]
|
||||
func (h *BaleTypeHandlers) GetBaleTypes(c *gin.Context) {
|
||||
types, err := h.Repo.GetBaleTypes(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, types)
|
||||
}
|
||||
|
||||
// GetBaleTypeByID Получить тип тюка по ID
|
||||
// @Summary Получить тип тюка по ID
|
||||
// @Description Возвращает тип тюка по ID
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID типа тюка"
|
||||
// @Success 200 {object} storage.BaleType
|
||||
// @Router /bale-types/{id} [get]
|
||||
func (h *BaleTypeHandlers) GetBaleTypeByID(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
bt, err := h.Repo.GetBaleTypeByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, bt)
|
||||
}
|
||||
|
||||
// CreateBaleType Создать новый тип тюка
|
||||
// @Summary Создать тип тюка
|
||||
// @Description Создаёт новый тип тюка
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param bale_type body storage.BaleType true "Данные типа тюка"
|
||||
// @Success 201 {object} storage.BaleType
|
||||
// @Router /bale-types [post]
|
||||
func (h *BaleTypeHandlers) CreateBaleType(c *gin.Context) {
|
||||
var input storage.BaleType
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
bt, err := h.Repo.CreateBaleType(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, bt)
|
||||
}
|
||||
|
||||
// UpdateBaleType Обновить тип тюка
|
||||
// @Summary Обновить тип тюка
|
||||
// @Description Обновляет данные типа тюка по ID
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID типа тюка"
|
||||
// @Param bale_type body storage.BaleType true "Данные типа тюка"
|
||||
// @Success 200 {object} storage.BaleType
|
||||
// @Router /bale-types/{id} [put]
|
||||
func (h *BaleTypeHandlers) UpdateBaleType(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
var input storage.BaleType
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
bt, err := h.Repo.UpdateBaleType(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, bt)
|
||||
}
|
||||
|
||||
// DeleteBaleType Удалить тип тюка
|
||||
// @Summary Удалить тип тюка
|
||||
// @Description Удаляет тип тюка по ID
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID типа тюка"
|
||||
// @Success 200 {object} map[string]bool
|
||||
// @Router /bale-types/{id} [delete]
|
||||
func (h *BaleTypeHandlers) DeleteBaleType(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
if err := h.Repo.DeleteBaleType(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"deleted": true})
|
||||
}
|
||||
|
||||
// GetBaleTypeQR Получить QR код для маркировки тюка
|
||||
// @Summary Получить QR код для маркировки тюка
|
||||
// @Description Возвращает QR код с URL для регистрации тюка этого типа
|
||||
// @Tags bale-types
|
||||
// @Accept json
|
||||
// @Produce png
|
||||
// @Param id path int true "ID типа тюка"
|
||||
// @Success 200 {file} image/png
|
||||
// @Router /bale-types/qr/{id} [get]
|
||||
func (h *BaleTypeHandlers) GetBaleTypeQR(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
|
||||
bt, err := h.Repo.GetBaleTypeByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "type not found"})
|
||||
return
|
||||
}
|
||||
|
||||
server := c.Request.URL.Host
|
||||
if server == "" {
|
||||
server = "localhost:8080"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("http://%s/api/v1/bales?type=%s", server, bt.Type)
|
||||
png, err := qrcode.Encode(url, qrcode.Medium, 256)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "image/png", png)
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/mq"
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type BaleHandlers struct {
|
||||
DB *pgxpool.Pool
|
||||
Repo *storage.Repository
|
||||
MQ *mq.RabbitMQ
|
||||
}
|
||||
|
||||
func (h *BaleHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
@@ -20,22 +23,128 @@ func (h *BaleHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
g.DELETE("/bales/:id", h.DeleteBale)
|
||||
}
|
||||
|
||||
// GetBales Получить список всех тюков
|
||||
// @Summary Получить все тюки
|
||||
// @Description Возвращает список всех тюков
|
||||
// @Tags bales
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} storage.Bale
|
||||
// @Router /bales [get]
|
||||
func (h *BaleHandlers) GetBales(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "GetBales"})
|
||||
bales, err := h.Repo.GetBales(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, bales)
|
||||
}
|
||||
|
||||
// GetBaleByID Получить тюк по ID
|
||||
// @Summary Получить тюк по ID
|
||||
// @Description Возвращает тюк по ID
|
||||
// @Tags bales
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID тюка"
|
||||
// @Success 200 {object} storage.Bale
|
||||
// @Router /bales/{id} [get]
|
||||
func (h *BaleHandlers) GetBaleByID(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "GetBaleByID"})
|
||||
id := c.Param("id")
|
||||
bale, err := h.Repo.GetBaleByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, bale)
|
||||
}
|
||||
|
||||
// CreateBale Создать новый тюк
|
||||
// @Summary Создать тюк
|
||||
// @Description Создаёт новый тюк и отправляет в очередь задач RabbitMQ
|
||||
// @Tags bales
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param bale body storage.Bale false "Данные тюка"
|
||||
// @Param type query string false "Тип тюка (для QR кодов)"
|
||||
// @Success 201 {object} storage.Bale
|
||||
// @Router /bales [post]
|
||||
func (h *BaleHandlers) CreateBale(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "CreateBale"})
|
||||
var input storage.Bale
|
||||
|
||||
if err := c.ShouldBindJSON(&input); err != nil && err.Error() != "EOF" {
|
||||
typeName := c.Query("type")
|
||||
if typeName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
bt, err := h.Repo.GetBaleTypeByType(c.Request.Context(), typeName)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "type not found"})
|
||||
return
|
||||
}
|
||||
input.TypeID = bt.ID
|
||||
} else if input.TypeID == 0 && c.Query("type") != "" {
|
||||
typeName := c.Query("type")
|
||||
bt, err := h.Repo.GetBaleTypeByType(c.Request.Context(), typeName)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "type not found"})
|
||||
return
|
||||
}
|
||||
input.TypeID = bt.ID
|
||||
}
|
||||
|
||||
bale, err := h.Repo.CreateBale(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.MQ != nil {
|
||||
data, _ := json.Marshal(bale)
|
||||
h.MQ.Publish(c.Request.Context(), data)
|
||||
}
|
||||
c.JSON(http.StatusCreated, bale)
|
||||
}
|
||||
|
||||
// UpdateBale Обновить тюк
|
||||
// @Summary Обновить тюк
|
||||
// @Description Обновляет данные тюка по ID
|
||||
// @Tags bales
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID тюка"
|
||||
// @Param bale body storage.Bale true "Данные тюка"
|
||||
// @Success 200 {object} storage.Bale
|
||||
// @Router /bales/{id} [put]
|
||||
func (h *BaleHandlers) UpdateBale(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "UpdateBale"})
|
||||
id := c.Param("id")
|
||||
var input storage.Bale
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
bale, err := h.Repo.UpdateBale(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, bale)
|
||||
}
|
||||
|
||||
// DeleteBale Удалить тюк
|
||||
// @Summary Удалить тюк
|
||||
// @Description Удаляет тюк по ID
|
||||
// @Tags bales
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID тюка"
|
||||
// @Success 200 {object} map[string]bool
|
||||
// @Router /bales/{id} [delete]
|
||||
func (h *BaleHandlers) DeleteBale(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "DeleteBale"})
|
||||
id := c.Param("id")
|
||||
if err := h.Repo.DeleteBale(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"deleted": true})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/mq"
|
||||
)
|
||||
|
||||
type QueueHandlers struct {
|
||||
MQ *mq.RabbitMQ
|
||||
}
|
||||
|
||||
func (h *QueueHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
g.GET("/queue/next", h.GetNextTask)
|
||||
}
|
||||
|
||||
// GetNextTask Получить следующую задачу из очереди
|
||||
// @Summary Получить следующую задачу из очереди
|
||||
// @Description Получает и удаляет из очереди следующую задачу (FIFO)
|
||||
// @Tags queue
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Success 404 {object} map[string]string
|
||||
// @Router /queue/next [get]
|
||||
func (h *QueueHandlers) GetNextTask(c *gin.Context) {
|
||||
if h.MQ == nil {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "rabbitmq not available"})
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.MQ.Consume()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "no messages in queue"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": string(data),
|
||||
"success": true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type mockRepo struct {
|
||||
storage.Repository
|
||||
baleTypes []storage.BaleType
|
||||
bales []storage.Bale
|
||||
}
|
||||
|
||||
func (r *mockRepo) GetBaleTypes(ctx context.Context) ([]storage.BaleType, error) {
|
||||
return r.baleTypes, nil
|
||||
}
|
||||
|
||||
func (r *mockRepo) GetBales(ctx context.Context) ([]storage.Bale, error) {
|
||||
return r.bales, nil
|
||||
}
|
||||
|
||||
func TestBaleTypeHandlers_HasRoutes(t *testing.T) {
|
||||
h := &BaleTypeHandlers{}
|
||||
if h == nil {
|
||||
t.Error("BaleTypeHandler is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaleHandlers_HasRoutes(t *testing.T) {
|
||||
h := &BaleHandlers{}
|
||||
if h == nil {
|
||||
t.Error("BaleHandler is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypesFieldMappings(t *testing.T) {
|
||||
bt := storage.BaleType{
|
||||
ID: 1,
|
||||
Type: "test",
|
||||
Weight: 10.0,
|
||||
}
|
||||
if bt.Type != "test" {
|
||||
t.Errorf("expected type 'test', got %s", bt.Type)
|
||||
}
|
||||
if bt.Weight != 10.0 {
|
||||
t.Errorf("expected weight 10.0, got %f", bt.Weight)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaleFieldMappings(t *testing.T) {
|
||||
b := storage.Bale{
|
||||
ID: 1,
|
||||
TypeID: 1,
|
||||
Type: "standard",
|
||||
}
|
||||
if b.TypeID != 1 {
|
||||
t.Errorf("expected typeId 1, got %d", b.TypeID)
|
||||
}
|
||||
if b.Type != "standard" {
|
||||
t.Errorf("expected type 'standard', got %s", b.Type)
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,30 @@ package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/mq"
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
DB *pgxpool.Pool
|
||||
Repo *storage.Repository
|
||||
MQ *mq.RabbitMQ
|
||||
}
|
||||
|
||||
func (h *Handlers) RegisterRoutes(r *gin.RouterGroup) {
|
||||
baleHandlers := &BaleHandlers{
|
||||
DB: h.DB,
|
||||
Repo: h.Repo,
|
||||
MQ: h.MQ,
|
||||
}
|
||||
baleHandlers.RegisterRoutes(r)
|
||||
|
||||
baleTypeHandlers := &BaleTypeHandlers{
|
||||
Repo: h.Repo,
|
||||
}
|
||||
baleTypeHandlers.RegisterRoutes(r)
|
||||
|
||||
queueHandlers := &QueueHandlers{
|
||||
MQ: h.MQ,
|
||||
}
|
||||
queueHandlers.RegisterRoutes(r)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
type RabbitMQ struct {
|
||||
conn *amqp.Connection
|
||||
channel *amqp.Channel
|
||||
queue amqp.Queue
|
||||
}
|
||||
|
||||
func NewRabbitMQ() (*RabbitMQ, error) {
|
||||
url := os.Getenv("RABBITMQ_URL")
|
||||
if url == "" {
|
||||
url = "amqp://guest:guest@localhost:5672/"
|
||||
}
|
||||
|
||||
conn, err := amqp.Dial(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to RabbitMQ: %w", err)
|
||||
}
|
||||
|
||||
ch, err := conn.Channel()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("failed to open channel: %w", err)
|
||||
}
|
||||
|
||||
q, err := ch.QueueDeclare("bales_tasks", true, false, false, false, nil)
|
||||
if err != nil {
|
||||
ch.Close()
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("failed to declare queue: %w", err)
|
||||
}
|
||||
|
||||
return &RabbitMQ{
|
||||
conn: conn,
|
||||
channel: ch,
|
||||
queue: q,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *RabbitMQ) Publish(ctx context.Context, body []byte) error {
|
||||
return r.channel.PublishWithContext(ctx, "", r.queue.Name, false, false, amqp.Publishing{
|
||||
ContentType: "application/json",
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *RabbitMQ) Consume() ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
msgs, err := r.channel.Consume(r.queue.Name, "", false, false, false, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
case msg, ok := <-msgs:
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("channel closed")
|
||||
}
|
||||
msg.Ack(false)
|
||||
return msg.Body, nil
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("timeout waiting for message")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RabbitMQ) Close() {
|
||||
if r.channel != nil {
|
||||
r.channel.Close()
|
||||
}
|
||||
if r.conn != nil {
|
||||
r.conn.Close()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
@@ -13,3 +15,139 @@ func NewRepository(pool *pgxpool.Pool) *Repository {
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) GetBales(ctx context.Context) ([]Bale, error) {
|
||||
rows, err := r.pool.Query(ctx, `
|
||||
SELECT b.id, b.type_id, COALESCE(bt.type, ''), b.timestamp
|
||||
FROM bales b
|
||||
LEFT JOIN bale_types bt ON b.type_id = bt.id
|
||||
ORDER BY b.id`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var bales []Bale
|
||||
for rows.Next() {
|
||||
var b Bale
|
||||
if err := rows.Scan(&b.ID, &b.TypeID, &b.Type, &b.Timestamp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bales = append(bales, b)
|
||||
}
|
||||
return bales, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetBaleByID(ctx context.Context, id string) (*Bale, error) {
|
||||
var b Bale
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
SELECT b.id, b.type_id, COALESCE(bt.type, ''), b.timestamp
|
||||
FROM bales b
|
||||
LEFT JOIN bale_types bt ON b.type_id = bt.id
|
||||
WHERE b.id = $1`, id).Scan(&b.ID, &b.TypeID, &b.Type, &b.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateBale(ctx context.Context, input Bale) (*Bale, error) {
|
||||
var b Bale
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
INSERT INTO bales (type_id) VALUES ($1)
|
||||
RETURNING id, type_id, timestamp`, input.TypeID).Scan(&b.ID, &b.TypeID, &b.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.TypeID > 0 {
|
||||
var bt BaleType
|
||||
r.pool.QueryRow(ctx, "SELECT id, type FROM bale_types WHERE id = $1", b.TypeID).Scan(&bt.ID, &bt.Type)
|
||||
b.Type = bt.Type
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateBale(ctx context.Context, id string, input Bale) (*Bale, error) {
|
||||
var b Bale
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
UPDATE bales SET type_id = $1 WHERE id = $2
|
||||
RETURNING id, type_id, timestamp`, input.TypeID, id).Scan(&b.ID, &b.TypeID, &b.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.TypeID > 0 {
|
||||
var bt BaleType
|
||||
r.pool.QueryRow(ctx, "SELECT id, type FROM bale_types WHERE id = $1", b.TypeID).Scan(&bt.ID, &bt.Type)
|
||||
b.Type = bt.Type
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
func (r *Repository) DeleteBale(ctx context.Context, id string) error {
|
||||
_, err := r.pool.Exec(ctx, "DELETE FROM bales WHERE id = $1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Repository) GetBaleTypes(ctx context.Context) ([]BaleType, error) {
|
||||
rows, err := r.pool.Query(ctx, "SELECT id, type, weight, height, width, length FROM bale_types ORDER BY id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var types []BaleType
|
||||
for rows.Next() {
|
||||
var bt BaleType
|
||||
if err := rows.Scan(&bt.ID, &bt.Type, &bt.Weight, &bt.Height, &bt.Width, &bt.Length); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
types = append(types, bt)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetBaleTypeByID(ctx context.Context, id int) (*BaleType, error) {
|
||||
var bt BaleType
|
||||
err := r.pool.QueryRow(ctx, "SELECT id, type, weight, height, width, length FROM bale_types WHERE id = $1", id).Scan(&bt.ID, &bt.Type, &bt.Weight, &bt.Height, &bt.Width, &bt.Length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bt, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetBaleTypeByType(ctx context.Context, typeName string) (*BaleType, error) {
|
||||
var bt BaleType
|
||||
err := r.pool.QueryRow(ctx, "SELECT id, type, weight, height, width, length FROM bale_types WHERE type = $1", typeName).Scan(&bt.ID, &bt.Type, &bt.Weight, &bt.Height, &bt.Width, &bt.Length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bt, nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateBaleType(ctx context.Context, input BaleType) (*BaleType, error) {
|
||||
var bt BaleType
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
INSERT INTO bale_types (type, weight, height, width, length)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, type, weight, height, width, length`, input.Type, input.Weight, input.Height, input.Width, input.Length).Scan(&bt.ID, &bt.Type, &bt.Weight, &bt.Height, &bt.Width, &bt.Length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bt, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateBaleType(ctx context.Context, id int, input BaleType) (*BaleType, error) {
|
||||
var bt BaleType
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
UPDATE bale_types SET type = $1, weight = $2, height = $3, width = $4, length = $5 WHERE id = $6
|
||||
RETURNING id, type, weight, height, width, length`, input.Type, input.Weight, input.Height, input.Width, input.Length, id).Scan(&bt.ID, &bt.Type, &bt.Weight, &bt.Height, &bt.Width, &bt.Length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bt, nil
|
||||
}
|
||||
|
||||
func (r *Repository) DeleteBaleType(ctx context.Context, id int) error {
|
||||
_, err := r.pool.Exec(ctx, "DELETE FROM bale_types WHERE id = $1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package storage
|
||||
|
||||
import "time"
|
||||
|
||||
// BaleType Тип тюка
|
||||
// @Description Тип тюка - характеристики тюка
|
||||
type BaleType struct {
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
@@ -9,10 +13,13 @@ type BaleType struct {
|
||||
Length float64 `json:"length"`
|
||||
}
|
||||
|
||||
// Bale Тюк
|
||||
// @Description Тюк - единица готовой продукции
|
||||
type Bale struct {
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ID int `json:"id"`
|
||||
TypeID int `json:"typeId"`
|
||||
Type string `json:"type"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
|
||||
Reference in New Issue
Block a user