add catalog listing in catalog

hotfix/hotfix-mysql-error
Ernest Litvinenko 2024-03-23 21:16:41 +03:00
parent e6c8e56e76
commit 914842291f
20 changed files with 431 additions and 164 deletions

11
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/cache v1.2.0 // indirect
github.com/gin-contrib/cors v1.5.0 // indirect
@ -21,24 +22,34 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/gomodule/redigo v1.8.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/memcachier/mc/v3 v3.0.3 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/redis/go-redis/v9 v9.5.1 // indirect
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/uptrace/bun v1.1.17 // indirect
github.com/uptrace/bun/dialect/mysqldialect v1.1.17 // indirect
github.com/uptrace/bun/extra/bundebug v1.1.17 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.20.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect

24
go.sum
View File

@ -17,6 +17,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cache v1.2.0 h1:WA+AJR4kmHDTaLLShCHo/IeWVmmGRZ3Lsr3JQ46tFlE=
@ -47,6 +49,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@ -60,6 +64,9 @@ github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgSh
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@ -72,6 +79,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -92,18 +101,33 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/uptrace/bun v1.1.17 h1:qxBaEIo0hC/8O3O6GrMDKxqyT+mw5/s0Pn/n6xjyGIk=
github.com/uptrace/bun v1.1.17/go.mod h1:hATAzivtTIRsSJR4B8AXR+uABqnQxr3myKDKEf5iQ9U=
github.com/uptrace/bun/dialect/mysqldialect v1.1.17 h1:CsaZu+C3hW6jH5XnbQWPeZbHOoeURRpX9wd9wNy9fYU=
github.com/uptrace/bun/dialect/mysqldialect v1.1.17/go.mod h1:PDT12yHB0yLidZWFoPjhXfEKvsu7tLyjY67+OSMQsVw=
github.com/uptrace/bun/extra/bundebug v1.1.17 h1:LcZ8DzyyGdXAmbUqmnCpBq7TPFegMp59FGy+uzEE21c=
github.com/uptrace/bun/extra/bundebug v1.1.17/go.mod h1:FOwNaBEGGChv3qBVh3pz3TPlUuikZ93qKjd/LJdl91o=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=

View File

@ -1,41 +1,78 @@
package endpoints
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
cmap "github.com/orcaman/concurrent-map/v2"
"relynolli-server/models"
"relynolli-server/services"
"strconv"
"relynolli-server/status"
"relynolli-server/storage"
"time"
"github.com/gin-gonic/gin"
// "relynolli-server/models"
// "relynolli-server/services"
// "strconv"
)
func (h *handlers) GetCatalogItems(c *gin.Context) {
type GetCatalogItemsRequest struct {
Limit int `form:"limit" `
Page int `form:"page"`
}
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
offset := (page - 1) * limit
if c.DefaultQuery("isFilter", "0") == "0" {
c.JSON(200, services.GetCatalogItems(limit, offset))
func (h *handlers) GetCatalogItems(c *gin.Context) {
queries := cmap.New[[]string]()
for key, val := range c.Request.URL.Query() {
queries.Set(key, val)
}
ctx := context.Background()
meta := models.Meta{
RequestStarted: time.Now().Unix(),
}
LPQuery := new(GetCatalogItemsRequest)
LPError := c.ShouldBindQuery(LPQuery)
if LPError != nil {
meta.RequestFinished = time.Now().Unix()
c.JSON(400, models.Response{
Status: status.STATUS_BAD_REQUEST,
Info: "Limit and page query params should be integer numbers",
Meta: &meta,
})
return
}
c.JSON(200, services.FilterCatalogItems(c.Request.URL.Query(), limit, offset))
if LPQuery.Page == 0 {
LPQuery.Page = 1
}
if LPQuery.Limit == 0 {
LPQuery.Limit = 10
}
s := storage.NewStorageCatalog()
count, items, err := s.GetCatalogItems(ctx, queries, LPQuery.Limit, (LPQuery.Page-1)*LPQuery.Limit)
if err != nil {
meta.RequestFinished = time.Now().Unix()
c.JSON(500,
models.Response{
Status: status.STATUS_SERVER_ERROR,
Info: fmt.Sprintf("Cannot resolve request. Details: %s", err.Error()),
Meta: &meta})
}
meta.Limit = LPQuery.Limit
meta.Page = LPQuery.Page
meta.RequestFinished = time.Now().Unix()
meta.Count = count
c.JSON(200, models.Response{
Status: status.STATUS_OK,
Data: items,
Meta: &meta,
})
}
func (h *handlers) GetCatalogItem(c *gin.Context) {
code := c.Param("code")
if code == "" {
c.JSON(400, models.Response{Status: 400, Info: "product \"Code\" should be provided"})
return
}
resp, err := services.GetCatalogItem(code)
if err != nil {
c.JSON(404, models.Response{Status: 404, Info: err.Error()})
return
}
c.JSON(200, resp)
}
func (h *handlers) Count(c *gin.Context) {
c.JSON(200, models.Response{Status: 200, Info: fmt.Sprintf("%d", services.GetCatalogItemsCount())})
}

View File

@ -7,8 +7,6 @@ type handlers struct{}
type Handlers interface {
GetFilters(c *gin.Context)
GetCatalogItems(c *gin.Context)
GetCatalogItem(c *gin.Context)
Count(c *gin.Context)
}
func GetHandlers() Handlers {

View File

@ -1,38 +1,41 @@
package endpoints
import (
"encoding/json"
"context"
"fmt"
"github.com/gin-gonic/gin"
"relynolli-server/internal"
"relynolli-server/models"
"relynolli-server/status"
"relynolli-server/storage"
"time"
)
type filterValues struct {
Id int `json:"id"`
Value string `json:"value"`
}
type filterStruct struct {
Id int `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
Values []filterValues `json:"values"`
valuesString []byte
}
func (h *handlers) GetFilters(c *gin.Context) {
stmt := "select * from api_filter;"
var responseData []filterStruct
db := internal.InitDatabase()
rows := db.Query(stmt)
for rows.Next() {
filter := filterStruct{}
// grab data from db
rows.Scan(&filter.Id, &filter.Code, &filter.Name, &filter.valuesString)
json.Unmarshal(filter.valuesString, &filter.Values)
// parse data as json
responseData = append(responseData, filter)
meta := models.Meta{
RequestStarted: time.Now().Unix(),
}
c.JSON(200, responseData)
s := storage.NewStorageCatalog()
ctx := context.Background()
count, items, err := s.GetFilters(ctx)
if err != nil {
meta.RequestFinished = time.Now().Unix()
c.JSON(500, models.Response{
Status: status.STATUS_SERVER_ERROR,
Info: fmt.Sprintf("Internal Server Error: %s", err.Error()),
Meta: &meta,
})
}
meta.RequestFinished = time.Now().Unix()
meta.Count = count
c.JSON(200, models.Response{
Status: status.STATUS_OK,
Data: &items,
Meta: &meta,
})
}

View File

@ -1,11 +1,16 @@
package catalog
import (
"github.com/gin-contrib/cache"
"os"
"relynolli-server/handlers/catalog/endpoints"
"relynolli-server/internal"
"time"
"github.com/gin-contrib/cache"
// "relynolli-server/internal"
// "time"
// "github.com/gin-contrib/cache"
"github.com/gin-gonic/gin"
)
@ -13,8 +18,15 @@ func HandleRoutes(parent *gin.RouterGroup) {
h := endpoints.GetHandlers()
cacheStore := internal.InitCacheStore()
catalog := parent.Group("/catalog")
catalog.GET("/filters", cache.CachePage(cacheStore, 15, h.GetFilters))
catalog.GET("/count", cache.CachePage(cacheStore, 15 * time.Minute, h.Count))
if os.Getenv("IS_PROD") == "1" {
// Caching for production usage
catalog.GET("", cache.CachePage(cacheStore, 15*time.Minute, h.GetCatalogItems))
catalog.GET("/:code", cache.CachePage(cacheStore, 15 * time.Minute, h.GetCatalogItem))
}
catalog.GET("", h.GetCatalogItems)
catalog.GET("/filters", h.GetFilters)
// catalog.GET("/filters", cache.CachePage(cacheStore, 15, h.GetFilters))
// catalog.GET("/count", cache.CachePage(cacheStore, 15 * time.Minute, h.Count))
// catalog.GET("/:code", cache.CachePage(cacheStore, 15 * time.Minute, h.GetCatalogItem))
}

View File

@ -1,8 +1,9 @@
package order
import (
"github.com/gin-gonic/gin"
"relynolli-server/handlers/order/endpoints"
"github.com/gin-gonic/gin"
)
func HandleRoutes(parent *gin.RouterGroup) {

View File

@ -2,16 +2,16 @@ package handlers
import (
"github.com/gin-gonic/gin"
"relynolli-server/handlers/cart"
// "relynolli-server/handlers/cart"
"relynolli-server/handlers/catalog"
"relynolli-server/handlers/order"
"relynolli-server/handlers/validate"
// "relynolli-server/handlers/order"
// "relynolli-server/handlers/validate"
)
func InitializeRouter(router *gin.Engine) {
APIV1Router := router.Group("/api/v1")
catalog.HandleRoutes(APIV1Router)
cart.HandleRoutes(APIV1Router)
order.HandleRoutes(APIV1Router)
validate.HandleRoutes(APIV1Router)
// cart.HandleRoutes(APIV1Router)
// order.HandleRoutes(APIV1Router)
// validate.HandleRoutes(APIV1Router)
}

View File

@ -3,25 +3,22 @@ package internal
import (
"database/sql"
"fmt"
"github.com/uptrace/bun/extra/bundebug"
"log"
"os"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/mysqldialect"
)
type database struct {
instance *sqlx.DB
instance *bun.DB
}
type Database interface {
GetInstance() *sqlx.DB
Close()
Query(stmt string) *sqlx.Rows
Execute(stmt string) sql.Result
FetchRows(stmt string, dest interface{})
GetInstance() *bun.DB
}
var (
@ -30,27 +27,31 @@ var (
)
func initialize() {
db, err := sqlx.Open("mysql", fmt.Sprintf(
db, err := sql.Open("mysql", fmt.Sprintf(
"%s:%s@tcp(%s)/%s",
os.Getenv("MYSQL_USER"),
os.Getenv("MYSQL_PASSWORD"),
os.Getenv("MYSQL_HOST"),
os.Getenv("MYSQL_DATABASE")))
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(3)
db.SetMaxIdleConns(3)
if err != nil {
panic(err)
}
conErr := db.Ping()
// Resolve instances of bun
ormDb := bun.NewDB(db, mysqldialect.New())
ormDb.AddQueryHook(bundebug.NewQueryHook())
// Check Connection
conErr := ormDb.Ping()
if conErr != nil {
panic(conErr)
}
log.Println("Connection to db succeded")
instance = &database{instance: db}
instance = &database{instance: ormDb}
}
func InitDatabase() Database {
@ -60,39 +61,6 @@ func InitDatabase() Database {
return instance
}
func (db *database) GetInstance() *sqlx.DB {
return db.instance
}
func (db *database) Close() {
defer log.Println("Connection to database was closed")
err := db.instance.Close()
if err != nil {
return
}
}
func (db *database) Query(stmt string) *sqlx.Rows {
rows, err := db.instance.Queryx(stmt)
if err != nil {
return nil
}
return rows
}
func (db *database) Execute(stmt string) sql.Result {
result, err := db.instance.Exec(stmt)
if err != nil {
log.Println(err)
}
return result
}
type FetchRowStruct []interface{}
func (db *database) FetchRows(stmt string, dest interface{}) {
err := db.instance.Select(dest, stmt)
if err != nil {
log.Println(err)
}
func (d *database) GetInstance() *bun.DB {
return d.instance
}

View File

@ -28,7 +28,7 @@ func main() {
rdb := internal.InitRedis()
handlers.InitializeRouter(server)
defer db.Close()
defer db.GetInstance().Close()
defer rdb.Close()
gracefullyShutDown := make(chan os.Signal, 1)

View File

@ -1,47 +1,47 @@
package models
type CatalogStruct struct {
Id int
Code string
Name string
IsActive int `json:"is_active" db:"is_active"`
Properties []byte
DetailText string `json:"detailText" db:"detailText"`
Price []byte
AvailableQuantity int `json:"availableQuantity,omitempty" db:"available_quantity"`
}
// type CatalogStruct struct {
// Id int
// Code string
// Name string
// IsActive int `json:"is_active" db:"is_active"`
// Properties []byte
// DetailText string `json:"detailText" db:"detailText"`
// Price []byte
// AvailableQuantity int `json:"availableQuantity,omitempty" db:"available_quantity"`
// }
type CatalogStructWeb struct {
Id int `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
IsActive int `json:"is_active" db:"is_active"`
Properties map[string]interface{} `json:"properties"`
DetailText string `json:"detailText" db:"detailText"`
Price map[string]interface{} `json:"price"`
AvailableQuantity int `json:"availableQuantity,omitempty" db:"available_quantity"`
}
// type CatalogStructWeb struct {
// Id int `json:"id"`
// Code string `json:"code"`
// Name string `json:"name"`
// IsActive int `json:"is_active" db:"is_active"`
// Properties map[string]interface{} `json:"properties"`
// DetailText string `json:"detailText" db:"detailText"`
// Price map[string]interface{} `json:"price"`
// AvailableQuantity int `json:"availableQuantity,omitempty" db:"available_quantity"`
// }
type CatalogWithQuantityWeb struct {
Id int `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
IsActive int `json:"is_active"`
Properties map[string]interface{} `json:"properties"`
DetailText string `json:"detailText"`
Price map[string]interface{} `json:"price"`
Quantity int `json:"quantity"`
AvailableQuantity int `json:"available_quantity" db:"available_quantity"`
}
// type CatalogWithQuantityWeb struct {
// Id int `json:"id"`
// Code string `json:"code"`
// Name string `json:"name"`
// IsActive int `json:"is_active"`
// Properties map[string]interface{} `json:"properties"`
// DetailText string `json:"detailText"`
// Price map[string]interface{} `json:"price"`
// Quantity int `json:"quantity"`
// AvailableQuantity int `json:"available_quantity" db:"available_quantity"`
// }
type CatalogWithQuantity struct {
Id int
Code string
Name string
IsActive int `json:"is_active" db:"is_active"`
Properties []byte
DetailText string `json:"detailText" db:"detailText"`
Price []byte
Quantity int `json:"quantity"`
AvailableQuantity int `json:"available_quantity" db:"available_quantity"`
}
// type CatalogWithQuantity struct {
// Id int
// Code string
// Name string
// IsActive int `json:"is_active" db:"is_active"`
// Properties []byte
// DetailText string `json:"detailText" db:"detailText"`
// Price []byte
// Quantity int `json:"quantity"`
// AvailableQuantity int `json:"available_quantity" db:"available_quantity"`
// }

52
models/catalog/db.go Normal file
View File

@ -0,0 +1,52 @@
package catalog
import "github.com/uptrace/bun"
type DBCatalog struct {
bun.BaseModel `bun:"select:api_catalog"`
Id int64 `bun:"id" json:"id"`
Code string `bun:"code" json:"code"`
Name string `bun:"name" json:"name"`
IsActive bool `bun:"is_active,type:integer" json:"isActive"`
Properties *DBCatalogProperties `bun:"properties" json:"properties"`
DetailText string `bun:"detailText" json:"detailText"`
Price *DBCatalogPrice `bun:"price" json:"price"`
AvailableQuantity int64 `bun:"available_quantity" json:"availableQuantity"`
}
type DBCatalogProperties struct {
Acea string `json:"acea,omitempty"`
Width string `json:"width,omitempty"`
Height string `json:"height,omitempty"`
Length string `json:"length,omitempty"`
Volume string `json:"volume,omitempty"`
Weight string `json:"weight,omitempty"`
Mileage string `json:"mileage,omitempty"`
BoxType string `json:"box_type,omitempty"`
Category string `json:"category,omitempty"`
OilType string `json:"oil_type,omitempty"`
Documents []string `json:"documents,omitempty"`
UseAreas string `json:"use_areas,omitempty"`
Viscosity string `json:"viscosity,omitempty"`
AcidIndex string `json:"acid_index,omitempty"`
MainImage []string `json:"main_image,omitempty"`
PourPoint string `json:"pour_point,omitempty"`
FlashPoint string `json:"flash_point,omitempty"`
Subcategory string `json:"subcategory,omitempty"`
VendorCode string `json:"vendor_code,omitempty"`
ApiStandart string `json:"api_standart,omitempty"`
Requirements string `json:"requirements,omitempty"`
ViscosityIndex string `json:"viscosity_index,omitempty"`
ViscosityKinematic string `json:"viscosity_kinematic,omitempty"`
TribologicalProperties string `json:"tribological_properties,omitempty"`
}
type DBCatalogPrice struct {
BASE float64 `json:"BASE"`
OPTMAX float64 `json:"OPTMAX,omitempty"`
OPTMIN float64 `json:"OPTMIN,omitempty"`
MOC float64 `json:"Мелко-Оптовая Цена (МОЦ),omitempty"`
KOC float64 `json:"Крупно-Оптовая Цена (КОЦ),omitempty"`
MCP float64 `json:"Минимальная Цена Продаж (МЦП),omitempty"`
RRC float64 `json:"Рекомендуемая Розничная цена (РРЦ),omitempty"`
}

13
models/catalog/domain.go Normal file
View File

@ -0,0 +1,13 @@
package catalog
type DomainCatalog struct {
// bun.BaseModel `bun:"select:api_catalog"`
Id int64
Code string
Name string
IsActive bool `bun:"is_active,type:integer"`
Properties string `bun:"properties"`
DetailText string `bun:"detailText"`
Price string `bun:"price"`
AvailableQunatity int64 `bun:"available_quantity"`
}

View File

@ -0,0 +1 @@
package catalog

16
models/filters/db.go Normal file
View File

@ -0,0 +1,16 @@
package filters
import "github.com/uptrace/bun"
type DBFilter struct {
bun.BaseModel `bun:"select:api_filter"`
Id int64 `bun:"id" json:"id"`
Code string `bun:"code" json:"code"`
Name string `bun:"name" json:"name"`
Values *[]DBFilterValues `bun:"values" json:"values"`
}
type DBFilterValues struct {
Id int64 `json:"id"`
Value string `json:"value"`
}

View File

@ -1,7 +1,19 @@
package models
import "relynolli-server/status"
type Response struct {
Status int `json:"status"`
Status status.Status `json:"status"`
Info string `json:"info,omitempty"`
Data interface{} `json:"data,omitempty"`
Meta *Meta `json:"meta"`
}
type Meta struct {
RequestStarted int64 `json:"requestStarted"`
RequestFinished int64 `json:"requestFinished"`
Page int `json:"page,omitempty"`
Limit int `json:"limit,omitempty"`
Count int `json:"count,omitempty"`
}

10
status/status.go Normal file
View File

@ -0,0 +1,10 @@
package status
type Status string
const (
STATUS_OK Status = "OK"
STATUS_NOT_FOUND Status = "not_found"
STATUS_BAD_REQUEST Status = "bad_request"
STATUS_SERVER_ERROR Status = "internal_server_error"
)

15
storage/cart.go Normal file
View File

@ -0,0 +1,15 @@
package storage
import "context"
type StorageCart interface {
}
func (s *storage) GetCartItem(ctx context.Context) {
}
func (s *storage) GetCartItems(ctx context.Context) {
}

83
storage/catalog.go Normal file
View File

@ -0,0 +1,83 @@
package storage
import (
"context"
"fmt"
cmap "github.com/orcaman/concurrent-map/v2"
"github.com/uptrace/bun"
"relynolli-server/internal"
"relynolli-server/models/catalog"
filters2 "relynolli-server/models/filters"
"slices"
)
type StorageCatalog interface {
GetCatalogItem(ctx context.Context, id *int64) (*catalog.DBCatalog, error)
GetCatalogItems(ctx context.Context, filters cmap.ConcurrentMap[string, []string], limit int, offset int) (int, *[]catalog.DBCatalog, error)
GetFilters(ctx context.Context) (int, *[]filters2.DBFilter, error)
}
func NewStorageCatalog() StorageCatalog {
if instance == nil {
instance = &storage{
db: internal.InitDatabase().GetInstance(),
}
}
return instance
}
func (s *storage) GetCatalogItem(ctx context.Context, id *int64) (*catalog.DBCatalog, error) {
db := internal.InitDatabase().GetInstance()
model := new(catalog.DBCatalog)
err := db.NewSelect().Model(model).Where("id = ?", id).Where("available_quantity > 0").Where("is_available = 1").Scan(ctx)
if err != nil {
return nil, err
}
return model, nil
}
func buildFilterGroup(ctx context.Context, q *bun.SelectQuery, filters *cmap.ConcurrentMap[string, []string]) *bun.SelectQuery {
db := internal.InitDatabase().GetInstance()
availableFilters := new([]filters2.DBFilter)
//Get filters
db.NewSelect().Model(availableFilters).Where("code in (?)", bun.In(filters.Keys())).Scan(ctx)
for _, filter := range filters.Keys() {
if !slices.ContainsFunc(*availableFilters, func(elem filters2.DBFilter) bool {
return elem.Code == filter
}) {
continue
}
q = q.WhereGroup(" AND ", func(query *bun.SelectQuery) *bun.SelectQuery {
values, _ := filters.Get(filter)
for _, val := range values {
query = q.WhereOr(fmt.Sprintf("properties->>'$.%s' = ?", filter), val)
}
return query
})
}
return q
}
func (s *storage) GetCatalogItems(ctx context.Context, filters cmap.ConcurrentMap[string, []string], limit int, offset int) (int, *[]catalog.DBCatalog, error) {
db := internal.InitDatabase().GetInstance()
model := new([]catalog.DBCatalog)
filterQuery := db.NewSelect().Model(model).Where("is_active = 1").Where("available_quantity > 0")
count, _ := buildFilterGroup(ctx, filterQuery, &filters).Limit(limit).Offset(offset).ScanAndCount(ctx)
return count, model, nil
}
func (s *storage) GetFilters(ctx context.Context) (int, *[]filters2.DBFilter, error) {
models := new([]filters2.DBFilter)
db := internal.InitDatabase().GetInstance()
count, err := db.NewSelect().Model(models).ScanAndCount(ctx)
if err != nil {
return 0, nil, err
}
return count, models, nil
}

11
storage/storage.go Normal file
View File

@ -0,0 +1,11 @@
package storage
import (
"github.com/uptrace/bun"
)
type storage struct {
db *bun.DB
}
var instance *storage = nil