init
This commit is contained in:
commit
0cb03a74a9
|
|
@ -0,0 +1,2 @@
|
|||
DB_DSN="usr:pwd@tcp(127.0.0.1:3306)/test_baza?parseTime=true"
|
||||
UPDATE_PERIOD=30m
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.env
|
||||
vendor
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/sarga_updater.iml" filepath="$PROJECT_DIR$/.idea/sarga_updater.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package bagisto_models
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Brand struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Name string `gorm:"default:TESTBRAND"`
|
||||
Code string
|
||||
Status bool
|
||||
Categories []Category `gorm:"many2many:category_brands;"`
|
||||
}
|
||||
|
||||
func FindOrCreateBrand(db *gorm.DB, brand string, categories []Category) (Brand, error) {
|
||||
|
||||
var brandObject Brand
|
||||
|
||||
if brand != "" {
|
||||
|
||||
code := slug.Make(brand)
|
||||
|
||||
err := db.FirstOrCreate(&brandObject, Brand{Name: brand, Code: code}).Error
|
||||
//err := db.Model(Brand{Name: brand, Code: code}).First(&brandObject).Error
|
||||
//log.Println(Brand{Name: brand, Code: code})
|
||||
|
||||
if err != nil {
|
||||
//err := db.Omit("Categories.*").FirstOrCreate(&brandObject, Brand{Name: brand, Code: code, Categories: categories}).Error
|
||||
log.Println("ERR0000000000000001" + err.Error())
|
||||
return brandObject, err
|
||||
}
|
||||
|
||||
db.Model(&brandObject).Association("Categories").Append(categories)
|
||||
|
||||
return brandObject, nil
|
||||
|
||||
}
|
||||
|
||||
// var brandObject Brand
|
||||
|
||||
return brandObject, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package bagisto_models
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CategoryTranslation struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Slug string
|
||||
CategoryID uint
|
||||
MetaDescription string
|
||||
MetaKeywords string
|
||||
Locale string
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Status int8
|
||||
Position int
|
||||
ParentId uint
|
||||
DisplayMode string
|
||||
Translations []CategoryTranslation
|
||||
}
|
||||
|
||||
func GetMainCategories(db *gorm.DB) ([]Category, error) {
|
||||
var categories []Category
|
||||
err := db.Model(&Category{}).Preload("Translations").Find(&categories, "status=1 AND parent_id=1 AND display_mode!='promotion'").Error
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func GetCatKeywords(db *gorm.DB, catIDs []int) ([]Category, error) {
|
||||
var categories []Category
|
||||
|
||||
if errCat := db.Preload("Translations", "locale=?", "tm").Find(&categories, catIDs).Error; errCat != nil {
|
||||
log.Println(errCat)
|
||||
return categories, errCat
|
||||
}
|
||||
|
||||
return categories, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package bagisto_models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AttributeOption struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
AttributeID uint
|
||||
AdminName string
|
||||
SortOrder int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Translations []AttributeOptionTranslation
|
||||
}
|
||||
|
||||
type AttributeOptionTranslation struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
AttributeOptionID uint
|
||||
Locale string
|
||||
Label string
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Code string
|
||||
AttributeOptions []AttributeOption
|
||||
}
|
||||
|
||||
type AttributeFamily struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Code string
|
||||
Groups []AttributeGroup
|
||||
}
|
||||
|
||||
type AttributeGroup struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string
|
||||
AttributeFamilyID uint
|
||||
Attributes []Attribute `gorm:"many2many:attribute_group_mappings;"`
|
||||
}
|
||||
|
||||
func GetFamilies(db *gorm.DB) ([]AttributeFamily, error) {
|
||||
var families []AttributeFamily
|
||||
|
||||
err := db.Model(&AttributeFamily{}).Preload("Groups").Order("id ASC").Find(&families).Error
|
||||
|
||||
return families, err
|
||||
}
|
||||
|
||||
func GetGroups(db *gorm.DB) []AttributeGroup {
|
||||
var groups []AttributeGroup
|
||||
err := db.Model(&AttributeGroup{}).Preload("Family").Find(&groups).Error
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
func GetAttributes(db *gorm.DB) ([]Attribute, error) {
|
||||
var attributes []Attribute
|
||||
|
||||
err := db.Model(&Attribute{}).Find(&attributes).Error
|
||||
|
||||
return attributes, err
|
||||
}
|
||||
|
||||
func GetAttributeOption(db *gorm.DB, attrbuteID uint, value string) AttributeOption {
|
||||
var option AttributeOption
|
||||
|
||||
err := db.Where("attribute_id=? and admin_name=?", attrbuteID, value).First(&option).Error
|
||||
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
option = AttributeOption{
|
||||
AttributeID: attrbuteID,
|
||||
AdminName: value,
|
||||
SortOrder: 1000,
|
||||
Translations: []AttributeOptionTranslation{
|
||||
{Locale: "tm", Label: value},
|
||||
{Locale: "ru", Label: value},
|
||||
{Locale: "tr", Label: value},
|
||||
},
|
||||
}
|
||||
|
||||
if err := db.Create(&option).Error; err != nil {
|
||||
log.Println(err.Error())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return option
|
||||
}
|
||||
|
||||
func GetAttrOptions(db *gorm.DB, attrbuteID uint) ([]AttributeOption, error) {
|
||||
|
||||
var options []AttributeOption
|
||||
|
||||
err := db.Find(&options, AttributeOption{AttributeID: attrbuteID}).Error
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
return options, err
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
package bagisto_models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Sku string
|
||||
Type string
|
||||
ParentID uint `sql:"DEFAULT:NULL"`
|
||||
AttributeFamilyID uint
|
||||
AttributeFamily AttributeFamily
|
||||
BrandID uint `sql:"DEFAULT:NULL"`
|
||||
Brand Brand
|
||||
Images []ProductImage
|
||||
Categories []Category `gorm:"many2many:product_categories;"`
|
||||
AttributeValues []ProductAttributeValue
|
||||
SuperAttributes []Attribute `gorm:"many2many:product_super_attributes;"`
|
||||
//Variants []*Product `gorm:"many2many:product_relations;foreignKey:child_id;primaryKey:parent_id;"`
|
||||
}
|
||||
|
||||
type ProductRelation struct {
|
||||
ParentID uint
|
||||
ChildID uint
|
||||
}
|
||||
|
||||
type ProductFlat struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Sku string
|
||||
ProductNumber string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Name string `sql:"DEFAULT:''" gorm:"default:''"`
|
||||
Weight float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||
Status bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
VisibleIndividually bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Price float64 `gorm:"type:decimal(12,4)"`
|
||||
MinPrice float64 `gorm:"type:decimal(12,4)"`
|
||||
MaxPrice float64 `gorm:"type:decimal(12,4)"`
|
||||
SpecialPrice float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||
UrlKey string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
ShortDescription string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Description string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
FavoritesCount uint `sql:"DEFAULT:NULL" gorm:"default:null;column:favoritesCount"`
|
||||
CreatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
UpdatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
ProductID uint
|
||||
Product Product
|
||||
Channel string `sql:"DEFAULT:default" gorm:"default:default"`
|
||||
Locale string `sql:"DEFAULT:tm" gorm:"default:tm"`
|
||||
ParentID uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Variants []ProductFlat `gorm:"foreignKey:ParentID"`
|
||||
Color int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
ColorLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Size int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
SizeLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Boyut int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
BoyutLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
MetaTitle string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
MetaKeywords string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
BrandID uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Brand Brand
|
||||
Cinsiyet int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
CinsiyetLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
}
|
||||
|
||||
type ProductImage struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Type string
|
||||
Path string
|
||||
ProductID uint
|
||||
}
|
||||
|
||||
type ProductAttributeValue struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Locale string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Channel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
ProductID uint
|
||||
AttributeID uint
|
||||
TextValue string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
BooleanValue bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
IntegerValue int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
FloatValue float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||
}
|
||||
|
||||
type ProductSuperAttribute struct {
|
||||
ProductID uint
|
||||
AttributeID uint
|
||||
}
|
||||
|
||||
type Tabler interface {
|
||||
TableName() string
|
||||
}
|
||||
|
||||
// TableName overrides the table name used by User to `profiles`
|
||||
func (ProductFlat) TableName() string {
|
||||
return "product_flat"
|
||||
}
|
||||
|
||||
func DeleteProducts(db *gorm.DB) error {
|
||||
//todo delete from elastico
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
//qb := "DELETE FROM products WHERE id NOT IN (select product_id as id from wishlist) AND id NOT IN (select product_id as id from order_items) AND id NOT IN (select parent_id as idfrom order_items);"
|
||||
qb := "DELETE p FROM products p " +
|
||||
"LEFT JOIN order_items oi ON p.id = oi.product_id " +
|
||||
"LEFT JOIN order_items op ON p.id = op.parent_id " +
|
||||
"LEFT JOIN wishlist wp ON p.id = wp.product_id " +
|
||||
"LEFT JOIN wishlist wl ON wl.product_id = p.parent_id " +
|
||||
"JOIN marketplace_products mp ON p.id = mp.product_id " +
|
||||
"WHERE oi.id IS NULL AND op.id IS NULL AND wp.id IS NULL AND wl.id IS NULL AND mp.marketplace_seller_id=1;"
|
||||
db.WithContext(ctx).Exec(qb)
|
||||
//db.WithContext(ctx).Exec("UPDATE product_flat set sku=concat(id,\"-ordered\"), status=0 where status=1 AND product_id IN (SELECT product_id from marketplace_products where marketplace_seller_id=1)" )
|
||||
//db.WithContext(ctx).Exec("UPDATE products set sku=concat(id,\"-ordered\") WHERE id IN (SELECT product_id from marketplace_products where marketplace_seller_id=1)")
|
||||
return db.Error
|
||||
}
|
||||
|
||||
func GetWishlistProducts(db *gorm.DB) ([]Product, error) {
|
||||
var products []Product
|
||||
err := db.Joins("JOIN wishlist wp ON products.id = wp.product_id").
|
||||
Joins("JOIN marketplace_products mp ON products.id = mp.product_id where marketplace_seller_id=1").
|
||||
Find(&products).Error
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return products, nil
|
||||
//qb := "SELECT p.id,p.sku FROM products p " +
|
||||
// "JOIN wishlist wp ON p.id = wp.product_id " +
|
||||
// "JOIN marketplace_products mp ON p.id = mp.product_id where marketplace_seller_id=1);"
|
||||
}
|
||||
|
||||
func GetFlatSources(db *gorm.DB) ([]ProductAttributeValue, error) {
|
||||
var productSources []ProductAttributeValue
|
||||
err := db.Joins("JOIN wishlist wp ON product_attribute_values.product_id = wp.product_id").
|
||||
Joins("JOIN marketplace_products mp ON product_attribute_values.product_id = mp.product_id where marketplace_seller_id=1").
|
||||
Find(&productSources, "text_value IS NOT NULL AND attribute_id=31").Error
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return productSources, nil
|
||||
}
|
||||
|
||||
//func DisableProducts (db *gorm.DB) error {
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
// defer cancel()
|
||||
// db.WithContext(ctx).Exec("UPDATE product_flat set sku=concat(sku,\"-ordered\"), status=0")
|
||||
// db.WithContext(ctx).Exec("UPDATE products set sku=concat(sku,\"-ordered\")")
|
||||
// return db.Error
|
||||
//}
|
||||
|
||||
//func Flush() error {
|
||||
// _, err := helper.SendRequest("GET", os.Getenv("scout_flash"), nil, "")
|
||||
//
|
||||
// return err
|
||||
//}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package bagisto_models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MarketplaceProduct struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
CreatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
UpdatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
ProductID uint
|
||||
//Product Product
|
||||
ParentID *uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Condition string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
Price float64
|
||||
Description string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
IsApproved bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||
IsOwner bool
|
||||
MarketplaceSellerID uint
|
||||
MarketplaceSeller MarketplaceSeller
|
||||
Variants []MarketplaceProduct `gorm:"foreignKey:ParentID"`
|
||||
}
|
||||
|
||||
type MarketplaceSeller struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Url string
|
||||
}
|
||||
|
||||
func GetSellers(db *gorm.DB) ([]MarketplaceSeller, error) {
|
||||
var sellers []MarketplaceSeller
|
||||
|
||||
err := db.Model(&MarketplaceSeller{}).Find(&sellers).Error
|
||||
|
||||
return sellers, err
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package controllers
|
||||
|
||||
import "net/http"
|
||||
|
||||
func StartUpdate(w http.ResponseWriter, _ *http.Request) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
module sarga_updater
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/gosimple/slug v1.13.1
|
||||
github.com/leesper/couchdb-golang v1.2.1
|
||||
gorm.io/driver/mysql v1.5.0
|
||||
gorm.io/gorm v1.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
|
||||
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
|
||||
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/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/leesper/couchdb-golang v1.2.1 h1:FqSaTxxT2mVRLbxGQVkZakRRoSzWhPmV8UEKYjA/GWc=
|
||||
github.com/leesper/couchdb-golang v1.2.1/go.mod h1:OU3FDAM3mazHx15oi8Hm+egTMneBUqepwnh0LuBSH54=
|
||||
gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM=
|
||||
gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
|
||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/leesper/couchdb-golang"
|
||||
models "sarga_updater/trendyol_models"
|
||||
)
|
||||
|
||||
// CdbServer is global couchdb server
|
||||
var CdbServer *couchdb.Server
|
||||
|
||||
// Categories stores all categories and their weight
|
||||
var Categories []models.CouchCategory = []models.CouchCategory{}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
models "sarga_updater/trendyol_models"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CheckDBExists(endpoint string) bool {
|
||||
|
||||
const ConnectMaxWaitTime = 30 * time.Second
|
||||
const RequestMaxWaitTime = 60 * time.Second
|
||||
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: ConnectMaxWaitTime,
|
||||
}).DialContext,
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
|
||||
req.Proto = "HTTP/1.0"
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Connection", "close")
|
||||
req.Close = true
|
||||
client.CloseIdleConnections()
|
||||
|
||||
response, err := client.Do(req)
|
||||
|
||||
// handle timeout
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
log.Println(err.Error())
|
||||
return false
|
||||
} else if err != nil {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
log.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func GetCategoryWeight(slug string) string {
|
||||
|
||||
db, err := CdbServer.Get(os.Getenv("db_ty_categories"))
|
||||
|
||||
if err != nil {
|
||||
Error("can not get db. Err: " + err.Error())
|
||||
return "0.5"
|
||||
}
|
||||
|
||||
doc, err := db.Get(slug, nil)
|
||||
|
||||
if err != nil {
|
||||
Error("can not get by slug. Err: " + err.Error())
|
||||
return "0.5"
|
||||
}
|
||||
|
||||
return doc["weight"].(string)
|
||||
}
|
||||
func IsLCWSizeVariantsAdded(items []models.Variant, keyItem models.Variant) bool {
|
||||
for _, option := range items {
|
||||
if option.ItemNumber == keyItem.ItemNumber {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsImageAdded(images []string, image string) bool {
|
||||
for _, img := range images {
|
||||
if img == image {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func IsVariantsAdded(variants []models.Variant, itemNumber int) bool {
|
||||
for _, variant := range variants {
|
||||
if variant.ItemNumber == itemNumber {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Level int
|
||||
|
||||
var (
|
||||
DefaultPrefix = ""
|
||||
DefaultCallerDepth = 2
|
||||
|
||||
logger *log.Logger
|
||||
logPrefix = ""
|
||||
levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
|
||||
)
|
||||
|
||||
const (
|
||||
DEBUG Level = iota
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
FATAL
|
||||
)
|
||||
|
||||
// Setup initialize the log instance
|
||||
func Setup() {
|
||||
logger = log.New(os.Stdout, DefaultPrefix, log.Flags()&^(log.Ldate|log.Ltime))
|
||||
}
|
||||
|
||||
// Debug output logs at debug level
|
||||
func Debug(v ...interface{}) {
|
||||
setPrefix(DEBUG)
|
||||
logger.Println(v...)
|
||||
}
|
||||
|
||||
// Info output logs at info level
|
||||
func Info(v ...interface{}) {
|
||||
setPrefix(INFO)
|
||||
logger.Println(v...)
|
||||
}
|
||||
|
||||
// Warn output logs at warn level
|
||||
func Warn(v ...interface{}) {
|
||||
setPrefix(WARNING)
|
||||
logger.Println(v...)
|
||||
}
|
||||
|
||||
// Error output logs at error level
|
||||
func Error(v ...interface{}) {
|
||||
prefix := setPrefix(ERROR)
|
||||
logger.Println(v...)
|
||||
|
||||
prefix.msg = v
|
||||
}
|
||||
|
||||
// Fatal output logs at fatal level
|
||||
func Fatal(v ...interface{}) {
|
||||
setPrefix(FATAL)
|
||||
logger.Fatalln(v...)
|
||||
}
|
||||
|
||||
// setPrefix set the prefix of the log output
|
||||
func setPrefix(level Level) prefix {
|
||||
_, file, line, ok := runtime.Caller(DefaultCallerDepth)
|
||||
if ok {
|
||||
logPrefix = fmt.Sprintf("[%s:%d] [%s] ", filepath.Base(file), line, levelFlags[level])
|
||||
} else {
|
||||
logPrefix = fmt.Sprintf("[%s] ", levelFlags[level])
|
||||
}
|
||||
|
||||
logPrefix += "| " + time.Now().Format("01.02.2006 15:04:05") + " | "
|
||||
|
||||
logger.SetPrefix(logPrefix)
|
||||
return prefix{prefix: logPrefix}
|
||||
}
|
||||
|
||||
type prefix struct {
|
||||
prefix string
|
||||
msg interface{}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SendRequest(method string, endpoint string, values []byte, authKey string) ([]byte, error) {
|
||||
|
||||
const ConnectMaxWaitTime = time.Minute
|
||||
const RequestMaxWaitTime = 5 * time.Minute
|
||||
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: ConnectMaxWaitTime,
|
||||
}).DialContext,
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||
defer cancel()
|
||||
|
||||
emptyBody := []byte{}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, endpoint, bytes.NewBuffer(values))
|
||||
req.Proto = "HTTP/2.0"
|
||||
if err != nil {
|
||||
Error(err)
|
||||
return emptyBody, err
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Connection", "close")
|
||||
req.Header.Add("Authorization", authKey)
|
||||
req.Close = true
|
||||
client.CloseIdleConnections()
|
||||
|
||||
response, err := client.Do(req)
|
||||
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
Error(err)
|
||||
return emptyBody, err
|
||||
} else if err != nil {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
Error(err)
|
||||
return emptyBody, err
|
||||
}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
err := fmt.Errorf("response: code: %d, body: %v", response.StatusCode, response.Body)
|
||||
Error(err)
|
||||
return emptyBody, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
Error(err)
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
return emptyBody, nil
|
||||
}
|
||||
|
||||
// if you call it in loop, don't use defer
|
||||
response.Body.Close()
|
||||
|
||||
return body, nil
|
||||
}
|
||||
func NewHttpClient() (http.Client, context.Context) {
|
||||
const ConnectMaxWaitTime = 30 * time.Second
|
||||
const RequestMaxWaitTime = 60 * time.Second
|
||||
//proxyUrl, _ := url.Parse("http://79.98.129.183:3128")
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: ConnectMaxWaitTime,
|
||||
}).DialContext,
|
||||
//Proxy: http.ProxyURL(proxyUrl),
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||
defer cancel()
|
||||
|
||||
return client, ctx
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package sarga_updater
|
||||
|
||||
import (
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sarga_updater/bagisto_models"
|
||||
"sarga_updater/repositories"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Retrieve database connection information from environment variables
|
||||
dsn := os.Getenv("DB_DSN")
|
||||
|
||||
// Connect to the database
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("Error connecting to database: %s", err)
|
||||
}
|
||||
importer, _ := repositories.ParseImporterInstance(db)
|
||||
|
||||
updatePeriodStr := os.Getenv("UPDATE_PERIOD")
|
||||
updatePeriod, err := time.ParseDuration(updatePeriodStr)
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing update period:", err)
|
||||
}
|
||||
// Start the worker
|
||||
stopCh := make(chan struct{})
|
||||
go worker(importer, stopCh, updatePeriod)
|
||||
|
||||
// Wait for termination signal
|
||||
signalCh := make(chan os.Signal, 1)
|
||||
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
|
||||
<-signalCh
|
||||
|
||||
// Stop the worker
|
||||
close(stopCh)
|
||||
|
||||
// Close the database connection
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting database connection from gorm: %s", err)
|
||||
}
|
||||
err = sqlDB.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Error closing database connection: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func worker(importer *repositories.Importer, stopCh <-chan struct{}, updatePeriod time.Duration) {
|
||||
ticker := time.NewTicker(updatePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
|
||||
result, err := bagisto_models.GetFlatSources(importer.Baza)
|
||||
if err != nil {
|
||||
log.Println("Error retrieving products:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Update products as needed
|
||||
for _, product := range result {
|
||||
// Retrieve the latest information from the source URL
|
||||
parser := repositories.NewLinkParser(product.TextValue)
|
||||
data, err := parser.ParseLink()
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error decoding product information:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
jsonProduct, err := parser.GetProductDetailWithOptions(data.ID, data.ProductGroupID)
|
||||
|
||||
// Update the product in the database
|
||||
if err := importer.UpdateOrCreate(jsonProduct).Error; err != nil {
|
||||
log.Println("Error decoding product information:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Product %d updated: %+v\n", product.ID, product)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,842 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"math"
|
||||
gm "sarga_updater/bagisto_models"
|
||||
helper "sarga_updater/helpers"
|
||||
models "sarga_updater/trendyol_models"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Importer struct {
|
||||
mainCategories []gm.Category
|
||||
Baza *gorm.DB
|
||||
families []gm.AttributeFamily
|
||||
sellers map[string]gm.MarketplaceSeller
|
||||
//wishlist map[string]gm.Product
|
||||
AttributesMap map[string]gm.Attribute
|
||||
Error error
|
||||
ImportWGroup sync.WaitGroup
|
||||
ColorOptions map[string]gm.AttributeOption
|
||||
SexOptions map[string]gm.AttributeOption
|
||||
ColorMutex sync.Mutex
|
||||
SexMutex sync.Mutex
|
||||
}
|
||||
type GormErr struct {
|
||||
Number int `json:"Number"`
|
||||
Message string `json:"Message"`
|
||||
}
|
||||
|
||||
func ParseImporterInstance(db *gorm.DB) (instance *Importer, err error) {
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instance = &Importer{Baza: db}
|
||||
instance.ImportWGroup.Add(3)
|
||||
//load families to memory
|
||||
go func() {
|
||||
defer instance.ImportWGroup.Done()
|
||||
instance.families, instance.Error = gm.GetFamilies(db)
|
||||
}()
|
||||
|
||||
//load attributes to memory
|
||||
go func() {
|
||||
defer instance.ImportWGroup.Done()
|
||||
|
||||
if attributes, err := gm.GetAttributes(db); err != nil {
|
||||
instance.Error = err
|
||||
return
|
||||
} else {
|
||||
instance.AttributesMap = make(map[string]gm.Attribute, len(attributes))
|
||||
|
||||
for _, attribute := range attributes {
|
||||
instance.AttributesMap[attribute.Code] = attribute
|
||||
}
|
||||
}
|
||||
|
||||
if colorOptions, err := gm.GetAttrOptions(db, instance.AttributesMap["color"].ID); err != nil {
|
||||
instance.Error = err
|
||||
return
|
||||
} else {
|
||||
instance.ColorOptions = make(map[string]gm.AttributeOption, len(colorOptions))
|
||||
|
||||
for _, option := range colorOptions {
|
||||
instance.ColorOptions[option.AdminName] = option
|
||||
}
|
||||
}
|
||||
|
||||
if sexOPtions, err := gm.GetAttrOptions(db, instance.AttributesMap["cinsiyet"].ID); err != nil {
|
||||
instance.Error = err
|
||||
return
|
||||
} else {
|
||||
instance.SexOptions = make(map[string]gm.AttributeOption, len(sexOPtions))
|
||||
|
||||
for _, option := range sexOPtions {
|
||||
instance.SexOptions[option.AdminName] = option
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
//load sellers to memory
|
||||
go func() {
|
||||
defer instance.ImportWGroup.Done()
|
||||
|
||||
var vendors, err = gm.GetSellers(db)
|
||||
|
||||
if err != nil {
|
||||
instance.Error = err
|
||||
return
|
||||
}
|
||||
//init sellers map
|
||||
instance.sellers = make(map[string]gm.MarketplaceSeller, len(vendors))
|
||||
|
||||
for _, vendor := range vendors {
|
||||
instance.sellers[vendor.Url] = vendor
|
||||
}
|
||||
}()
|
||||
|
||||
if instance.Error != nil {
|
||||
log.Println(instance.Error)
|
||||
return nil, instance.Error
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (importer *Importer) ImportProduct(product models.Product) (instance *Importer) {
|
||||
|
||||
var linkedProducts []gm.Product
|
||||
var firstProduct *gm.Product
|
||||
if firstProduct, importer.Error = importer.importVariant(product); importer.Error != nil {
|
||||
return importer
|
||||
} else if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||
linkedProducts = append(linkedProducts, *firstProduct)
|
||||
for _, colorVariant := range *product.ColorVariants {
|
||||
|
||||
if !colorVariant.IsSellable {
|
||||
continue
|
||||
}
|
||||
|
||||
if variant, err := importer.importVariant(colorVariant); err == nil {
|
||||
linkedProducts = append(linkedProducts, *variant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(linkedProducts) > 1 {
|
||||
|
||||
var relation []gm.ProductRelation
|
||||
for index, variant := range linkedProducts {
|
||||
//spoint := "color" + strconv.Itoa(index)
|
||||
temp := make([]gm.Product, len(linkedProducts))
|
||||
|
||||
copy(temp, linkedProducts)
|
||||
|
||||
if index+1 <= len(temp) {
|
||||
temp = append(temp[:index], temp[index+1:]...)
|
||||
|
||||
for _, item := range temp {
|
||||
relation = append(relation, gm.ProductRelation{ParentID: variant.ID, ChildID: item.ID})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return importer
|
||||
}
|
||||
|
||||
func (importer *Importer) importVariant(product models.Product) (*gm.Product, error) {
|
||||
|
||||
// check if wishlisted then update if.
|
||||
//if _, ok := importer.wishlist[product.ProductNumber]; ok {
|
||||
// delete(importer.wishlist,product.ProductNumber)
|
||||
// return importer.updateVariant(product)
|
||||
//}
|
||||
|
||||
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||
|
||||
if categories, err := gm.GetCatKeywords(importer.Baza, product.Categories); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
productRepo.SetCategories(categories)
|
||||
}
|
||||
|
||||
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
productRepo.Brand = brand
|
||||
}
|
||||
|
||||
mainPorduct := productRepo.makeProduct(importer)
|
||||
|
||||
//BEGIN TRANSACTION
|
||||
tx := importer.Baza.Begin()
|
||||
|
||||
if err := tx.Omit("Categories.*", "SuperAttributes.*", "ParentID").Create(&mainPorduct).Error; err != nil {
|
||||
//todo update categories
|
||||
byteErr, _ := json.Marshal(err)
|
||||
var newError GormErr
|
||||
|
||||
if err1 := json.Unmarshal((byteErr), &newError); err1 != nil {
|
||||
tx.Rollback()
|
||||
log.Println(err1, "err2")
|
||||
return nil, err1
|
||||
}
|
||||
|
||||
if newError.Number == 1062 {
|
||||
var barProduct gm.Product
|
||||
if err2 := tx.First(&barProduct, "sku = ?", mainPorduct.Sku).Error; err2 != nil {
|
||||
tx.Rollback()
|
||||
log.Println(err2, "err3")
|
||||
return nil, err2
|
||||
}
|
||||
|
||||
if err3 := tx.Model(&barProduct).Association("Categories").Append(mainPorduct.Categories); err3 != nil {
|
||||
tx.Rollback()
|
||||
log.Println(err3, "err4")
|
||||
return nil, err3
|
||||
}
|
||||
|
||||
if err4 := tx.Commit().Error; err4 == nil {
|
||||
return importer.updateVariant(product)
|
||||
}
|
||||
} else {
|
||||
tx.Rollback()
|
||||
log.Println(err, "er1")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mainFlat := productRepo.makeProductFlat(mainPorduct.ID)
|
||||
|
||||
if err := tx.Create(&mainFlat).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Println(err, "er5")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if productRepo.HasSizeVariants() {
|
||||
var sizeVariants []gm.ProductFlat
|
||||
for index, variant := range *product.SizeVariants {
|
||||
if !variant.Sellable {
|
||||
continue
|
||||
}
|
||||
savePoint := "size" + strconv.Itoa(index)
|
||||
tx.SavePoint(savePoint)
|
||||
|
||||
var sizeOPtion gm.AttributeOption
|
||||
|
||||
if variant.AttributeName == "Beden" {
|
||||
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||
} else {
|
||||
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||
}
|
||||
|
||||
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||
|
||||
variantProduct := productRepo.makeVariant(mainPorduct.ID, mainPorduct.AttributeFamilyID, sku)
|
||||
|
||||
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||
|
||||
if err := tx.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||
log.Println("Variant Product Create Error: " + err.Error())
|
||||
tx.RollbackTo(savePoint)
|
||||
continue
|
||||
}
|
||||
|
||||
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, mainFlat.ID, variantProduct.ID)
|
||||
|
||||
if err := tx.Create(&variantFlat).Error; err != nil {
|
||||
log.Println("Variant Flat Create Error: " + err.Error())
|
||||
tx.RollbackTo(savePoint)
|
||||
continue
|
||||
}
|
||||
|
||||
sizeVariants = append(sizeVariants, variantFlat)
|
||||
}
|
||||
|
||||
if len(sizeVariants) == 0 {
|
||||
tx.Rollback()
|
||||
return nil, errors.New("size variantlary yok bolsa main productam girayenok")
|
||||
} else {
|
||||
calcPrice(sizeVariants, &mainFlat)
|
||||
|
||||
err := tx.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&mainFlat).Error
|
||||
|
||||
mainFlat.Variants = sizeVariants
|
||||
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
log.Println(err, "er6")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sProduct := importer.createSellerProduct(&mainFlat, product.Vendor)
|
||||
|
||||
if errSProduct := tx.Create(&sProduct).Error; errSProduct != nil {
|
||||
tx.Rollback()
|
||||
return nil, errSProduct
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mainPorduct, nil
|
||||
}
|
||||
|
||||
func (importer *Importer) createSellerProduct(flat *gm.ProductFlat, sellerURL string) gm.MarketplaceProduct {
|
||||
sellerID := importer.sellers[sellerURL].ID
|
||||
|
||||
if sellerID == 0 {
|
||||
sellerID = 1
|
||||
}
|
||||
|
||||
sellerProduct := gm.MarketplaceProduct{
|
||||
MarketplaceSellerID: sellerID,
|
||||
IsApproved: true,
|
||||
Condition: "new",
|
||||
Description: "scraped",
|
||||
IsOwner: true,
|
||||
ProductID: flat.ProductID,
|
||||
}
|
||||
|
||||
for _, variant := range flat.Variants {
|
||||
sellerProduct.Variants = append(sellerProduct.Variants, gm.MarketplaceProduct{
|
||||
ProductID: variant.ProductID,
|
||||
IsOwner: true,
|
||||
IsApproved: true,
|
||||
MarketplaceSellerID: sellerID,
|
||||
Condition: "new",
|
||||
})
|
||||
}
|
||||
|
||||
return sellerProduct
|
||||
|
||||
}
|
||||
|
||||
func calcPrice(variants []gm.ProductFlat, flat *gm.ProductFlat) {
|
||||
for _, variant := range variants {
|
||||
|
||||
if !variant.Status {
|
||||
continue
|
||||
}
|
||||
|
||||
if flat.MinPrice == 0 || flat.MinPrice > variant.MinPrice {
|
||||
flat.MinPrice = variant.MinPrice
|
||||
}
|
||||
|
||||
flat.MaxPrice = math.Max(flat.MaxPrice, variant.MaxPrice)
|
||||
}
|
||||
}
|
||||
|
||||
func (importer *Importer) GetColorOption(optionName string) gm.AttributeOption {
|
||||
if optionName == "" {
|
||||
return gm.AttributeOption{}
|
||||
}
|
||||
|
||||
importer.ColorMutex.Lock()
|
||||
var option gm.AttributeOption
|
||||
var ok bool
|
||||
|
||||
if option, ok = importer.ColorOptions[optionName]; !ok {
|
||||
option := gm.GetAttributeOption(importer.Baza, importer.AttributesMap["color"].ID, optionName)
|
||||
|
||||
importer.ColorOptions[optionName] = option
|
||||
}
|
||||
importer.ColorMutex.Unlock()
|
||||
return option
|
||||
}
|
||||
|
||||
func (importer *Importer) GetSexOption(optionName string) gm.AttributeOption {
|
||||
if optionName == "" {
|
||||
return gm.AttributeOption{}
|
||||
}
|
||||
importer.SexMutex.Lock()
|
||||
var option gm.AttributeOption
|
||||
var ok bool
|
||||
|
||||
if option, ok = importer.ColorOptions[optionName]; !ok {
|
||||
option = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["cinsiyet"].ID, optionName)
|
||||
importer.SexOptions[optionName] = option
|
||||
}
|
||||
importer.SexMutex.Unlock()
|
||||
return option
|
||||
}
|
||||
|
||||
func (importer *Importer) updateVariant(product models.Product) (*gm.Product, error) {
|
||||
|
||||
var flat gm.ProductFlat
|
||||
err := importer.Baza.Preload("Product").Preload("Variants").First(&flat, "sku = ?", product.ProductNumber).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return importer.importVariant(product)
|
||||
}
|
||||
//todo not found bolsa create etmeli
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if flat.Product.Type == "configurable" {
|
||||
|
||||
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
productRepo.Brand = brand
|
||||
}
|
||||
|
||||
for _, variant := range *product.SizeVariants {
|
||||
|
||||
found := false
|
||||
for _, flatVariant := range flat.Variants {
|
||||
|
||||
if variant.AttributeValue == flatVariant.BoyutLabel || variant.AttributeValue == flatVariant.SizeLabel {
|
||||
|
||||
if !variant.Sellable {
|
||||
importer.Baza.Model(&flatVariant).Update("status", false)
|
||||
} else {
|
||||
|
||||
importer.updatePrice(variant.Price, &flatVariant)
|
||||
|
||||
if !flatVariant.Status {
|
||||
|
||||
importer.Baza.Model(&flatVariant).Update("status", true)
|
||||
}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if variant.Sellable && !found {
|
||||
// insert variant
|
||||
var sizeOPtion gm.AttributeOption
|
||||
|
||||
if variant.AttributeName == "Beden" {
|
||||
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||
} else {
|
||||
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||
}
|
||||
|
||||
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||
variantProduct := productRepo.makeVariant(flat.ProductID, flat.Product.AttributeFamilyID, sku)
|
||||
|
||||
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||
|
||||
if err := importer.Baza.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||
log.Println("Variant Product Create Error: " + err.Error())
|
||||
} else {
|
||||
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, flat.ID, variantProduct.ID)
|
||||
|
||||
if err := importer.Baza.Create(&variantFlat).Error; err != nil {
|
||||
log.Println("Variant Flat Create Error: " + err.Error())
|
||||
|
||||
}
|
||||
flat.Variants = append(flat.Variants, variantFlat)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
calcPrice(flat.Variants, &flat)
|
||||
importer.Baza.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&flat)
|
||||
|
||||
} else { //simple
|
||||
|
||||
importer.updatePrice(product.Price, &flat)
|
||||
|
||||
}
|
||||
return &flat.Product, nil
|
||||
}
|
||||
|
||||
func (importer *Importer) updatePrice(price models.Price, flat *gm.ProductFlat) {
|
||||
if price.OriginalPrice.Value > price.DiscountedPrice.Value {
|
||||
|
||||
importer.Baza.Model(&flat).Updates(map[string]interface{}{
|
||||
"price": price.OriginalPrice.Value,
|
||||
"special_price": price.DiscountedPrice.Value,
|
||||
"min_price": price.DiscountedPrice.Value,
|
||||
"max_price": price.OriginalPrice.Value,
|
||||
})
|
||||
|
||||
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||
Where("attribute_id = 11 and product_id = ?", flat.ProductID).
|
||||
Update("float_value", price.OriginalPrice.Value)
|
||||
|
||||
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||
Where("attribute_id = 13 and product_id = ?", flat.ProductID).
|
||||
Update("float_value", price.DiscountedPrice.Value)
|
||||
} else {
|
||||
|
||||
importer.Baza.Model(&flat).Updates(map[string]interface{}{
|
||||
"price": price.DiscountedPrice.Value,
|
||||
"special_price": nil,
|
||||
"min_price": price.DiscountedPrice.Value,
|
||||
"max_price": price.DiscountedPrice.Value,
|
||||
})
|
||||
|
||||
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||
Where("attribute_id = 11 and product_id = ?", flat.ProductID).
|
||||
Update("float_value", price.DiscountedPrice.Value)
|
||||
|
||||
importer.Baza.Where("attribute_id = 13 and product_id = ?", flat.ProductID).
|
||||
Delete(&gm.ProductAttributeValue{})
|
||||
}
|
||||
}
|
||||
|
||||
func (importer *Importer) importParsedVariant(product models.Product) (gm.Product, error) {
|
||||
|
||||
//todo search if exists
|
||||
|
||||
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||
productRepo.ImageType = "lcw"
|
||||
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||
return gm.Product{}, err
|
||||
} else {
|
||||
productRepo.Brand = brand
|
||||
}
|
||||
mainPorduct := productRepo.makeProduct(importer)
|
||||
|
||||
//BEGIN TRANSACTION
|
||||
tx := importer.Baza.Begin()
|
||||
|
||||
if err := tx.Omit("Categories", "SuperAttributes.*", "ParentID").Create(&mainPorduct).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return gm.Product{}, err
|
||||
|
||||
}
|
||||
|
||||
mainFlat := productRepo.makeProductFlat(mainPorduct.ID)
|
||||
|
||||
if err := tx.Create(&mainFlat).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return gm.Product{}, err
|
||||
}
|
||||
|
||||
if productRepo.HasSizeVariants() {
|
||||
var sizeVariants []gm.ProductFlat
|
||||
for index, variant := range *product.SizeVariants {
|
||||
if !variant.Sellable {
|
||||
continue
|
||||
}
|
||||
savePoint := "size" + strconv.Itoa(index)
|
||||
tx.SavePoint(savePoint)
|
||||
|
||||
var sizeOPtion gm.AttributeOption
|
||||
|
||||
if variant.AttributeName == "Beden" {
|
||||
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||
} else {
|
||||
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||
}
|
||||
|
||||
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||
log.Println(sku)
|
||||
log.Println(variant)
|
||||
|
||||
variantProduct := productRepo.makeVariant(mainPorduct.ID, mainPorduct.AttributeFamilyID, sku)
|
||||
|
||||
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||
|
||||
if err := tx.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||
log.Println("Variant Product Create Error: " + err.Error())
|
||||
tx.RollbackTo(savePoint)
|
||||
continue
|
||||
}
|
||||
|
||||
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, mainFlat.ID, variantProduct.ID)
|
||||
|
||||
if err := tx.Create(&variantFlat).Error; err != nil {
|
||||
log.Println("Variant Flat Create Error: " + err.Error())
|
||||
tx.RollbackTo(savePoint)
|
||||
continue
|
||||
}
|
||||
|
||||
sizeVariants = append(sizeVariants, variantFlat)
|
||||
}
|
||||
|
||||
if len(sizeVariants) == 0 {
|
||||
tx.Rollback()
|
||||
return gm.Product{}, errors.New("siz variantlary yok bolsa main productam girayenok")
|
||||
} else {
|
||||
calcPrice(sizeVariants, &mainFlat)
|
||||
|
||||
err := tx.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&mainFlat).Error
|
||||
|
||||
mainFlat.Variants = sizeVariants
|
||||
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return gm.Product{}, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.Println(product.Vendor)
|
||||
|
||||
sProduct := importer.createSellerProduct(&mainFlat, product.Vendor)
|
||||
|
||||
if errSProduct := tx.Create(&sProduct).Error; errSProduct != nil {
|
||||
tx.Rollback()
|
||||
return gm.Product{}, errSProduct
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return gm.Product{}, err
|
||||
}
|
||||
|
||||
return mainPorduct, nil
|
||||
|
||||
}
|
||||
|
||||
func (importer *Importer) updateParsedVariant(product models.Product) (gm.Product, error) {
|
||||
var flat gm.ProductFlat
|
||||
err := importer.Baza.Preload("Product").Preload("Variants").First(&flat, "sku = ?", product.ProductNumber).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return importer.importParsedVariant(product)
|
||||
}
|
||||
//todo not found bolsa create etmeli
|
||||
return gm.Product{}, err
|
||||
}
|
||||
|
||||
if flat.Product.Type == "configurable" {
|
||||
|
||||
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||
productRepo.ImageType = "lcw"
|
||||
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||
return gm.Product{}, err
|
||||
} else {
|
||||
productRepo.Brand = brand
|
||||
}
|
||||
|
||||
for _, variant := range *product.SizeVariants {
|
||||
|
||||
found := false
|
||||
for _, flatVariant := range flat.Variants {
|
||||
|
||||
if variant.AttributeValue == flatVariant.BoyutLabel || variant.AttributeValue == flatVariant.SizeLabel {
|
||||
|
||||
if !variant.Sellable {
|
||||
importer.Baza.Model(&flatVariant).Update("status", false)
|
||||
} else {
|
||||
|
||||
importer.updatePrice(variant.Price, &flatVariant)
|
||||
|
||||
if !flatVariant.Status {
|
||||
|
||||
importer.Baza.Model(&flatVariant).Update("status", true)
|
||||
}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if variant.Sellable && !found {
|
||||
// insert variant
|
||||
var sizeOPtion gm.AttributeOption
|
||||
|
||||
if variant.AttributeName == "Beden" {
|
||||
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||
} else {
|
||||
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||
}
|
||||
log.Println(variant)
|
||||
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||
log.Println(sku)
|
||||
|
||||
variantProduct := productRepo.makeVariant(flat.ProductID, flat.Product.AttributeFamilyID, sku)
|
||||
|
||||
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||
|
||||
if err := importer.Baza.Omit("Categories").Create(&variantProduct).Error; err != nil {
|
||||
log.Println("Variant Product Create Error: " + err.Error())
|
||||
}
|
||||
|
||||
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, flat.ID, variantProduct.ID)
|
||||
|
||||
if err := importer.Baza.Create(&variantFlat).Error; err != nil {
|
||||
log.Println("Variant Flat Create Error: " + err.Error())
|
||||
|
||||
}
|
||||
flat.Variants = append(flat.Variants, variantFlat)
|
||||
}
|
||||
}
|
||||
|
||||
calcPrice(flat.Variants, &flat)
|
||||
importer.Baza.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&flat)
|
||||
|
||||
} else { //simple
|
||||
|
||||
importer.updatePrice(product.Price, &flat)
|
||||
|
||||
}
|
||||
return flat.Product, nil
|
||||
}
|
||||
|
||||
func (importer *Importer) UpdateOrCreateLCW(product models.Product) (instance *Importer) {
|
||||
var firstProduct gm.Product
|
||||
result := importer.Baza.First(&firstProduct, "sku=?", product.ProductNumber)
|
||||
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
firstProduct, importer.Error = importer.importParsedVariant(product)
|
||||
|
||||
} else {
|
||||
firstProduct, importer.Error = importer.updateParsedVariant(product)
|
||||
|
||||
}
|
||||
|
||||
var newProducts []gm.Product
|
||||
|
||||
if importer.Error != nil {
|
||||
return importer
|
||||
} else if &firstProduct != nil {
|
||||
newProducts = append(newProducts, firstProduct)
|
||||
}
|
||||
|
||||
if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||
linkedProducts := []gm.Product{firstProduct}
|
||||
|
||||
for _, colorVariant := range *product.ColorVariants {
|
||||
|
||||
var (
|
||||
variant gm.Product
|
||||
err error
|
||||
)
|
||||
if !colorVariant.IsSellable {
|
||||
|
||||
if err = importer.Baza.Model(&gm.ProductFlat{}).Where("sku=?", colorVariant.ProductNumber).Update("status", false).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
continue
|
||||
|
||||
} else {
|
||||
|
||||
//todo check parsed import variant
|
||||
if variant, err = importer.importParsedVariant(colorVariant); err != nil {
|
||||
|
||||
if variant, importer.Error = importer.updateParsedVariant(colorVariant); importer.Error != nil {
|
||||
return importer
|
||||
}
|
||||
|
||||
linkedProducts = append(linkedProducts, variant)
|
||||
|
||||
} else {
|
||||
newProducts = append(newProducts, variant)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(newProducts) > 0 {
|
||||
// relation
|
||||
|
||||
var relation []gm.ProductRelation
|
||||
for _, linkedProduct := range linkedProducts {
|
||||
|
||||
for _, newProd := range newProducts {
|
||||
relation = append(relation, gm.ProductRelation{ParentID: linkedProduct.ID, ChildID: newProd.ID})
|
||||
relation = append(relation, gm.ProductRelation{ParentID: newProd.ID, ChildID: linkedProduct.ID})
|
||||
}
|
||||
}
|
||||
|
||||
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return importer
|
||||
}
|
||||
|
||||
func (importer *Importer) UpdateOrCreate(product models.Product) (instance *Importer) {
|
||||
|
||||
firstProduct, err := importer.updateVariant(product)
|
||||
var newProducts []gm.Product
|
||||
if err != nil {
|
||||
helper.Error(err)
|
||||
|
||||
firstProduct, importer.Error = importer.updateVariant(product)
|
||||
|
||||
if importer.Error != nil {
|
||||
return importer
|
||||
}
|
||||
} else if &firstProduct != nil {
|
||||
newProducts = append(newProducts, *firstProduct)
|
||||
}
|
||||
|
||||
if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||
linkedProducts := []gm.Product{*firstProduct}
|
||||
|
||||
for _, colorVariant := range *product.ColorVariants {
|
||||
|
||||
var (
|
||||
variant *gm.Product
|
||||
err error
|
||||
)
|
||||
if !colorVariant.IsSellable {
|
||||
|
||||
if err = importer.Baza.Model(&gm.ProductFlat{}).Where("sku=?", colorVariant.ProductNumber).Update("status", false).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
continue
|
||||
|
||||
} else {
|
||||
|
||||
if variant, err = importer.importVariant(colorVariant); err != nil {
|
||||
|
||||
if variant, importer.Error = importer.updateVariant(colorVariant); importer.Error != nil {
|
||||
return importer
|
||||
}
|
||||
|
||||
linkedProducts = append(linkedProducts, *variant)
|
||||
|
||||
} else {
|
||||
newProducts = append(newProducts, *variant)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(newProducts) > 0 {
|
||||
// relation
|
||||
|
||||
var relation []gm.ProductRelation
|
||||
for _, linkedProduct := range linkedProducts {
|
||||
|
||||
for _, newProd := range newProducts {
|
||||
relation = append(relation, gm.ProductRelation{ParentID: linkedProduct.ID, ChildID: newProd.ID})
|
||||
relation = append(relation, gm.ProductRelation{ParentID: newProd.ID, ChildID: linkedProduct.ID})
|
||||
}
|
||||
}
|
||||
|
||||
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return importer
|
||||
}
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
helper "sarga_updater/helpers"
|
||||
models "sarga_updater/trendyol_models"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LinkParser struct {
|
||||
link string
|
||||
}
|
||||
|
||||
func NewLinkParser(link string) LinkParser {
|
||||
return LinkParser{link: link}
|
||||
}
|
||||
|
||||
func (l LinkParser) ParseLink() (models.TrendyolProductDetailModel, error) {
|
||||
|
||||
helper.Info("link: ", l.link)
|
||||
|
||||
productId := ""
|
||||
|
||||
if isShortLink(l.link) {
|
||||
productId = getProductIdFromShortLink(l.link)
|
||||
} else {
|
||||
productId = getProductIdFromLink(l.link)
|
||||
}
|
||||
|
||||
if len(productId) == 0 {
|
||||
parseErr := errors.New("can not parse product id")
|
||||
helper.Error(parseErr)
|
||||
return models.TrendyolProductDetailModel{}, parseErr
|
||||
}
|
||||
|
||||
helper.Info("productId: ", productId)
|
||||
|
||||
return GetProductDetails(productId)
|
||||
}
|
||||
|
||||
func getProductIdFromShortLink(shortLink string) string {
|
||||
|
||||
var productId string
|
||||
|
||||
client, _ := helper.NewHttpClient()
|
||||
req, err := http.NewRequest("GET", shortLink, nil)
|
||||
req.Proto = "HTTP/2.0"
|
||||
if err != nil {
|
||||
helper.Error(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Connection", "close")
|
||||
req = req.WithContext(req.Context())
|
||||
req.Close = true
|
||||
client.CloseIdleConnections()
|
||||
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
helper.Error(err)
|
||||
return productId
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
url := response.Request.URL.Path
|
||||
|
||||
helper.Info("link url: ", url)
|
||||
|
||||
productId = getProductIdFromLink(url)
|
||||
|
||||
helper.Info("productId: ", productId)
|
||||
|
||||
return productId
|
||||
}
|
||||
|
||||
func getProductIdFromLink(link string) string {
|
||||
var productId string
|
||||
|
||||
if strings.Contains(link, "?") {
|
||||
link = strings.Split(link, "?")[0]
|
||||
}
|
||||
|
||||
strArr := strings.Split(link, "-")
|
||||
productId = strArr[len(strArr)-1]
|
||||
|
||||
return productId
|
||||
}
|
||||
|
||||
func isShortLink(link string) bool {
|
||||
return !strings.Contains(link, "trendyol.com")
|
||||
}
|
||||
|
||||
// GetProductDetails return JSON object. Merges color option
|
||||
func GetProductDetails(productId string) (models.TrendyolProductDetailModel, error) {
|
||||
|
||||
var response models.TrendyolProductDetailResponse
|
||||
productDetailModel := models.TrendyolProductDetailModel{}
|
||||
|
||||
// set linearVariants false to get variants
|
||||
url := "https://public.trendyol.com/discovery-web-productgw-service/api/productDetail/" + productId + "?storefrontId=1&culture=tr-TR&linearVariants=false"
|
||||
|
||||
body, err := helper.SendRequest("GET", url, nil, "")
|
||||
|
||||
if err != nil {
|
||||
return productDetailModel, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &response)
|
||||
|
||||
if err != nil {
|
||||
helper.Error(err)
|
||||
return productDetailModel, err
|
||||
}
|
||||
|
||||
productDetailModel = response.Result
|
||||
|
||||
return productDetailModel, nil
|
||||
}
|
||||
|
||||
// getProductDetailWithOptions returns JSON with variants
|
||||
func (l LinkParser) GetProductDetailWithOptions(productId, productGroupId int) (models.Product, error) {
|
||||
|
||||
primaryProductDetail, err := GetProductDetails(strconv.Itoa(productId))
|
||||
|
||||
if err != nil {
|
||||
return models.Product{}, err
|
||||
}
|
||||
|
||||
productDetailJSON := CreateJSONFromModel(primaryProductDetail)
|
||||
|
||||
if productDetailJSON != nil {
|
||||
colorVariants, err := GetProductColorVariants(productGroupId)
|
||||
|
||||
var colorVariantsJson []map[string]interface{}
|
||||
|
||||
if err != nil {
|
||||
return models.Product{}, err
|
||||
}
|
||||
|
||||
// get the color variant products
|
||||
for _, slicingAttribute := range colorVariants.Result.SlicingAttributes {
|
||||
for _, attribute := range slicingAttribute.Attributes {
|
||||
for _, content := range attribute.Contents {
|
||||
// Don't fetch primary product again.
|
||||
if content.ID != primaryProductDetail.ID {
|
||||
productVariantDetail, errGPD := GetProductDetails(strconv.Itoa(content.ID))
|
||||
if errGPD == nil {
|
||||
productVariantJSON := CreateJSONFromModel(productVariantDetail)
|
||||
if productVariantJSON != nil {
|
||||
colorVariantsJson = append(colorVariantsJson, productVariantJSON)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Color variant count
|
||||
if len(slicingAttribute.Attributes) > 0 {
|
||||
productDetailJSON["color_variant_count"] = len(slicingAttribute.Attributes)
|
||||
productDetailJSON["color_variants"] = colorVariantsJson
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsonString, _ := json.Marshal(productDetailJSON)
|
||||
|
||||
// convert json to struct
|
||||
converted := models.Product{}
|
||||
json.Unmarshal(jsonString, &converted)
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
// getProductColorVariants returns color options of product
|
||||
func GetProductColorVariants(productGroupId int) (models.TrendyolProductVariantsResponse, error) {
|
||||
|
||||
url := "https://public.trendyol.com/discovery-web-productgw-service/api/productGroup/" + strconv.Itoa(productGroupId) + "?storefrontId=1&culture=tr-TR"
|
||||
|
||||
var response models.TrendyolProductVariantsResponse
|
||||
|
||||
body, err := helper.SendRequest("GET", url, nil, "")
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &response)
|
||||
|
||||
if err != nil {
|
||||
helper.Error(err)
|
||||
return response, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// createJSONFromModel creates json from [trendyol.TrendyolProductDetailModel] and returns
|
||||
func CreateJSONFromModel(model models.TrendyolProductDetailModel) map[string]interface{} {
|
||||
|
||||
json := map[string]interface{}{}
|
||||
|
||||
// get weight from categories (Stored in helper array (RAM))
|
||||
weight := helper.GetCategoryWeight(model.Category.BeautifiedName)
|
||||
productGroupId := strconv.Itoa(model.ProductGroupID)
|
||||
|
||||
json["_id"] = productGroupId
|
||||
json["product_group_id"] = productGroupId
|
||||
json["vendor"] = "trendyol"
|
||||
json["sku"] = "p-" + strconv.Itoa(model.ID)
|
||||
json["product_number"] = strconv.Itoa(model.ID)
|
||||
json["product_code"] = model.ProductCode
|
||||
json["name"] = model.Name
|
||||
json["sellable"] = model.IsSellable
|
||||
json["favorite_count"] = model.FavoriteCount
|
||||
json["weight"] = weight
|
||||
json["name_with_product_code"] = model.NameWithProductCode
|
||||
json["url_key"] = "https://www.trendyol.com" + model.URL
|
||||
json["images"] = model.Images
|
||||
json["brand"] = model.Brand.Name
|
||||
json["cinsiyet"] = model.Gender.Name
|
||||
json["description"] = model.Description
|
||||
json["descriptions"] = model.ContentDescriptions
|
||||
json["short_description"] = model.Description
|
||||
// nested structure
|
||||
json["price"] = make(map[string]interface{})
|
||||
json["price"].(map[string]interface{})["originalPrice"] = model.Price.OriginalPrice
|
||||
json["price"].(map[string]interface{})["sellingPrice"] = model.Price.SellingPrice
|
||||
json["price"].(map[string]interface{})["discountedPrice"] = model.Price.DiscountedPrice
|
||||
|
||||
attrLen := len(model.Attributes)
|
||||
|
||||
if attrLen != 0 {
|
||||
for i := 0; i < attrLen; i++ {
|
||||
attribute := model.Attributes[i]
|
||||
if attribute.Key.Name == "Renk" {
|
||||
json["color"] = attribute.Value.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set categories with value 1
|
||||
json["categories"] = []int{1}
|
||||
|
||||
attributes := make([]map[string]string, 0)
|
||||
for _, attr := range model.Attributes {
|
||||
var re = regexp.MustCompile(`/[^A-Z0-9]/ig`)
|
||||
keyStr := re.ReplaceAllString(attr.Key.Name, `_`)
|
||||
key := strings.ToLower(keyStr)
|
||||
attribute := map[string]string{
|
||||
key: attr.Value.Name,
|
||||
}
|
||||
|
||||
attributes = append(attributes, attribute)
|
||||
}
|
||||
|
||||
json["attributes"] = attributes
|
||||
|
||||
var variants []models.Variant
|
||||
|
||||
// if show variants, then it is configurable product.
|
||||
if model.ShowVariants {
|
||||
|
||||
for i := 0; i < len(model.Variants); i++ {
|
||||
variant := model.Variants[i]
|
||||
|
||||
if variant.Sellable {
|
||||
stockType := reflect.TypeOf(variant.Stock)
|
||||
|
||||
if stockType == nil {
|
||||
variants = append(variants, variant)
|
||||
} else {
|
||||
// scrape is via link parse
|
||||
// we need to parse other variants
|
||||
|
||||
// convert stock to int
|
||||
stock, ok := variant.Stock.(float64)
|
||||
|
||||
if ok {
|
||||
if stock > 0 {
|
||||
variants = append(variants, variant)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(model.AllVariants); i++ {
|
||||
singleVariant := model.AllVariants[i]
|
||||
|
||||
// get the first variant for attribute info
|
||||
fv := variants[0]
|
||||
|
||||
variant := models.Variant{
|
||||
AttributeID: fv.AttributeID, //
|
||||
AttributeName: fv.AttributeName, // Sample: "Beden"
|
||||
AttributeType: fv.AttributeType, // Sample: "Size"
|
||||
AttributeValue: singleVariant.Value,
|
||||
Price: models.Price{
|
||||
ProfitMargin: 0,
|
||||
DiscountedPrice: struct {
|
||||
Text string "json:\"text\""
|
||||
Value float64 "json:\"value\""
|
||||
}{
|
||||
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||
Value: singleVariant.Price,
|
||||
},
|
||||
SellingPrice: struct {
|
||||
Text string "json:\"text\""
|
||||
Value float64 "json:\"value\""
|
||||
}{
|
||||
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||
Value: singleVariant.Price,
|
||||
},
|
||||
OriginalPrice: struct {
|
||||
Text string "json:\"text\""
|
||||
Value float64 "json:\"value\""
|
||||
}{
|
||||
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||
Value: singleVariant.Price,
|
||||
},
|
||||
Currency: singleVariant.Currency,
|
||||
},
|
||||
ItemNumber: singleVariant.ItemNumber,
|
||||
Sellable: singleVariant.InStock,
|
||||
}
|
||||
|
||||
exists := helper.IsVariantsAdded(variants, variant.ItemNumber)
|
||||
|
||||
if !exists {
|
||||
variants = append(variants, variant)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(model.AlternativeVariants); i++ {
|
||||
alternativeVariant := model.AlternativeVariants[i]
|
||||
|
||||
if len(variants) > 0 {
|
||||
|
||||
// get the first variant for attribute info
|
||||
fv := variants[0]
|
||||
|
||||
variant := models.Variant{
|
||||
AttributeID: fv.AttributeID, //
|
||||
AttributeName: fv.AttributeName, // Sample: "Beden"
|
||||
AttributeType: fv.AttributeType, // Sample: "Size"
|
||||
AttributeValue: alternativeVariant.AttributeValue,
|
||||
Price: alternativeVariant.Price,
|
||||
ItemNumber: alternativeVariant.ItemNumber,
|
||||
Sellable: alternativeVariant.Quantity > 0,
|
||||
}
|
||||
exists := helper.IsVariantsAdded(variants, variant.ItemNumber)
|
||||
|
||||
if !exists {
|
||||
variants = append(variants, variant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json["size_variants"] = variants
|
||||
}
|
||||
|
||||
if model.ShowVariants && len(variants) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
gm "sarga_updater/bagisto_models"
|
||||
models "sarga_updater/trendyol_models"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ProductRepo struct {
|
||||
Categories []gm.Category
|
||||
Brand gm.Brand
|
||||
Weight float64
|
||||
Description string
|
||||
Keywords string
|
||||
Data *models.Product
|
||||
Error error
|
||||
ColorOption gm.AttributeOption
|
||||
SexOption gm.AttributeOption
|
||||
ImageType string
|
||||
}
|
||||
|
||||
func InitProductRepo(data *models.Product, color, sex gm.AttributeOption) *ProductRepo {
|
||||
|
||||
weight, wError := strconv.ParseFloat(data.Weight, 64)
|
||||
|
||||
if wError != nil || weight == 0 {
|
||||
weight = 0.5
|
||||
}
|
||||
|
||||
var description string
|
||||
|
||||
for _, desc := range data.Descriptions {
|
||||
description += "<p>" + desc.Description + "</p>"
|
||||
}
|
||||
|
||||
instance := &ProductRepo{
|
||||
Weight: weight,
|
||||
Description: description,
|
||||
Data: data,
|
||||
ColorOption: color,
|
||||
SexOption: sex,
|
||||
ImageType: "cdn",
|
||||
}
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) SetCategories(categories []gm.Category) {
|
||||
pr.Categories = categories
|
||||
pr.Keywords = pr.Data.Brand
|
||||
|
||||
for _, cat := range categories {
|
||||
//log.Println(cat)
|
||||
if len(cat.Translations) > 0 && cat.Translations[0].MetaKeywords != "" {
|
||||
translation := cat.Translations[0]
|
||||
|
||||
pr.Keywords += "," + translation.MetaKeywords
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) HasSizeVariants() bool {
|
||||
return pr.Data.SizeVariants != nil && len(*pr.Data.SizeVariants) > 0
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) HasColorVariants() bool {
|
||||
return pr.Data.ColorVariants != nil && len(*pr.Data.ColorVariants) > 0
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) makeProduct(imp *Importer) gm.Product {
|
||||
var famID uint = 1
|
||||
|
||||
if len(imp.families) > 0 { //todo make real fam function
|
||||
famID = imp.families[0].ID
|
||||
}
|
||||
|
||||
product := gm.Product{
|
||||
Sku: pr.Data.ProductNumber,
|
||||
AttributeFamilyID: famID,
|
||||
Categories: pr.Categories,
|
||||
AttributeValues: pr.getProductAttributes(imp.AttributesMap, pr.Data),
|
||||
}
|
||||
|
||||
if pr.Data.Brand != "" {
|
||||
product.BrandID = pr.Brand.ID
|
||||
}
|
||||
|
||||
if pr.HasSizeVariants() {
|
||||
product.Type = "configurable"
|
||||
if (*pr.Data.SizeVariants)[0].AttributeName == "Beden" {
|
||||
product.SuperAttributes = []gm.Attribute{imp.AttributesMap["size"]}
|
||||
} else {
|
||||
product.SuperAttributes = []gm.Attribute{imp.AttributesMap["boyut"]}
|
||||
product.AttributeFamilyID = 4 // todo fix hardcode
|
||||
}
|
||||
|
||||
} else {
|
||||
product.Type = "simple"
|
||||
price := pr.Data.Price
|
||||
|
||||
if price.OriginalPrice.Value > price.DiscountedPrice.Value {
|
||||
product.AttributeValues = append(product.AttributeValues, []gm.ProductAttributeValue{
|
||||
{AttributeID: imp.AttributesMap["price"].ID, FloatValue: price.OriginalPrice.Value},
|
||||
{AttributeID: imp.AttributesMap["special_price"].ID, FloatValue: price.DiscountedPrice.Value},
|
||||
}...)
|
||||
} else {
|
||||
product.AttributeValues = append(product.AttributeValues, gm.ProductAttributeValue{
|
||||
AttributeID: imp.AttributesMap["price"].ID, FloatValue: price.DiscountedPrice.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, element := range pr.Data.Images {
|
||||
product.Images = append(product.Images, gm.ProductImage{Type: pr.ImageType, Path: element})
|
||||
}
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) makeVariant(parentID, famID uint, sku string) gm.Product {
|
||||
|
||||
product := gm.Product{
|
||||
Sku: sku,
|
||||
Type: "simple",
|
||||
AttributeFamilyID: famID,
|
||||
Categories: pr.Categories,
|
||||
ParentID: parentID,
|
||||
}
|
||||
|
||||
if pr.Data.Brand != "" {
|
||||
product.BrandID = pr.Brand.ID
|
||||
}
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) makeProductFlat(productId uint) gm.ProductFlat {
|
||||
|
||||
flat := gm.ProductFlat{
|
||||
ProductID: productId,
|
||||
Status: true,
|
||||
VisibleIndividually: true,
|
||||
Name: pr.Data.Name,
|
||||
Sku: pr.Data.ProductNumber,
|
||||
//ProductNumber: pr.Data.ProductNumber,
|
||||
Description: pr.Description,
|
||||
//UrlKey: pr.Data.ProductGroupID,
|
||||
Weight: pr.Weight,
|
||||
FavoritesCount: uint(pr.Data.FavoriteCount),
|
||||
MetaKeywords: pr.Keywords,
|
||||
MaxPrice: 0,
|
||||
MinPrice: 0,
|
||||
Price: 0,
|
||||
}
|
||||
|
||||
if pr.Data.Color != "" {
|
||||
flat.Color = int(pr.ColorOption.ID)
|
||||
flat.ColorLabel = pr.Data.Color
|
||||
}
|
||||
|
||||
if pr.Data.Brand != "" {
|
||||
flat.BrandID = pr.Brand.ID
|
||||
}
|
||||
|
||||
if pr.Data.Cinsiyet != "" {
|
||||
flat.Cinsiyet = int(pr.SexOption.ID)
|
||||
flat.CinsiyetLabel = pr.Data.Cinsiyet
|
||||
}
|
||||
|
||||
if !pr.HasSizeVariants() {
|
||||
flat.MinPrice = pr.Data.Price.DiscountedPrice.Value
|
||||
flat.MaxPrice = math.Max(pr.Data.Price.OriginalPrice.Value, pr.Data.Price.DiscountedPrice.Value)
|
||||
flat.Price = flat.MaxPrice
|
||||
|
||||
if flat.MinPrice < flat.MaxPrice {
|
||||
flat.SpecialPrice = flat.MinPrice
|
||||
}
|
||||
|
||||
}
|
||||
//else {//todo calculate price after variants inserted
|
||||
// for _, variant := range *pr.Data.SizeVariants {
|
||||
// price := variant.Price
|
||||
//
|
||||
// if flat.MinPrice == 0 || flat.MinPrice > price.DiscountedPrice.Value {
|
||||
// flat.MaxPrice = price.DiscountedPrice.Value
|
||||
// }
|
||||
//
|
||||
// maxPrice := math.Max(price.OriginalPrice.Value, price.DiscountedPrice.Value)
|
||||
// if flat.MaxPrice == 0 || flat.MaxPrice < maxPrice {
|
||||
// flat.MaxPrice = maxPrice
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
return flat
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) makeVariantFlat(variant models.Variant, SizID, parentID, productID uint) gm.ProductFlat {
|
||||
|
||||
maxPRice := math.Max(variant.Price.OriginalPrice.Value, variant.Price.DiscountedPrice.Value)
|
||||
sku := fmt.Sprintf("%s-%d", pr.Data.ProductNumber, variant.ItemNumber)
|
||||
|
||||
flat := gm.ProductFlat{
|
||||
ParentID: parentID,
|
||||
Status: true,
|
||||
Name: pr.Data.Name,
|
||||
Sku: sku,
|
||||
ProductNumber: fmt.Sprintf("%d", variant.ItemNumber),
|
||||
Weight: pr.Weight,
|
||||
FavoritesCount: uint(pr.Data.FavoriteCount),
|
||||
//SizeLabel: variant.AttributeValue,
|
||||
//Size: int(SizID),
|
||||
MaxPrice: maxPRice,
|
||||
MinPrice: variant.Price.DiscountedPrice.Value,
|
||||
Price: maxPRice,
|
||||
ProductID: productID,
|
||||
}
|
||||
|
||||
if variant.AttributeName == "Beden" {
|
||||
flat.Size = int(SizID)
|
||||
flat.SizeLabel = variant.AttributeValue
|
||||
} else {
|
||||
flat.Boyut = int(SizID)
|
||||
flat.BoyutLabel = variant.AttributeValue
|
||||
}
|
||||
|
||||
if flat.MaxPrice > flat.MinPrice {
|
||||
flat.SpecialPrice = flat.MinPrice
|
||||
}
|
||||
|
||||
if pr.Data.Color != "" {
|
||||
flat.Color = int(pr.ColorOption.ID)
|
||||
flat.ColorLabel = pr.Data.Color
|
||||
}
|
||||
|
||||
if pr.Data.Brand != "" {
|
||||
flat.BrandID = pr.Brand.ID
|
||||
}
|
||||
|
||||
if pr.Data.Cinsiyet != "" {
|
||||
flat.Cinsiyet = int(pr.SexOption.ID)
|
||||
flat.CinsiyetLabel = pr.Data.Cinsiyet
|
||||
}
|
||||
|
||||
return flat
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) getProductAttributes(AttributesMap map[string]gm.Attribute, product *models.Product) []gm.ProductAttributeValue {
|
||||
attributes := []gm.ProductAttributeValue{
|
||||
{AttributeID: AttributesMap["source"].ID, TextValue: product.URLKey},
|
||||
{AttributeID: AttributesMap["favoritesCount"].ID, IntegerValue: product.FavoriteCount},
|
||||
{AttributeID: AttributesMap["sku"].ID, TextValue: product.ProductNumber},
|
||||
{AttributeID: AttributesMap["name"].ID, TextValue: product.Name, Channel: "default", Locale: "tm"},
|
||||
{AttributeID: AttributesMap["weight"].ID, TextValue: product.Weight},
|
||||
{AttributeID: AttributesMap["status"].ID, BooleanValue: true},
|
||||
{AttributeID: AttributesMap["visible_individually"].ID, BooleanValue: true},
|
||||
{AttributeID: AttributesMap["description"].ID, TextValue: pr.Description, Channel: "default", Locale: "tm"},
|
||||
{AttributeID: AttributesMap["meta_keywords"].ID, TextValue: pr.Keywords, Channel: "default", Locale: "tm"},
|
||||
}
|
||||
|
||||
if product.Color != "" {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["color"].ID, IntegerValue: int(pr.ColorOption.ID)})
|
||||
}
|
||||
|
||||
if product.Cinsiyet != "" {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["cinsiyet"].ID, IntegerValue: int(pr.SexOption.ID)})
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
||||
func (pr *ProductRepo) getVariantAttributes(AttributesMap map[string]gm.Attribute, product *models.Variant, SizID uint) []gm.ProductAttributeValue {
|
||||
|
||||
price := math.Max(product.Price.OriginalPrice.Value, product.Price.DiscountedPrice.Value)
|
||||
sku := fmt.Sprintf("%s-%d", pr.Data.ProductNumber, product.ItemNumber)
|
||||
attributes := []gm.ProductAttributeValue{
|
||||
{AttributeID: AttributesMap["source"].ID, TextValue: pr.Data.URLKey},
|
||||
{AttributeID: AttributesMap["sku"].ID, TextValue: sku}, //todo unique
|
||||
{AttributeID: AttributesMap["product_number"].ID, TextValue: fmt.Sprintf("%d", product.ItemNumber)}, //todo unique
|
||||
{AttributeID: AttributesMap["name"].ID, TextValue: pr.Data.Name, Channel: "default", Locale: "tm"},
|
||||
{AttributeID: AttributesMap["weight"].ID, TextValue: pr.Data.Weight},
|
||||
{AttributeID: AttributesMap["status"].ID, BooleanValue: true},
|
||||
//{AttributeID: AttributesMap["size"].ID, IntegerValue: int(SizID)},
|
||||
{AttributeID: AttributesMap["price"].ID, FloatValue: price},
|
||||
}
|
||||
if product.AttributeName == "Beden" {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["size"].ID, IntegerValue: int(SizID)})
|
||||
} else {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["boyut"].ID, IntegerValue: int(SizID)})
|
||||
}
|
||||
|
||||
if pr.Data.Color != "" {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["color"].ID, IntegerValue: int(pr.ColorOption.ID)})
|
||||
}
|
||||
|
||||
if pr.Data.Cinsiyet != "" {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["cinsiyet"].ID, IntegerValue: int(pr.SexOption.ID)})
|
||||
}
|
||||
|
||||
if product.Price.OriginalPrice.Value > product.Price.DiscountedPrice.Value {
|
||||
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["special_price"].ID, FloatValue: product.Price.DiscountedPrice.Value})
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package trendyol_models
|
||||
|
||||
type Category struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
TrendyolURL string `json:"trendyol_url"`
|
||||
LcwURL string `json:"lcw_url"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
ImageURL interface{} `json:"image_url"`
|
||||
CategoryIconPath interface{} `json:"category_icon_path"`
|
||||
ProductLimit int `json:"product_limit"`
|
||||
Children []Category
|
||||
}
|
||||
|
||||
type BagistoResponse struct {
|
||||
Data []Category
|
||||
}
|
||||
type CouchCategory struct {
|
||||
Rev string `json:"_rev"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Order string `json:"order"`
|
||||
ParentID string `json:"parent_id"`
|
||||
SargaID string `json:"sarga_id"`
|
||||
Slug string `json:"slug"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Weight string `json:"weight"`
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
package trendyol_models
|
||||
|
||||
type Product struct {
|
||||
Attributes []map[string]string `json:"attributes"`
|
||||
Brand string `json:"brand"`
|
||||
Categories []int `json:"categories"`
|
||||
Cinsiyet string `json:"cinsiyet"`
|
||||
Color string `json:"color"`
|
||||
ColorVariantCount int `json:"color_variant_count"`
|
||||
ColorVariants *[]Product `json:"color_variants"`
|
||||
Description string `json:"description"`
|
||||
IsSellable bool `json:"sellable"`
|
||||
FavoriteCount int `json:"favorite_count"`
|
||||
Descriptions []struct {
|
||||
Description string `json:"description"`
|
||||
Bold bool `json:"bold"`
|
||||
} `json:"descriptions"`
|
||||
Images []string `json:"images"`
|
||||
Name string `json:"name"`
|
||||
NameWithProductCode string `json:"name_with_product_code"`
|
||||
Price Price `json:"price"`
|
||||
ProductCode string `json:"product_code"`
|
||||
ProductGroupID string `json:"product_group_id"`
|
||||
ProductNumber string `json:"product_number"`
|
||||
ShortDescription string `json:"short_description"`
|
||||
SizeVariants *[]Variant `json:"size_variants"`
|
||||
Sku string `json:"sku"`
|
||||
Stock interface{} `json:"stock"`
|
||||
URLKey string `json:"url_key"`
|
||||
Vendor string `json:"vendor"`
|
||||
Weight string `json:"weight"`
|
||||
}
|
||||
|
||||
type Price struct {
|
||||
ProfitMargin int `json:"profitMargin"`
|
||||
DiscountedPrice PriceValue `json:"discountedPrice"`
|
||||
SellingPrice PriceValue `json:"sellingPrice"`
|
||||
OriginalPrice PriceValue `json:"originalPrice"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
type PriceValue struct {
|
||||
Text string `json:"text"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type Variant struct {
|
||||
AttributeID int `json:"attributeId"`
|
||||
AttributeName string `json:"attributeName"`
|
||||
AttributeType string `json:"attributeType"`
|
||||
AttributeValue string `json:"attributeValue"`
|
||||
Stamps []interface{} `json:"stamps"`
|
||||
Price Price `json:"price"`
|
||||
FulfilmentType string `json:"fulfilmentType"`
|
||||
AttributeBeautifiedValue string `json:"attributeBeautifiedValue"`
|
||||
IsWinner bool `json:"isWinner"`
|
||||
ListingID string `json:"listingId"`
|
||||
Stock interface{} `json:"stock"`
|
||||
Sellable bool `json:"sellable"`
|
||||
AvailableForClaim bool `json:"availableForClaim"`
|
||||
Barcode string `json:"barcode"`
|
||||
ItemNumber int `json:"itemNumber"`
|
||||
DiscountedPriceInfo string `json:"discountedPriceInfo"`
|
||||
HasCollectable bool `json:"hasCollectable"`
|
||||
RushDeliveryMerchantListingExist bool `json:"rushDeliveryMerchantListingExist"`
|
||||
LowerPriceMerchantListingExist bool `json:"lowerPriceMerchantListingExist"`
|
||||
}
|
||||
type Row struct {
|
||||
Key string `json:"key"`
|
||||
Value map[string]interface{} `json:"value"`
|
||||
Doc Product
|
||||
}
|
||||
type BagistoModelResponse struct {
|
||||
Rows []Row `json:"rows"`
|
||||
}
|
||||
|
||||
type TrendyolProductDetailResponse struct {
|
||||
IsSuccess bool `json:"isSuccess"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Error interface{} `json:"error"`
|
||||
Result TrendyolProductDetailModel `json:"result"`
|
||||
Headers struct {
|
||||
Tysidecarcachable string `json:"tysidecarcachable"`
|
||||
} `json:"headers"`
|
||||
}
|
||||
|
||||
type TrendyolProductDetailModel struct {
|
||||
AlternativeVariants []AlternativeVariant `json:"alternativeVariants"`
|
||||
Attributes []struct {
|
||||
Key struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
} `json:"key"`
|
||||
Value struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
} `json:"value"`
|
||||
Starred bool `json:"starred"`
|
||||
} `json:"attributes"`
|
||||
Variants []Variant `json:"variants"`
|
||||
OtherMerchants []interface{} `json:"otherMerchants"`
|
||||
Campaign struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
StartDate string `json:"startDate"`
|
||||
EndDate string `json:"endDate"`
|
||||
IsMultipleSupplied bool `json:"isMultipleSupplied"`
|
||||
StockTypeID int `json:"stockTypeId"`
|
||||
URL string `json:"url"`
|
||||
ShowTimer bool `json:"showTimer"`
|
||||
} `json:"campaign"`
|
||||
Category struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Hierarchy string `json:"hierarchy"`
|
||||
Refundable bool `json:"refundable"`
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
IsVASEnabled bool `json:"isVASEnabled"`
|
||||
} `json:"category"`
|
||||
Brand struct {
|
||||
IsVirtual bool `json:"isVirtual"`
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
} `json:"brand"`
|
||||
Color string `json:"color"`
|
||||
MetaBrand struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
IsVirtual bool `json:"isVirtual"`
|
||||
Path string `json:"path"`
|
||||
} `json:"metaBrand"`
|
||||
ShowVariants bool `json:"showVariants"`
|
||||
ShowSexualContent bool `json:"showSexualContent"`
|
||||
BrandCategoryBanners []interface{} `json:"brandCategoryBanners"`
|
||||
AllVariants []struct {
|
||||
ItemNumber int `json:"itemNumber"`
|
||||
Value string `json:"value"`
|
||||
InStock bool `json:"inStock"`
|
||||
Currency string `json:"currency"`
|
||||
Barcode string `json:"barcode"`
|
||||
Price float64 `json:"price"`
|
||||
} `json:"allVariants"`
|
||||
OtherMerchantVariants []interface{} `json:"otherMerchantVariants"`
|
||||
InstallmentBanner interface{} `json:"installmentBanner"`
|
||||
IsVasEnabled bool `json:"isVasEnabled"`
|
||||
OriginalCategory struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Hierarchy string `json:"hierarchy"`
|
||||
Refundable bool `json:"refundable"`
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
IsVASEnabled bool `json:"isVASEnabled"`
|
||||
} `json:"originalCategory"`
|
||||
Landings []interface{} `json:"landings"`
|
||||
ID int `json:"id"`
|
||||
ProductCode string `json:"productCode"`
|
||||
Name string `json:"name"`
|
||||
NameWithProductCode string `json:"nameWithProductCode"`
|
||||
Description string `json:"description"`
|
||||
ContentDescriptions []struct {
|
||||
Description string `json:"description"`
|
||||
Bold bool `json:"bold"`
|
||||
} `json:"contentDescriptions"`
|
||||
ProductGroupID int `json:"productGroupId"`
|
||||
Tax int `json:"tax"`
|
||||
BusinessUnit string `json:"businessUnit"`
|
||||
MaxInstallment int `json:"maxInstallment"`
|
||||
Gender struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
} `json:"gender"`
|
||||
URL string `json:"url"`
|
||||
Images []string `json:"images"`
|
||||
IsSellable bool `json:"isSellable"`
|
||||
IsBasketDiscount bool `json:"isBasketDiscount"`
|
||||
HasStock bool `json:"hasStock"`
|
||||
Price Price `json:"price"`
|
||||
IsFreeCargo bool `json:"isFreeCargo"`
|
||||
Promotions []struct {
|
||||
PromotionRemainingTime string `json:"promotionRemainingTime"`
|
||||
Type int `json:"type"`
|
||||
Text string `json:"text"`
|
||||
ID int `json:"id"`
|
||||
Link string `json:"link"`
|
||||
} `json:"promotions"`
|
||||
Merchant struct {
|
||||
IsSearchableMerchant bool `json:"isSearchableMerchant"`
|
||||
Stickers []interface{} `json:"stickers"`
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
OfficialName string `json:"officialName"`
|
||||
CityName string `json:"cityName"`
|
||||
TaxNumber string `json:"taxNumber"`
|
||||
SellerScore float64 `json:"sellerScore"`
|
||||
SellerScoreColor string `json:"sellerScoreColor"`
|
||||
DeliveryProviderName string `json:"deliveryProviderName"`
|
||||
SellerLink string `json:"sellerLink"`
|
||||
} `json:"merchant"`
|
||||
DeliveryInformation struct {
|
||||
IsRushDelivery bool `json:"isRushDelivery"`
|
||||
DeliveryDate string `json:"deliveryDate"`
|
||||
} `json:"deliveryInformation"`
|
||||
CargoRemainingDays int `json:"cargoRemainingDays"`
|
||||
IsMarketplace bool `json:"isMarketplace"`
|
||||
ProductStamps []struct {
|
||||
Type string `json:"type"`
|
||||
ImageURL string `json:"imageUrl"`
|
||||
Position string `json:"position"`
|
||||
AspectRatio float64 `json:"aspectRatio"`
|
||||
Priority int `json:"priority"`
|
||||
PriceTagStamp bool `json:"priceTagStamp,omitempty"`
|
||||
} `json:"productStamps"`
|
||||
HasHTMLContent bool `json:"hasHtmlContent"`
|
||||
FavoriteCount int `json:"favoriteCount"`
|
||||
UxLayout string `json:"uxLayout"`
|
||||
IsDigitalGood bool `json:"isDigitalGood"`
|
||||
IsRunningOut bool `json:"isRunningOut"`
|
||||
ScheduledDelivery bool `json:"scheduledDelivery"`
|
||||
RatingScore struct {
|
||||
AverageRating float64 `json:"averageRating"`
|
||||
TotalRatingCount int `json:"totalRatingCount"`
|
||||
TotalCommentCount int `json:"totalCommentCount"`
|
||||
} `json:"ratingScore"`
|
||||
ShowStarredAttributes bool `json:"showStarredAttributes"`
|
||||
ReviewsURL string `json:"reviewsUrl"`
|
||||
QuestionsURL string `json:"questionsUrl"`
|
||||
SellerQuestionEnabled bool `json:"sellerQuestionEnabled"`
|
||||
SizeExpectationAvailable bool `json:"sizeExpectationAvailable"`
|
||||
CrossPromotionAward struct {
|
||||
AwardType interface{} `json:"awardType"`
|
||||
AwardValue interface{} `json:"awardValue"`
|
||||
ContentID int `json:"contentId"`
|
||||
MerchantID int `json:"merchantId"`
|
||||
} `json:"crossPromotionAward"`
|
||||
RushDeliveryMerchantListingExist bool `json:"rushDeliveryMerchantListingExist"`
|
||||
LowerPriceMerchantListingExist bool `json:"lowerPriceMerchantListingExist"`
|
||||
ShowValidFlashSales bool `json:"showValidFlashSales"`
|
||||
ShowExpiredFlashSales bool `json:"showExpiredFlashSales"`
|
||||
WalletRebate struct {
|
||||
MinPrice int `json:"minPrice"`
|
||||
MaxPrice int `json:"maxPrice"`
|
||||
RebateRatio float64 `json:"rebateRatio"`
|
||||
} `json:"walletRebate"`
|
||||
IsArtWork bool `json:"isArtWork"`
|
||||
}
|
||||
|
||||
type TrendyolProductVariantsResponse struct {
|
||||
IsSuccess bool `json:"isSuccess"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Error interface{} `json:"error"`
|
||||
Result struct {
|
||||
SlicingAttributes []struct {
|
||||
Brand struct {
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsVirtual bool `json:"isVirtual"`
|
||||
Path string `json:"path"`
|
||||
} `json:"brand"`
|
||||
Attributes []struct {
|
||||
Contents []struct {
|
||||
URL string `json:"url"`
|
||||
ID int `json:"id"`
|
||||
ImageURL string `json:"imageUrl"`
|
||||
Name string `json:"name"`
|
||||
Price struct {
|
||||
DiscountedPrice struct {
|
||||
Text string `json:"text"`
|
||||
Value float64 `json:"value"`
|
||||
} `json:"discountedPrice"`
|
||||
OriginalPrice struct {
|
||||
Text string `json:"text"`
|
||||
Value float64 `json:"value"`
|
||||
} `json:"originalPrice"`
|
||||
SellingPrice struct {
|
||||
Text string `json:"text"`
|
||||
Value float64 `json:"value"`
|
||||
} `json:"sellingPrice"`
|
||||
} `json:"price"`
|
||||
} `json:"contents"`
|
||||
Name string `json:"name"`
|
||||
BeautifiedName string `json:"beautifiedName"`
|
||||
} `json:"attributes"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Order int `json:"order"`
|
||||
DisplayType int `json:"displayType"`
|
||||
} `json:"slicingAttributes"`
|
||||
} `json:"result"`
|
||||
Headers struct {
|
||||
Tysidecarcachable string `json:"tysidecarcachable"`
|
||||
} `json:"headers"`
|
||||
}
|
||||
type AlternativeVariant struct {
|
||||
AttributeValue string `json:"attributeValue"`
|
||||
AttributeBeautifiedValue string `json:"attributeBeautifiedValue"`
|
||||
CampaignID int `json:"campaignId"`
|
||||
MerchantID int `json:"merchantId"`
|
||||
URLQuery string `json:"urlQuery"`
|
||||
ListingID string `json:"listingId"`
|
||||
ItemNumber int `json:"itemNumber"`
|
||||
Barcode string `json:"barcode"`
|
||||
Stock interface{} `json:"stock"`
|
||||
Quantity int `json:"quantity"`
|
||||
Price Price `json:"price"`
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package trendyol_models
|
||||
|
||||
type SizeVariants struct {
|
||||
Price float64 `json:"price"`
|
||||
Size string `json:"size"`
|
||||
Stock int `json:"stock"`
|
||||
}
|
||||
Loading…
Reference in New Issue