diff --git a/external/bitrix/api.go b/external/bitrix/api.go index 1145cea..82ebc1c 100644 --- a/external/bitrix/api.go +++ b/external/bitrix/api.go @@ -7,6 +7,8 @@ import ( "github.com/google/uuid" "github.com/mitchellh/mapstructure" "github.com/sirupsen/logrus" + "io" + "log" "net/http" "os" "strings" @@ -165,7 +167,8 @@ type BasketResource struct { } type bitrix struct { - EP string + EP string + EPCustom string } type Bitrix interface { @@ -175,8 +178,9 @@ type Bitrix interface { CancelOrder(orderId int) error GetOrderInfo(orderId int) (*OrderResource, error) CreatePayment(orderId int, sum float64) error - AddProductToOrder(orderId int, productId int, price float64, quantity int) error + AddProductToOrder(orderId int, productId int, price float64, quantity int, productName string) error UpdateContact(contactId int, email string, name string, phone string) error + GetTotalForProduct(fuserId int, coupon *string) (*GetTotalOrderResponse, error) } type createAnonymousUserRequest struct { @@ -231,6 +235,11 @@ type createOrderResponse struct { } `json:"result"` } +type GetTotalOrderResponse struct { + Price float64 `json:"price"` + BasePrice float64 `json:"basePrice"` +} + func (b bitrix) CreateOrder(userId int) (int, error) { req := createOrderRequestWrapper{createOrderRequest{ Lid: "s2", @@ -257,7 +266,10 @@ func (b bitrix) ApprovePayment(paymentId int, paySystemId int) error { query, _ := json.Marshal(req) - _, err := http.Post(b.EP+"/sale.payment.update", "application/json", bytes.NewBuffer(query)) + resp, err := http.Post(b.EP+"/sale.payment.update", "application/json", bytes.NewBuffer(query)) + + str, _ := io.ReadAll(resp.Body) + log.Println(str) return err } @@ -317,20 +329,26 @@ func (b bitrix) CreatePayment(orderId int, sum float64) error { return err } -func (b bitrix) AddProductToOrder(orderId int, productId int, price float64, quantity int) error { +func (b bitrix) AddProductToOrder(orderId int, productId int, price float64, quantity int, productName string) error { req := map[string]interface{}{ "fields": map[string]interface{}{ - "orderId": orderId, - "productId": productId, - "quantity": quantity, - "currency": "RUB", - "price": price, + "name": productName, + "orderId": orderId, + "module": "catalog", + "productId": productId, + "quantity": quantity, + "currency": "RUB", + "vatIncluded": "Y", + "vatRate": 0.2, + "basePrice": price, + "productXmlId": fmt.Sprintf("%d", productId), + "detailPageUrl": fmt.Sprintf("\\/CRM_PRODUCT_CATALOG\\/detail.php?ID=%d", productId), }, } query, _ := json.Marshal(req) - _, err := http.Post(b.EP+"/sale.basketitem.addCatalogProduct", "application/json", bytes.NewBuffer(query)) + _, err := http.Post(b.EP+"/sale.basketitem.add", "application/json", bytes.NewBuffer(query)) return err } @@ -361,8 +379,30 @@ func (b bitrix) UpdateContact(contactId int, email string, name string, phone st return err } +func (b bitrix) GetTotalForProduct(fuserId int, coupon *string) (*GetTotalOrderResponse, error) { + var result *http.Response + var err error + + if coupon != nil { + result, err = http.Get(b.EPCustom + fmt.Sprintf("/order/total?fuserId=%d&coupon=%s", fuserId, *coupon)) + } else { + result, err = http.Get(b.EPCustom + fmt.Sprintf("/order/total?fuserId=%d", fuserId)) + } + if err != nil { + return nil, err + } + defer result.Body.Close() + + returnedValue := new(GetTotalOrderResponse) + json.NewDecoder(result.Body).Decode(returnedValue) + + return returnedValue, nil + +} + func Initialize() Bitrix { return &bitrix{ - EP: os.Getenv("BITRIX_API_EP"), + EP: os.Getenv("BITRIX_API_EP"), + EPCustom: os.Getenv("BTIRIX_API_CUSTOM"), } } diff --git a/external/kassa/kassa.go b/external/kassa/kassa.go index b4de977..7c41596 100644 --- a/external/kassa/kassa.go +++ b/external/kassa/kassa.go @@ -120,3 +120,21 @@ func CreatePayment(orderId int, sum float64, fullName string, email string, phon return result, nil } + +func CheckPayment(paymentId uuid.UUID) (*KassaResult, error) { + uid, err := uuid.NewUUID() + client := new(http.Client) + request, _ := http.NewRequest(http.MethodGet, BASE_URL+fmt.Sprintf("/%s", paymentId.String()), nil) + request.Header.Set("Authorization", "Basic "+basicAuth(os.Getenv("YOOKASSA_ACCOUNT_ID"), + os.Getenv("YOOKASSA_ACCOUNT_SECRET"))) + request.Header.Set("Idempotence-Key", uid.String()) + request.Header.Set("Content-Type", "application/json") + response, err := client.Do(request) + + result := new(KassaResult) + json.NewDecoder(response.Body).Decode(&result) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/external/yaGeo/init.go b/external/yaGeo/init.go new file mode 100644 index 0000000..73a11e3 --- /dev/null +++ b/external/yaGeo/init.go @@ -0,0 +1,124 @@ +package yaGeo + +import ( + "encoding/json" + "net/http" + "os" +) + +type GeoObject struct { + MetaDataProperty struct { + GeocoderMetaData struct { + Precision string `json:"precision"` + Text string `json:"text"` + Kind string `json:"kind"` + Address struct { + CountryCode string `json:"country_code"` + Formatted string `json:"formatted"` + PostalCode string `json:"postal_code,omitempty"` + Components []struct { + Kind string `json:"kind"` + Name string `json:"name"` + } `json:"Components"` + } `json:"Address"` + AddressDetails struct { + Country struct { + AddressLine string `json:"AddressLine"` + CountryNameCode string `json:"CountryNameCode"` + CountryName string `json:"CountryName"` + AdministrativeArea struct { + AdministrativeAreaName string `json:"AdministrativeAreaName"` + SubAdministrativeArea struct { + SubAdministrativeAreaName string `json:"SubAdministrativeAreaName"` + Locality struct { + LocalityName string `json:"LocalityName"` + Thoroughfare struct { + ThoroughfareName string `json:"ThoroughfareName"` + Premise struct { + PremiseNumber string `json:"PremiseNumber"` + PostalCode struct { + PostalCodeNumber string `json:"PostalCodeNumber"` + } `json:"PostalCode,omitempty"` + } `json:"Premise"` + } `json:"Thoroughfare,omitempty"` + DependentLocality struct { + DependentLocalityName string `json:"DependentLocalityName"` + Thoroughfare struct { + ThoroughfareName string `json:"ThoroughfareName"` + Premise struct { + PremiseNumber string `json:"PremiseNumber"` + } `json:"Premise"` + } `json:"Thoroughfare"` + } `json:"DependentLocality,omitempty"` + } `json:"Locality"` + } `json:"SubAdministrativeArea"` + } `json:"AdministrativeArea"` + } `json:"Country"` + } `json:"AddressDetails"` + } `json:"GeocoderMetaData"` + } `json:"metaDataProperty"` + Name string `json:"name"` + Description string `json:"description"` + BoundedBy struct { + Envelope struct { + LowerCorner string `json:"lowerCorner"` + UpperCorner string `json:"upperCorner"` + } `json:"Envelope"` + } `json:"boundedBy"` + Uri string `json:"uri"` + Point struct { + Pos string `json:"pos"` + } `json:"Point"` +} + +type geoResponseWrapper struct { + GeoResponse `json:"response"` +} + +type geoObjectCollection struct { + FeatureMember []struct { + GeoObject `json:"GeoObject"` + } `json:"featureMember"` +} +type GeoResponse struct { + geoObjectCollection `json:"GeoObjectCollection"` +} + +type yaGeo struct { + EP string + apiKey string +} + +type YaGeo interface { + GeoCode(q string) (*[]GeoObject, error) +} + +func Init() YaGeo { + return &yaGeo{ + EP: "https://geocode-maps.yandex.ru/1.x/", + apiKey: os.Getenv("YANDEX_GEOCODER_API_KEY"), + } +} + +func (y *yaGeo) GeoCode(q string) (*[]GeoObject, error) { + data := new(geoResponseWrapper) + req, _ := http.NewRequest("GET", y.EP, nil) + params := req.URL.Query() + params.Add("apikey", y.apiKey) + params.Add("geocode", q) + params.Add("lang", "ru_RU") + params.Add("format", "json") + req.URL.RawQuery = params.Encode() + + resp, err := http.Get(req.URL.String()) + if err != nil { + return nil, err + } + err = json.NewDecoder(resp.Body).Decode(data) + items := []GeoObject{} + + for _, d := range data.GeoResponse.geoObjectCollection.FeatureMember { + items = append(items, d.GeoObject) + } + return &items, err +} diff --git a/go.mod b/go.mod index ddb9bcb..05dd588 100644 --- a/go.mod +++ b/go.mod @@ -2,49 +2,62 @@ module relynolli-server go 1.21 +require ( + github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345 + github.com/geotrace/geo v0.0.0-20160115125640-a9248f7f2ad1 + github.com/gin-contrib/cache v1.2.0 + github.com/gin-contrib/cors v1.5.0 + github.com/gin-gonic/gin v1.9.1 + github.com/go-playground/validator/v10 v10.19.0 + github.com/go-sql-driver/mysql v1.7.1 + github.com/google/go-querystring v1.1.0 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-multierror v1.0.0 + github.com/joho/godotenv v1.5.1 + github.com/mitchellh/mapstructure v1.5.0 + github.com/orcaman/concurrent-map/v2 v2.0.1 + github.com/pkg/errors v0.9.1 + github.com/redis/go-redis/v9 v9.5.1 + github.com/rs/zerolog v1.31.0 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.8.4 + github.com/uptrace/bun v1.1.17 + github.com/uptrace/bun/dialect/mysqldialect v1.1.17 + github.com/uptrace/bun/extra/bundebug v1.1.17 +) + require ( github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect github.com/bytedance/sonic v1.11.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/davecgh/go-spew v1.1.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 github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.19.0 // indirect - github.com/go-sql-driver/mysql v1.7.1 // indirect 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/hashicorp/errwrap v1.0.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/kr/text v0.2.0 // 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/pmezard/go-difflib v1.0.0 // 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 diff --git a/go.sum b/go.sum index d7e4ca9..85df1fd 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.11.1 h1:JC0+6c9FoWYYxakaoa+c5QTtJeiSZNeByOBhXtAFSn4= @@ -13,14 +17,21 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345 h1:AZLrCR38RDhsyCQakz1UxCx72As18Ai5mObrKvT8DK8= +github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345/go.mod h1:pw+gaKQ52Cl/SrERU62yQAiWauPpLgKpuR1hkxwL4tM= 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/geotrace/geo v0.0.0-20160115125640-a9248f7f2ad1 h1:t/FumljonSghkl+LUhgKJEhIWC3Zwu9JY7rLrU9YYuU= +github.com/geotrace/geo v0.0.0-20160115125640-a9248f7f2ad1/go.mod h1:5gbC4+PtjSPzYBiq6ANs+3D4SxiPenPozK8jRUORapU= github.com/gin-contrib/cache v1.2.0 h1:WA+AJR4kmHDTaLLShCHo/IeWVmmGRZ3Lsr3JQ46tFlE= github.com/gin-contrib/cache v1.2.0/go.mod h1:2KkFL8PSnPF3Tt5E2Jpc3HWuBAUKqGZnClCFMm0tXQI= github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= @@ -29,30 +40,35 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= -github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 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/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -61,15 +77,18 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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= github.com/memcachier/mc/v3 v3.0.3 h1:qii+lDiPKi36O4Xg+HVKwHu6Oq+Gt17b+uEiA0Drwv4= github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -83,13 +102,18 @@ github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/ 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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 h1:pyecQtsPmlkCsMkYhT5iZ+sUXuwee+OvfuJjinEA3ko= github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62/go.mod h1:65XQgovT59RWatovFwnwocoUxiI/eENTnOY5GK3STuY= -github.com/rvinnie/yookassa-sdk-go v0.0.0-20230904104101-ff7e5be5530c h1:m6dxe045lJQ1tkJeCBwseulCwppUDcdZk+RIxzBjQXQ= -github.com/rvinnie/yookassa-sdk-go v0.0.0-20230904104101-ff7e5be5530c/go.mod h1:flatybkcu+7YLaB7mMnj9JTNKeim4jZ+ZrXNFjVA0pA= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -100,6 +124,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 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= @@ -117,6 +142,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU 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= +github.com/vseinstrumentiru/cdek v0.0.7 h1:73O/Zp0JH/MxPWLHXKoDfrlUAQ9WHYqSPcJlefSMFuI= +github.com/vseinstrumentiru/cdek v0.0.7/go.mod h1:9oNSNbQX0Am56kJcRDpouqlZ77ZJI9Wl4g8HB38ln3Y= 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= @@ -130,14 +157,18 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handlers/address/endpoints/ep.go b/handlers/address/endpoints/ep.go new file mode 100644 index 0000000..24b5216 --- /dev/null +++ b/handlers/address/endpoints/ep.go @@ -0,0 +1,58 @@ +package endpoints + +import ( + "fmt" + "github.com/gin-gonic/gin" + "relynolli-server/external/yaGeo" + "relynolli-server/models" + "relynolli-server/status" + "time" +) + +type handlers struct{} + +type Handlers interface { + SearchAddress(c *gin.Context) +} + +func GetHandlers() Handlers { + return &handlers{} +} + +type searchAddressParams struct { + SearchString string `form:"q"` +} + +func (h *handlers) SearchAddress(c *gin.Context) { + query := new(searchAddressParams) + err := c.ShouldBindQuery(query) + meta := models.Meta{ + RequestStarted: time.Now().Unix(), + } + response := models.Response{ + Status: status.STATUS_OK, + Meta: &meta, + } + if err != nil { + response.Info = fmt.Sprintf("Error: %s", err.Error()) + response.Status = status.STATUS_BAD_REQUEST + meta.RequestFinished = time.Now().Unix() + c.JSON(400, response) + return + } + + geo := yaGeo.Init() + results, err := geo.GeoCode(query.SearchString) + + if err != nil { + response.Info = fmt.Sprintf("Error: %s", err.Error()) + response.Status = status.STATUS_SERVER_ERROR + meta.RequestFinished = time.Now().Unix() + c.JSON(500, response) + return + } + + response.Data = results + meta.RequestFinished = time.Now().Unix() + c.JSON(200, response) +} diff --git a/handlers/address/routes.go b/handlers/address/routes.go new file mode 100644 index 0000000..fea3a5d --- /dev/null +++ b/handlers/address/routes.go @@ -0,0 +1,21 @@ +package address + +import ( + "github.com/gin-contrib/cache" + "github.com/gin-gonic/gin" + "os" + "relynolli-server/handlers/address/endpoints" + "relynolli-server/internal" + "time" +) + +func HandleRoutes(parent *gin.RouterGroup) { + h := endpoints.GetHandlers() + addr := parent.Group("/address") + if os.Getenv("IS_PROD") == "1" { + store := internal.InitCacheStore() + addr.GET("/search", cache.CachePage(store, 15*time.Minute, h.SearchAddress)) + } else { + addr.GET("/search", h.SearchAddress) + } +} diff --git a/handlers/article/endpoints/article.go b/handlers/article/endpoints/article.go new file mode 100644 index 0000000..3f6e6dc --- /dev/null +++ b/handlers/article/endpoints/article.go @@ -0,0 +1,113 @@ +package endpoints + +import ( + "context" + "fmt" + "relynolli-server/models" + "relynolli-server/status" + "relynolli-server/storage" + "time" + + "github.com/gin-gonic/gin" +) + +type handlers struct{} + +type Handlers interface { + GetNews(c *gin.Context) + RetrieveNews(c *gin.Context) +} + +func GetHandlers() Handlers { + return &handlers{} +} + +type ListNewsRequest struct { + Limit int `form:"limit" ` + Page int `form:"page"` +} + +func (h *handlers) GetNews(c *gin.Context) { + ctx := context.Background() + meta := models.Meta{ + RequestStarted: time.Now().Unix(), + } + + query := ListNewsRequest{ + Limit: 10, + Page: 1, + } + + err := c.ShouldBindQuery(&query) + if err != nil { + meta.RequestFinished = time.Now().Unix() + c.JSON(400, models.Response{ + Status: status.STATUS_BAD_REQUEST, + Info: fmt.Sprintf("Error: %s", err.Error()), + Meta: &meta, + }) + return + } + + s := storage.NewStorageArticle() + count, resp, err := s.GetArticles(ctx, int64(query.Limit), int64(query.Limit*(query.Page-1))) + if err != nil { + meta.RequestFinished = time.Now().Unix() + c.JSON(500, models.Response{ + Status: status.STATUS_SERVER_ERROR, + Info: fmt.Sprintf("Error: %s", err.Error()), + Meta: &meta, + }) + return + } + + meta.Count = count + meta.Limit = query.Limit + meta.Page = query.Page + c.JSON(200, models.Response{ + Status: status.STATUS_OK, + Data: resp, + Meta: &meta, + }) + +} + +type retireveNewsReq struct { + Code string `uri:"code" binding:"required"` +} + +func (h *handlers) RetrieveNews(c *gin.Context) { + ctx := context.Background() + meta := models.Meta{ + RequestStarted: time.Now().Unix(), + } + query := new(retireveNewsReq) + + err := c.ShouldBindUri(query) + if err != nil { + meta.RequestFinished = time.Now().Unix() + c.JSON(400, models.Response{ + Status: status.STATUS_BAD_REQUEST, + Info: fmt.Sprintf("Error: %s", err.Error()), + Meta: &meta, + }) + return + } + + s := storage.NewStorageArticle() + resp, _ := s.RetrieveArticle(ctx, query.Code) + meta.RequestFinished = time.Now().Unix() + + statusResult := status.STATUS_OK + responseCode := 200 + if resp == nil { + statusResult = status.STATUS_NOT_FOUND + responseCode = 404 + } + + c.JSON(responseCode, models.Response{ + Status: statusResult, + Data: resp, + Meta: &meta, + }) +} diff --git a/handlers/article/endpoints/ep.go b/handlers/article/endpoints/ep.go deleted file mode 100644 index e69de29..0000000 diff --git a/handlers/article/routes.go b/handlers/article/routes.go index e69de29..57df3b6 100644 --- a/handlers/article/routes.go +++ b/handlers/article/routes.go @@ -0,0 +1,26 @@ +package article + +import ( + "os" + "relynolli-server/handlers/article/endpoints" + "relynolli-server/internal" + "time" + + "github.com/gin-contrib/cache" + "github.com/gin-gonic/gin" +) + +func HandleRoutes(parent *gin.RouterGroup) { + h := endpoints.GetHandlers() + cacheStore := internal.InitCacheStore() + catalog := parent.Group("/articles") + if os.Getenv("IS_PROD") == "1" { + // Caching for production usage + catalog.GET("", cache.CachePage(cacheStore, 15*time.Minute, h.GetNews)) + catalog.GET("/:code", cache.CachePage(cacheStore, 15*time.Minute, h.RetrieveNews)) + } else { + catalog.GET("", h.GetNews) + catalog.GET("/:code", h.RetrieveNews) + } + +} diff --git a/handlers/catalog/routes.go b/handlers/catalog/routes.go index aa53102..b051292 100644 --- a/handlers/catalog/routes.go +++ b/handlers/catalog/routes.go @@ -24,12 +24,12 @@ func HandleRoutes(parent *gin.RouterGroup) { catalog.GET("", cache.CachePage(cacheStore, 15*time.Minute, h.GetCatalogItems)) catalog.GET("/filters", cache.CachePage(cacheStore, 15*time.Minute, h.GetFilters)) catalog.GET("/:code", cache.CachePage(cacheStore, 15*time.Minute, h.GetCatalogItem)) + } else { + catalog.GET("", h.GetCatalogItems) + catalog.GET("/:code", h.GetCatalogItem) + catalog.GET("/filters", h.GetFilters) } - catalog.GET("", h.GetCatalogItems) - catalog.GET("/:code", h.GetCatalogItem) - 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)) diff --git a/handlers/cdek/endpoints/ep.go b/handlers/cdek/endpoints/ep.go new file mode 100644 index 0000000..4241396 --- /dev/null +++ b/handlers/cdek/endpoints/ep.go @@ -0,0 +1,112 @@ +package endpoints + +import ( + "context" + "encoding/json" + "fmt" + "github.com/geotrace/geo" + "github.com/gin-gonic/gin" + "os" + cdek "relynolli-server/CDEK/v2" + "relynolli-server/internal" + "relynolli-server/models" + "relynolli-server/status" + "time" +) + +type handlers struct{} + +type Handlers interface { + GetDeliveryPoints(c *gin.Context) +} + +func GetHandlers() Handlers { + return &handlers{} +} + +type CoordRequest struct { + Lat float64 `form:"lat"` + Lon float64 `form:"lon"` +} + +func (h *handlers) GetDeliveryPoints(c *gin.Context) { + + query := new(CoordRequest) + ctx := context.Background() + + err := c.ShouldBindQuery(&query) + meta := models.Meta{ + RequestStarted: time.Now().Unix(), + RequestFinished: 0, + } + resp := models.Response{ + Status: status.STATUS_OK, + Info: "", + Data: nil, + Meta: &meta, + } + + if err != nil { + meta.RequestFinished = time.Now().Unix() + resp.Info = err.Error() + c.JSON(400, resp) + return + } + + client := cdek.NewClient(&cdek.Options{ + Endpoint: cdek.EndpointProd, + Credentials: &cdek.Credentials{ClientID: os.Getenv("CDEK_ACCOUNT_ID"), + ClientSecret: os.Getenv("CDEK_API_KEY")}, + }) + + if err != nil { + meta.RequestFinished = time.Now().Unix() + resp.Info = err.Error() + c.JSON(400, resp) + return + } + + rdb := internal.InitRedis() + + keys, _ := rdb.Keys(ctx, "CDEK_DP:*").Result() + + preflightResult := []cdek.DeliveryPoint{} + + if len(keys) == 0 { + r1, _ := client.DeliveryPoints(ctx, &cdek.DeliveryPointsRequest{}) + pipe := rdb.Pipeline() + for _, d := range *r1 { + str, _ := json.Marshal(d) + pipe.Set(ctx, fmt.Sprintf("CDEK_DP:%s", d.Code), str, -1).Err() + preflightResult = append(preflightResult, d) + } + pipe.Exec(ctx) + } else { + for _, key := range keys { + item := new(cdek.DeliveryPoint) + data, _ := rdb.Get(ctx, key).Result() + json.Unmarshal([]byte(data), item) + preflightResult = append(preflightResult, *item) + } + } + + pointOrigin := geo.Point{ + query.Lon, + query.Lat, + } + resultedArray := []cdek.DeliveryPoint{} + + for _, d := range preflightResult { + p1 := geo.Point{ + d.Location.Longitude, + d.Location.Latitude, + } + + if pointOrigin.Distance(p1) < 10000 { + resultedArray = append(resultedArray, d) + } + } + meta.RequestFinished = time.Now().Unix() + resp.Data = resultedArray + c.JSON(200, resp) +} diff --git a/handlers/cdek/routes.go b/handlers/cdek/routes.go new file mode 100644 index 0000000..7ae1b5f --- /dev/null +++ b/handlers/cdek/routes.go @@ -0,0 +1,18 @@ +package cdek + +import ( + "github.com/gin-gonic/gin" + "os" + "relynolli-server/handlers/cdek/endpoints" +) + +func HandleRoutes(parent *gin.RouterGroup) { + h := endpoints.GetHandlers() + cdek := parent.Group("/cdek") + if os.Getenv("IS_PROD") == "1" { + // Caching for production usage + cdek.GET("/points", h.GetDeliveryPoints) + } else { + cdek.GET("/points", h.GetDeliveryPoints) + } +} diff --git a/handlers/news/routes.go b/handlers/news/routes.go index a9b2092..b044016 100644 --- a/handlers/news/routes.go +++ b/handlers/news/routes.go @@ -18,9 +18,10 @@ func HandleRoutes(parent *gin.RouterGroup) { // Caching for production usage catalog.GET("", cache.CachePage(cacheStore, 15*time.Minute, h.GetNews)) catalog.GET("/:code", cache.CachePage(cacheStore, 15*time.Minute, h.RetrieveNews)) + } else { + + catalog.GET("", h.GetNews) + catalog.GET("/:code", h.RetrieveNews) } - catalog.GET("", h.GetNews) - catalog.GET("/:code", h.RetrieveNews) - } diff --git a/handlers/order/endpoints/ep.go b/handlers/order/endpoints/ep.go index 987f8d9..bc1c46d 100644 --- a/handlers/order/endpoints/ep.go +++ b/handlers/order/endpoints/ep.go @@ -18,7 +18,8 @@ import ( type handlers struct{} type getTotalRequest struct { - FuserId int `json:"fuserId"` + FuserId int `json:"fuserId"` + Coupon *string `json:"coupon,omitempty"` } type getTotalResponse struct { @@ -55,8 +56,16 @@ func (h handlers) GetTotal(c *gin.Context) { return } - data, err := s.GetTotal(ctx, int64(req.FuserId)) + data, err := s.GetTotal(ctx, int64(req.FuserId), req.Coupon) + + if err != nil { + resp.Status = status.STATUS_SERVER_ERROR + resp.Info = err.Error() + meta.RequestFinished = time.Now().Unix() + c.JSON(500, resp) + } resp.Data = data + meta.RequestFinished = time.Now().Unix() c.JSON(200, resp) } diff --git a/handlers/routers.go b/handlers/routers.go index 2520747..31fc9f4 100644 --- a/handlers/routers.go +++ b/handlers/routers.go @@ -1,7 +1,10 @@ package handlers import ( + "relynolli-server/handlers/address" + "relynolli-server/handlers/article" "relynolli-server/handlers/cart" + "relynolli-server/handlers/cdek" "relynolli-server/handlers/news" "relynolli-server/handlers/order" @@ -19,4 +22,7 @@ func InitializeRouter(router *gin.Engine) { cart.HandleRoutes(APIV1Router) news.HandleRoutes(APIV1Router) order.HandleRoutes(APIV1Router) + article.HandleRoutes(APIV1Router) + address.HandleRoutes(APIV1Router) + cdek.HandleRoutes(APIV1Router) } diff --git a/handlers/validate/endpoints/validate.go b/handlers/validate/endpoints/validate.go index 8b45f10..139f98a 100644 --- a/handlers/validate/endpoints/validate.go +++ b/handlers/validate/endpoints/validate.go @@ -1,9 +1,7 @@ package endpoints import ( - "fmt" "net/http" - "relynolli-server/models" "relynolli-server/services" "github.com/gin-gonic/gin" @@ -24,8 +22,7 @@ func (_ handlers) Validate(c *gin.Context) { req := ValidateReq{} err := c.ShouldBindJSON(&req) if err != nil { - c.JSON(http.StatusBadRequest, models.Response{Status: http.StatusBadRequest, - Info: fmt.Sprintf("Error: %s", err.Error())}) + c.JSON(http.StatusBadRequest) } services.YookassaValidate(req.Object.Id, req.Object.Status) diff --git a/main.go b/main.go index c4151e0..ee44487 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "os/signal" "relynolli-server/handlers" "relynolli-server/internal" + "relynolli-server/services" "syscall" "github.com/gin-contrib/cors" @@ -35,6 +36,7 @@ func main() { signal.Notify(gracefullyShutDown, syscall.SIGINT, syscall.SIGTERM) go server.Run("0.0.0.0:8000") + go services.PaymentValidation() <-gracefullyShutDown diff --git a/models/discount/db.go b/models/discount/db.go new file mode 100644 index 0000000..1033eac --- /dev/null +++ b/models/discount/db.go @@ -0,0 +1,41 @@ +package discount + +import "github.com/uptrace/bun" + +type DBDiscount struct { + bun.BaseModel `bun:"table:b_sale_discount"` + ID int64 + Name string + Actions string +} + +type DomainDiscounts struct { + ID int64 + Name string + Actions *DomainActions +} + +type DomainActions struct { + CLASSID string `json:"CLASS_ID"` + DATA struct { + All string `json:"All"` + } `json:"DATA"` + CHILDREN []struct { + CLASSID string `json:"CLASS_ID"` + DATA struct { + Type string `json:"Type"` + Value int `json:"Value"` + Unit string `json:"Unit"` + Max int `json:"Max"` + All string `json:"All"` + True string `json:"True"` + } `json:"DATA"` + CHILDREN map[string]struct { + CLASSID string `json:"CLASS_ID"` + DATA struct { + Logic string `json:"logic"` + Value string `json:"value"` + } `json:"DATA"` + } `json:"CHILDREN"` + } `json:"CHILDREN"` +} diff --git a/models/news/db.go b/models/news/db.go index 644e54d..585e6d0 100644 --- a/models/news/db.go +++ b/models/news/db.go @@ -7,13 +7,25 @@ import ( ) type DBNews struct { - bun.BaseModel `bun:"select:api_news"` - ID int64 `bun:"id" json:"id"` - IsActive bool `bun:"is_active" json:"isActive"` - Sort int64 `bun:"sort" json:"sort"` - Name string `bun:"name" json:"name"` - Content string `bun:"content" json:"content"` - Code string `bun:"code" json:"code"` - Picture string `bun:"picture" json:"picture"` - Date time.Time `bun:"date" json:"date"` + bun.BaseModel `bun:"select:api_news"` + ID int64 `bun:"id" json:"id"` + IsActive bool `bun:"is_active" json:"isActive"` + Sort int64 `bun:"sort" json:"sort"` + Name string `bun:"name" json:"name"` + Content string `bun:"content" json:"content"` + Code string `bun:"code" json:"code"` + Picture string `bun:"picture" json:"picture"` + Date time.Time `bun:"date" json:"date"` +} + +type DBArticle struct { + bun.BaseModel `bun:"select:api_article"` + ID int64 `bun:"id" json:"id"` + IsActive bool `bun:"is_active" json:"isActive"` + Sort int64 `bun:"sort" json:"sort"` + Name string `bun:"name" json:"name"` + Content string `bun:"content" json:"content"` + Code string `bun:"code" json:"code"` + Picture string `bun:"picture" json:"picture"` + Date time.Time `bun:"date" json:"date"` } diff --git a/models/order/db.go b/models/order/db.go new file mode 100644 index 0000000..1e6edf7 --- /dev/null +++ b/models/order/db.go @@ -0,0 +1,21 @@ +package order + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type DBPayment struct { + bun.BaseModel `bun:"table:api_youkassa_payment"` + ID uuid.UUID `bun:"payment_id,type:char(36),pk" json:"id"` + OrderId int64 `bun:"order_id" json:"orderId"` + Status string `bun:"status" json:"status"` + Link string `bun:"link" json:"link"` + BitrixPayment *DBOrderPayment `bun:"-"` +} + +type DBOrderPayment struct { + bun.BaseModel `bun:"table:b_sale_order_payment"` + ID int `bun:"ID,pk"` + OrderId int `bun:"ORDER_ID,pk"` +} diff --git a/services/order.go b/services/order.go index c5409e0..7f08638 100644 --- a/services/order.go +++ b/services/order.go @@ -24,7 +24,7 @@ func addProductsToOrder(ctx context.Context, storage storage.StorageCart, api bi } for _, product := range *items { - err = api.AddProductToOrder(orderId, int(product.ProductId), product.Product.Price.BASE, int(product.Quantity)) + err = api.AddProductToOrder(orderId, int(product.ProductId), product.Product.Price.BASE, int(product.Quantity), product.Product.Name) if err != nil { return err } @@ -35,7 +35,7 @@ func addProductsToOrder(ctx context.Context, storage storage.StorageCart, api bi // -func MakeOrder(ctx context.Context, fuserId int, email string, fullName string, phone string) (*kassa.KassaResult, error) { +func MakeOrder(ctx context.Context, fuserId int, email string, fullName, phone string) (*kassa.KassaResult, error) { // // Инициализируем api s := storage.NewStorageCart() diff --git a/services/validate.go b/services/validate.go index 0e8a98e..8312c50 100644 --- a/services/validate.go +++ b/services/validate.go @@ -1,5 +1,51 @@ package services +import ( + "context" + "log" + "relynolli-server/external/bitrix" + "relynolli-server/external/kassa" + "relynolli-server/storage" + "time" +) + +func PaymentValidation() { + ctx := context.Background() + s := storage.NewStorageOrder() + api := bitrix.Initialize() + + for { + payments, err := s.GetPayments(ctx) + if err != nil { + panic(err.Error()) + } + for _, payment := range *payments { + result, _ := kassa.CheckPayment(payment.ID) + + if result == nil { + continue + } + payment.Status = result.Status + if result.Status == "succeeded" { + err := api.ApprovePayment(int(payment.BitrixPayment.ID), 8) + if err != nil { + log.Println(err.Error()) + continue + } + } + if result.Status == "canceled" { + err := api.CancelOrder(int(payment.OrderId)) + if err != nil { + log.Println(err.Error()) + } + } + s.UpdatePayment(ctx, &payment) + time.Sleep(1 * time.Second) + } + time.Sleep(5 * time.Second) + } +} + // //func YookassaValidate(paymentId string, status string) { // stmt := fmt.Sprintf(`select t1.order_id as order_id, t2.ID as payment_id from api_youkassa_payment t1 join b_sale_order_payment t2 on t1.order_id = t2.ORDER_ID where t1.payment_id = '%s';`, paymentId) diff --git a/storage/article.go b/storage/article.go index 82be054..c022900 100644 --- a/storage/article.go +++ b/storage/article.go @@ -1 +1,43 @@ package storage + +import ( + "context" + "relynolli-server/internal" + "relynolli-server/models/news" +) + +type StorageArticle interface { + GetArticles(ctx context.Context, limit, offset int64) (int, *[]news.DBArticle, error) + RetrieveArticle(ctx context.Context, code string) (*news.DBArticle, error) +} + +func NewStorageArticle() StorageArticle { + if instance == nil { + instance = &storage{ + db: internal.InitDatabase().GetInstance(), + rdb: internal.InitRedis(), + } + } + return instance +} + +func (s *storage) GetArticles(ctx context.Context, limit, offset int64) (int, *[]news.DBArticle, error) { + model := new([]news.DBArticle) + stmt := s.db.NewSelect().Model(model).Where("is_active = 1").OrderExpr("sort ASC, date DESC").Limit(int(limit)).Offset(int(offset)) + count, err := stmt.ScanAndCount(ctx) + if err != nil { + return 0, nil, err + } + + return count, model, nil +} + +func (s *storage) RetrieveArticle(ctx context.Context, code string) (*news.DBArticle, error) { + model := new(news.DBArticle) + stmt := s.db.NewSelect().Model(model).Where("code = ?", code).Where("is_active = 1") + err := stmt.Scan(ctx) + if err != nil { + return nil, err + } + return model, nil +} diff --git a/storage/order.go b/storage/order.go index 5a3f400..ad4f91b 100644 --- a/storage/order.go +++ b/storage/order.go @@ -1,12 +1,22 @@ package storage import ( + "bytes" "context" + "encoding/json" + "github.com/uptrace/bun" + "os/exec" + "relynolli-server/external/bitrix" "relynolli-server/internal" + "relynolli-server/models/cart" + "relynolli-server/models/discount" + "relynolli-server/models/order" ) type StorageOrder interface { - GetTotal(ctx context.Context, fuserId int64) (*TotalQuery, error) + GetTotal(ctx context.Context, fuserId int64, coupon *string) (*TotalQuery, error) + UpdatePayment(ctx context.Context, payment *order.DBPayment) error + GetPayments(ctx context.Context) (*[]order.DBPayment, error) } func NewStorageOrder() StorageOrder { @@ -19,16 +29,108 @@ func NewStorageOrder() StorageOrder { return instance } -type TotalQuery struct { - Total float64 `bun:"total" json:"total"` +type DiscountQuery struct { + Name string + Value int64 } -func (s *storage) GetTotal(ctx context.Context, fuserId int64) (*TotalQuery, error) { - model := new(TotalQuery) - stmt := `select sum(price.PRICE * cart.quantity) as total from api_cart cart left join b_catalog_price price on cart.product_id = price.PRODUCT_ID and cart.price_type_id = price.CATALOG_GROUP_ID where fuser_id = ?` - err := s.db.NewRaw(stmt, fuserId).Scan(ctx, model) +type QueryItem struct { + Cart *cart.DBCart `json:"cart"` + Discount *DiscountQuery `json:"discount"` +} + +type TotalQuery struct { + Total float64 `bun:"total" json:"total"` + BasePrice float64 `bun:"-" json:"basePrice"` + Items *[]QueryItem `bun:"-" json:"items"` +} + +func (s *storage) fetchDiscounts(ctx context.Context) (*[]discount.DBDiscount, error) { + model := new([]discount.DBDiscount) + err := s.db.NewSelect().Model(model).Scan(ctx) if err != nil { return nil, err } return model, nil } + +type DiscountType struct { + CLASSID string `json:"CLASS_ID"` + DATA struct { + All string `json:"All"` + } `json:"DATA"` + CHILDREN []struct { + CLASSID string `json:"CLASS_ID"` + DATA struct { + Type string `json:"Type"` + Value int `json:"Value"` + Unit string `json:"Unit"` + Max int `json:"Max"` + All string `json:"All"` + True string `json:"True"` + } `json:"DATA"` + CHILDREN []interface{} `json:"CHILDREN"` + } `json:"CHILDREN"` +} + +func (s *storage) getDiscountForCoupon(ctx context.Context, coupon string) (*DiscountType, error) { + var couponDiscountPhpQuery string + stmt := "select disc.ACTIONS from b_sale_discount_coupon coupon join b_sale_discount disc on coupon.DISCOUNT_ID = disc.ID where coupon.COUPON = ? and coupon.ACTIVE = 'Y'" + err := s.db.NewRaw(stmt, coupon).Scan(ctx, &couponDiscountPhpQuery) + if err != nil { + return nil, err + } + var out bytes.Buffer + var result DiscountType + + cmd := exec.Command("php", "test.php", couponDiscountPhpQuery) + cmd.Stdout = &out + err = cmd.Run() + + if err != nil { + return nil, err + } + + err = json.Unmarshal(out.Bytes(), &result) + if err != nil { + return nil, err + } + + return &result, nil +} + +func (s *storage) GetTotal(ctx context.Context, fuserId int64, coupon *string) (*TotalQuery, error) { + model := new(TotalQuery) + + api := bitrix.Initialize() + data, err := api.GetTotalForProduct(int(fuserId), coupon) + + if err != nil { + return nil, err + } + model.Total = data.Price + model.BasePrice = data.BasePrice + return model, nil +} + +func (s *storage) GetPayments(ctx context.Context) (*[]order.DBPayment, error) { + var model []order.DBPayment + + err := s.db.NewSelect().Model(&model).Where("status not in (?)", bun.In([]string{"succeeded", "canceled"})).Order("order_id DESC").Scan(ctx) + if err != nil { + return nil, err + } + + for idx, item := range model { + bitrixPayment := new(order.DBOrderPayment) + s.db.NewSelect().Model(bitrixPayment).Where("ORDER_ID = ?", item.OrderId).Scan(ctx) + model[idx].BitrixPayment = bitrixPayment + } + + return &model, nil +} + +func (s *storage) UpdatePayment(ctx context.Context, payment *order.DBPayment) error { + _, err := s.db.NewUpdate().Model(payment).WherePK().Exec(ctx) + return err +} diff --git a/test.php b/test.php new file mode 100644 index 0000000..e8553a6 --- /dev/null +++ b/test.php @@ -0,0 +1,9 @@ +