You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
9.0 KiB
379 lines
9.0 KiB
package jcook
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"recook/configs"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
type client struct {
|
|
}
|
|
|
|
var c *client
|
|
|
|
var once sync.Once
|
|
|
|
func GetClient() *client {
|
|
once.Do(func() {
|
|
c = &client{}
|
|
})
|
|
return c
|
|
}
|
|
|
|
var httpClient = &http.Client{
|
|
Timeout: time.Second * 5,
|
|
}
|
|
|
|
func Md5OrderlyWithSecret(secret string, data map[string]string) string {
|
|
h := md5.New()
|
|
|
|
var order []string
|
|
|
|
for key, _ := range data {
|
|
order = append(order, key)
|
|
}
|
|
|
|
sort.Strings(order)
|
|
|
|
h.Write([]byte(secret))
|
|
|
|
for _, value := range order {
|
|
h.Write([]byte(fmt.Sprintf("%s%s", value, data[value])))
|
|
}
|
|
|
|
h.Write([]byte(secret))
|
|
|
|
return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
|
|
}
|
|
|
|
type Requester interface {
|
|
GetApiName() string
|
|
}
|
|
|
|
type Response struct {
|
|
Code uint `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Data interface{} `json:"data"`
|
|
ErrMsg string `json:"err_msg"`
|
|
}
|
|
|
|
type SkuListReq struct {
|
|
Page uint `json:"page"`
|
|
PageSize uint `json:"page_size"`
|
|
Brand string `json:"brand"`
|
|
}
|
|
|
|
func (o SkuListReq) GetApiName() string {
|
|
return "sku/list"
|
|
}
|
|
|
|
type SkuInfo struct {
|
|
SkuID uint `json:"sku_id"`
|
|
SkuName string `json:"sku_name"`
|
|
BrandName string `json:"brand_name"`
|
|
CategoryFirstName string `json:"category_first_name"`
|
|
CategorySecondName string `json:"category_second_name"`
|
|
CategoryThirdName string `json:"category_third_name"`
|
|
MainPhoto string `json:"main_photo"`
|
|
Status bool `json:"status"`
|
|
SupplyPrice decimal.Decimal `json:"supply_price"`
|
|
GuidePrice decimal.Decimal `json:"guide_price"`
|
|
Unit string `json:"unit"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
Kind uint `json:"kind"`
|
|
UpcCode string `json:"upc_code"`
|
|
Yn bool `json:"yn"`
|
|
}
|
|
|
|
type SkuListResp struct {
|
|
Total uint `json:"total"`
|
|
Entries []SkuInfo `json:"entries"`
|
|
}
|
|
|
|
func (o *client) get(r Requester) (*http.Response, error) {
|
|
p := url.Values{}
|
|
ur, _ := url.Parse(configs.ConfigJCookUrl + r.GetApiName())
|
|
params, _ := json.Marshal(r)
|
|
|
|
data := map[string]string{
|
|
"timestamp": time.Now().Format("2006-01-02 15:04:05"),
|
|
"app_key": configs.ConfigJCookAppKey,
|
|
"channel_id": strconv.Itoa(configs.ConfigJCookAppChannelID),
|
|
"params": string(params),
|
|
}
|
|
|
|
sign := Md5OrderlyWithSecret(configs.ConfigJCookAppSecret, data)
|
|
|
|
data["sign"] = sign
|
|
|
|
for k, v := range data {
|
|
p.Set(k, v)
|
|
}
|
|
ur.RawQuery = p.Encode()
|
|
|
|
req, _ := http.NewRequest("GET", ur.String(), nil)
|
|
|
|
return httpClient.Do(req)
|
|
}
|
|
|
|
func (o *client) parse(resp *http.Response) (res Response, err error) {
|
|
data, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return
|
|
}
|
|
d := json.NewDecoder(bytes.NewReader(data))
|
|
d.UseNumber()
|
|
err = d.Decode(&res)
|
|
return
|
|
}
|
|
|
|
func (o *client) mapToStruct(input interface{}, output interface{}) (err error) {
|
|
if reflect.TypeOf(output).Kind() != reflect.Ptr {
|
|
err = errors.New("output must a ptr")
|
|
return
|
|
}
|
|
t, err := json.Marshal(input)
|
|
err = json.Unmarshal(t, output)
|
|
return
|
|
}
|
|
|
|
func (o *client) exec(req Requester) (data Response, err error) {
|
|
resp, err := o.get(req)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
data, err = o.parse(resp)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return data, err
|
|
}
|
|
|
|
func (o *client) Exec(req Requester, obj interface{}) error {
|
|
data, err := o.exec(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if data.Code != 0 {
|
|
return errors.New(data.ErrMsg)
|
|
}
|
|
err = o.mapToStruct(data.Data, obj)
|
|
return err
|
|
}
|
|
|
|
type SkuDetailReq struct {
|
|
SkuIDSet []uint `json:"sku_id_set"`
|
|
}
|
|
|
|
type SkuImage struct {
|
|
Url string `json:"url"`
|
|
IsPrimer bool `json:"is_primer"`
|
|
OrderSort uint `json:"order_sort"`
|
|
}
|
|
type SkuDetailBase struct {
|
|
SkuID uint `json:"sku_id"`
|
|
SkuName string `json:"sku_name"`
|
|
ShopName string `json:"shop_name"`
|
|
VendorName string `json:"vendor_name"`
|
|
BrandName string `json:"brand_name"`
|
|
CategoryFirstName string `json:"category_first_name"`
|
|
CategorySecondName string `json:"category_second_name"`
|
|
CategoryThirdName string `json:"category_third_name"`
|
|
MainPhoto string `json:"main_photo"`
|
|
Status bool `json:"status"`
|
|
SupplyPrice decimal.Decimal `json:"supply_price"`
|
|
GuidePrice decimal.Decimal `json:"guide_price"`
|
|
Model string `json:"model"`
|
|
Tax string `json:"tax"`
|
|
Unit string `json:"unit"`
|
|
Size string `json:"size"`
|
|
Color string `json:"color"`
|
|
Warranty string `json:"warranty"`
|
|
ShelfLife string `json:"shelf_life"`
|
|
Delivery string `json:"delivery"`
|
|
PlaceOfProduction string `json:"place_of_production"`
|
|
UpcCode string `json:"upc_code"`
|
|
Length float64 `json:"length"`
|
|
Width float64 `json:"width"`
|
|
Height float64 `json:"height"`
|
|
Weight float64 `json:"weight"`
|
|
Yn bool `json:"yn"`
|
|
Kind uint `json:"kind"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
type Spec struct {
|
|
GroupName string `json:"group_name"`
|
|
Attribute []SkuAttribute `json:"attribute"`
|
|
}
|
|
|
|
type SkuAttribute struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
type SkuDetailResp struct {
|
|
Images []SkuImage `json:"images"`
|
|
SkuDetailBase SkuDetailBase `json:"sku_detail_base"`
|
|
BigInfo BigInfo `json:"big_info"`
|
|
Specification []Spec `json:"specification"`
|
|
ExtAttr []SkuAttribute `json:"ext_attr"`
|
|
}
|
|
|
|
type BigInfo struct {
|
|
PcWDis string `json:"pc_wdis"`
|
|
PcJsContent string `json:"pc_js_content"`
|
|
PcCssContent string `json:"pc_css_content"`
|
|
PcHtmlContent string `json:"pc_html_content"`
|
|
}
|
|
|
|
func (o SkuDetailReq) GetApiName() string {
|
|
return "sku/detail"
|
|
}
|
|
|
|
type SkuBrotherReq struct {
|
|
SkuIDSet []uint `json:"sku_id_set"`
|
|
}
|
|
|
|
type SkuBrotherResp struct {
|
|
BrotherSkuIDs []uint `json:"brother_sku_ids"`
|
|
}
|
|
|
|
func (o SkuBrotherReq) GetApiName() string {
|
|
return "sku/brother"
|
|
}
|
|
|
|
type SkuPriceReq struct {
|
|
SkuIDSet []uint `json:"sku_id_set"`
|
|
}
|
|
|
|
type SkuPriceResp struct {
|
|
SupplyPrice decimal.Decimal `json:"supply_price"`
|
|
GuidePrice decimal.Decimal `json:"guide_price"`
|
|
SkuID uint `json:"sku_id"`
|
|
}
|
|
|
|
func (o SkuPriceReq) GetApiName() string {
|
|
return "sku/price"
|
|
}
|
|
|
|
type SkuStockReq struct {
|
|
Address string `json:"address"`
|
|
SkuList []SkuQuantity `json:"sku_list"`
|
|
}
|
|
|
|
type SkuQuantity struct {
|
|
SkuID uint `json:"sku_id"`
|
|
Quantity uint `json:"quantity"`
|
|
}
|
|
|
|
type SkuStockResp struct {
|
|
SkuID uint `json:"sku_id"`
|
|
Quantity uint `json:"quantity"`
|
|
StockState uint `json:"stock_state"`
|
|
}
|
|
|
|
func (o SkuStockReq) GetApiName() string {
|
|
return "stock/detail"
|
|
}
|
|
|
|
type LogisticsFeeReq struct {
|
|
Address string `json:"address"`
|
|
SkuList []LogisticsInfo `json:"sku_list"`
|
|
OrderFee decimal.Decimal `json:"order_fee"`
|
|
}
|
|
|
|
type LogisticsInfo struct {
|
|
SkuID uint `json:"sku_id"`
|
|
Quantity uint `json:"quantity"`
|
|
SkuPrice decimal.Decimal `json:"sku_price"`
|
|
}
|
|
|
|
type LogisticsFeeResp struct {
|
|
Fee decimal.Decimal
|
|
}
|
|
|
|
func (o LogisticsFeeReq) GetApiName() string {
|
|
return "logistics/fee"
|
|
}
|
|
|
|
type OrderSubmitReq struct {
|
|
Address string `json:"address"`
|
|
SkuList []LogisticsInfo `json:"sku_list"`
|
|
OrderFee decimal.Decimal `json:"order_fee"`
|
|
Receiver OrderReceiver `json:"receiver"`
|
|
FreightFee decimal.Decimal `json:"freight_fee"`
|
|
UserIp string `json:"user_ip"`
|
|
ChannelOrderID string `json:"channel_order_id"`
|
|
}
|
|
|
|
type OrderReceiver struct {
|
|
Name string `json:"name"`
|
|
Mobile string `json:"mobile"`
|
|
}
|
|
|
|
type OrderSubmitResp struct {
|
|
OrderID uint `json:"order_id"`
|
|
ChannelOrderID string `json:"channel_order_id"`
|
|
ChannelID uint `json:"channel_id"`
|
|
}
|
|
|
|
func (o OrderSubmitReq) GetApiName() string {
|
|
return "order/submit"
|
|
}
|
|
|
|
type OrderCancelReq struct {
|
|
OrderID uint `json:"order_id"`
|
|
CancelReasonCode uint `json:"cancel_reason_code"`
|
|
}
|
|
|
|
type OrderCancelResp struct {
|
|
CancelStatus uint `json:"cancel_status"`
|
|
}
|
|
|
|
func (o OrderCancelReq) GetApiName() string {
|
|
return "order/cancel"
|
|
}
|
|
|
|
type OrderPushReq struct {
|
|
OrderID uint `json:"order_id"`
|
|
}
|
|
|
|
type OrderPushResp struct {
|
|
}
|
|
|
|
func (o OrderPushReq) GetApiName() string {
|
|
return "order/push"
|
|
}
|
|
|
|
type OrderFinishReq struct {
|
|
OrderID uint `json:"order_id"`
|
|
ClientIp string `json:"client_ip"`
|
|
ClientPort string `json:"client_port"`
|
|
}
|
|
|
|
type OrderFinishResp struct {
|
|
}
|
|
|
|
func (o OrderFinishReq) GetApiName() string {
|
|
return "order/confirm"
|
|
}
|