daShangDao_kfzgw-info/pdd/pdd.go
2026-01-13 16:21:38 +08:00

1030 lines
39 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
/*
#include <stdlib.h>
*/
import "C"
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/parnurzeal/gorequest"
"io"
"mime/multipart"
"os"
"path/filepath"
"sort"
"strings"
"time"
"unsafe"
)
// ErrorResponse 错误响应结构
type ErrorResponse struct {
ErrorMsg string `json:"error_msg"` // 错误信息
SubMsg string `json:"sub_msg"` // 子错误信息
SubCode interface{} `json:"sub_code"` // 使用json.RawMessage处理null和不同类型
ErrorCode int `json:"error_code"` // 错误代码
RequestID string `json:"request_id"` // 请求ID
}
// ErrorWrapper 最外层错误响应包装
type ErrorWrapper struct {
ErrorResponse ErrorResponse `json:"error_response"` // 错误响应
}
/*
* 生成签名
* param params[map[string]interface{}] 生成签名需要的map
* param clientSecret[string] 客户端密钥
* return 签名
*/
func generateSign(params map[string]interface{}, clientSecret string) string {
// 获取所有键并排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
// 按键名升序排序
sort.Strings(keys)
// 拼接签名字符串
var signStr strings.Builder
for _, k := range keys {
// 跳过 sign 参数(签名本身不参与签名)
if strings.ToLower(k) == "sign" {
continue
}
value := ""
if params[k] != nil {
value = fmt.Sprintf("%v", params[k]) // 将任意值转为字符串
}
// 按照文档格式:参数名+参数值
signStr.WriteString(k + value)
}
signString := signStr.String()
// 拼接client_secret: client_secret + 参数串 + client_secret
data := clientSecret + signString + clientSecret
// MD5加密并转大写
hasher := md5.New()
hasher.Write([]byte(data))
return strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
}
/*
* 类目预测
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param outerCatId[string] 外部类目ID
* param outerCatName[string] 外部类目名称
* param outerGoodsName[string] 外部商品名称
* return 类目预测响应结构体,错误信息
*/
func pddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
outerCatId, outerCatName, outerGoodsName string) (string, error) {
// API地址
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
// 当前时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名参数
params := map[string]interface{}{
"type": "pdd.goods.outer.cat.mapping.get", // API类型
"data_type": "JSON", // 数据类型
"client_id": clientId, // 客户端ID
"access_token": accessToken, // 访问令牌
"outer_cat_id": outerCatId, // 外部类目ID
"outer_cat_name": outerCatName, // 外部类目名称
"outer_goods_name": outerGoodsName, // 外部商品名称
"timestamp": timestamp, // 时间戳
}
// 生成签名
sign := generateSign(params, clientSecret)
// 请求体
formData := map[string]interface{}{
"type": "pdd.goods.outer.cat.mapping.get",
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"outer_cat_id": outerCatId,
"outer_cat_name": outerCatName,
"outer_goods_name": outerGoodsName,
"timestamp": timestamp,
"sign": sign, // 签名
}
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second). // 30秒超时
Send(formData). // 发送表单数据
End() // 结束请求
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
/*
* 快递公司查看
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* return 快递公司查看响应结构体,错误信息
*/
func pddLogisticsCompaniesGet(clientId, clientSecret string) (string, error) {
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名参数
params := map[string]interface{}{
"type": "pdd.logistics.companies.get", // API类型获取物流公司
"data_type": "JSON",
"client_id": clientId,
"timestamp": timestamp,
}
sign := generateSign(params, clientSecret)
// 请求体
formData := map[string]interface{}{
"type": "pdd.logistics.companies.get",
"data_type": "JSON",
"client_id": clientId,
"timestamp": timestamp,
"sign": sign,
}
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second).
Send(formData).
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理:检查是否有错误响应
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
/*
* erp打单信息同步
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param logisticsId[string] 物流公司编码
* param orderSn[string] 订单号
* param orderState[string] 订单状态1-已打单
* param waybillNo[string] 运单号
* return 快递公司查看响应结构体,错误信息
*/
func pddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
orderSn, orderState, waybillNo string) (string, error) {
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名
params := map[string]interface{}{
"type": "pdd.erp.order.sync", // API类型ERP订单同步
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"logistics_id": logisticsId, // 物流公司ID
"order_sn": orderSn, // 订单号
"order_state": orderState, // 订单状态
"waybill_no": waybillNo, // 运单号
"timestamp": timestamp,
}
sign := generateSign(params, clientSecret)
// 请求体
formData := map[string]interface{}{
"type": "pdd.erp.order.sync",
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"logistics_id": logisticsId,
"order_sn": orderSn,
"order_state": orderState,
"waybill_no": waybillNo,
"timestamp": timestamp,
"sign": sign,
}
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second).
Send(formData).
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
/*
* 订单发货通知接口
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param logisticsOnlineSendJson[string] 订单发货通知json字符串
* return 订单发货通知接口响应结构体,错误信息
*/
func pddLogisticsOnlineSend(clientId, clientSecret, accessToken string, logisticsOnlineSendJson string) (string, error) {
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名
params := map[string]interface{}{
"type": "pdd.logistics.online.send",
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"timestamp": timestamp,
}
// 将JSON参数合并到params中
toParams, err := addStructToParams(logisticsOnlineSendJson, params)
if err != nil {
return "", err
}
sign := generateSign(toParams, clientSecret)
if sign == "" {
return "", fmt.Errorf("生成 sign 签名错误!")
}
params["sign"] = sign
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second).
Send(params).
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
// LogisticsCompany 物流公司信息结构体
type LogisticsCompany struct {
Available int `json:"available"` // 是否可用
Code string `json:"code"` // 物流公司代码
ID int `json:"id"` // 物流公司ID
LogisticsCompany string `json:"logistics_company"` // 物流公司名称
}
// LogisticsCompaniesGetResponse 物流公司列表响应结构体
type LogisticsCompaniesGetResponse struct {
LogisticsCompanies []LogisticsCompany `json:"logistics_companies"` // 物流公司列表
}
// LogisticsResponse 最外层响应结构体
type LogisticsResponse struct {
LogisticsCompaniesGetResponse LogisticsCompaniesGetResponse `json:"logistics_companies_get_response"` // 物流公司响应
}
/*
* 拼多多订单同步(组合接口:先查物流公司,再同步订单)
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param logisticsCompany[string] 外部类目ID
* param logisticsOnlineSendJson[string] 拼多多订单同步json字符串
* return 类目预测响应结构体,错误信息
*/
func pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson string) (string, error) {
var logisticsOnlineSendData map[string]interface{}
err := json.Unmarshal([]byte(logisticsOnlineSendJson), &logisticsOnlineSendData)
if err != nil {
return "", fmt.Errorf("logisticsOnlineSendJson JSON解析失败: %v", err)
}
// 1. 获取物流公司列表
logisticsCompaniesGet, err := pddLogisticsCompaniesGet(clientId, clientSecret)
if err != nil {
return "", err
}
// 2. 解析物流公司响应
var response LogisticsResponse
if err := json.Unmarshal([]byte(logisticsCompaniesGet), &response); err != nil {
return "", fmt.Errorf("解析JSON失败: %v", err)
}
// 3. 根据物流公司名称查找对应的物流公司ID
for _, lc := range response.LogisticsCompaniesGetResponse.LogisticsCompanies {
if lc.LogisticsCompany == logisticsCompany {
logisticsOnlineSendData["logistics_id"] = fmt.Sprintf("%d", lc.ID)
break
}
}
fmt.Println(logisticsOnlineSendData["logistics_id"])
logisticsOnlineSendStr, err := json.Marshal(logisticsOnlineSendData)
if err != nil {
return "", fmt.Errorf("logisticsOnlineSendData 序列化失败: %v", err)
}
logisticsOnlineSend, err := pddLogisticsOnlineSend(clientId, clientSecret, accessToken, string(logisticsOnlineSendStr))
if err != nil {
return "", err
}
return logisticsOnlineSend, nil
}
/*
* 商品图片上传接口
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param filePath[string] 商品图片文件流
* return 商品图片上传响应结构体,错误信息
*/
func pddGoodsImgUpload(clientId, clientSecret, accessToken string, filePath string) (string, error) {
url := fmt.Sprint("https://gw-upload.pinduoduo.com/api/upload") // 上传专用地址
// 1. 打开文件
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("打开文件失败: %v", err)
}
defer file.Close() // 确保文件关闭
// 当前时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名参数
params := map[string]interface{}{
"type": "pdd.goods.img.upload", // API类型商品图片上传
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"timestamp": timestamp,
}
sign := generateSign(params, clientSecret)
// 创建multipart/form-data请求体
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 读取文件内容
fileData, err := os.ReadFile(filePath)
if err != nil {
return "", fmt.Errorf("读取文件失败: %v", err)
}
// 添加文件字段
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return "", fmt.Errorf("创建表单文件失败: %v", err)
}
_, err = io.Copy(part, bytes.NewReader(fileData))
if err != nil {
return "", fmt.Errorf("复制文件内容失败: %v", err)
}
// 添加其他参数到multipart表单
for key, value := range params {
writer.WriteField(key, fmt.Sprintf("%v", value))
}
writer.WriteField("sign", sign) // 添加签名
writer.Close() // 关闭writer完成表单构建
// 发送POST请求
request := gorequest.New()
resp, respBody, errs := request.Post(url).
Timeout(30*time.Second).
Set("Content-Type", writer.FormDataContentType()). // 设置Content-Type
Send(body.String()). // 发送请求体
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(respBody), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(respBody), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
// GoodsAddRequest 商品添加/编辑请求结构体
type GoodsAddRequest struct {
AutoFillSpuProperty bool `json:"auto_fill_spu_property,omitempty"` // 是否自动补充标品属性
BadFruitClaim int `json:"bad_fruit_claim,omitempty"` // 坏果包赔
BuyLimit int64 `json:"buy_limit,omitempty"` // 限购次数
CarouselGallery []string `json:"carousel_gallery"` // 商品轮播图
CarouselVideo []CarouselVideo `json:"carousel_video,omitempty"` // 商品视频
CarouselVideoURL string `json:"carousel_video_url,omitempty"` // 轮播视频
CatID int64 `json:"cat_id"` // 叶子类目ID
CostTemplateID int64 `json:"cost_template_id"` // 物流运费模板ID
CountryID int `json:"country_id"` // 地区/国家ID
CustomerNum int64 `json:"customer_num,omitempty"` // 团购人数
Customs string `json:"customs,omitempty"` // 海关名称
DeliveryOneDay int `json:"delivery_one_day,omitempty"` // 是否当日发货
DeliveryType int `json:"delivery_type,omitempty"` // 发货方式
DetailGallery []string `json:"detail_gallery"` // 商品详情图
ElecGoodsAttributes ElecGoodsAttributes `json:"elec_goods_attributes,omitempty"` // 卡券类商品属性
GoodsDesc string `json:"goods_desc,omitempty"` // 商品描述
GoodsName string `json:"goods_name"` // 商品标题
GoodsProperties []GoodsProperty `json:"goods_properties,omitempty"` // 商品属性列表
GoodsTradeAttr GoodsTradeAttr `json:"goods_trade_attr,omitempty"` // 日历商品交易相关信息
GoodsTravelAttr GoodsTravelAttr `json:"goods_travel_attr,omitempty"` // 商品出行信息
GoodsType int `json:"goods_type"` // 商品类型
IgnoreEditWarn bool `json:"ignore_edit_warn,omitempty"` // 是否获取商品发布警告信息
ImageURL string `json:"image_url,omitempty"` // 商品主图
InvoiceStatus bool `json:"invoice_status,omitempty"` // 是否支持开票
IsCustoms bool `json:"is_customs,omitempty"` // 是否需要上报海关
IsFolt bool `json:"is_folt"` // 是否支持假一赔十
IsGroupPreSale int `json:"is_group_pre_sale,omitempty"` // 是否成团预售
IsPreSale bool `json:"is_pre_sale"` // 是否预售
IsRefundable bool `json:"is_refundable"` // 是否7天无理由退换货
IsSkuPreSale int `json:"is_sku_pre_sale,omitempty"` // 是否sku预售
LackOfWeightClaim int `json:"lack_of_weight_claim,omitempty"` // 缺重包退
LocalServiceIDList []int `json:"local_service_id_list,omitempty"` // 本地服务id
MaiJiaZiTi string `json:"mai_jia_zi_ti,omitempty"` // 买家自提模版id
MarketPrice int64 `json:"market_price"` // 参考价格(分)
OrderLimit int `json:"order_limit,omitempty"` // 单次限量
OriginCountryID int `json:"origin_country_id,omitempty"` // 原产地id
OutGoodsID string `json:"out_goods_id,omitempty"` // 商品外部编码
OutSourceGoodsID string `json:"out_source_goods_id,omitempty"` // 第三方商品Id
OutSourceType int `json:"out_source_type,omitempty"` // 第三方商品来源
OverseaGoods OverseaGoods `json:"oversea_goods,omitempty"` // 海淘商品信息
OverseaType int `json:"oversea_type,omitempty"` // oversea_type
PreSaleTime int64 `json:"pre_sale_time,omitempty"` // 预售时间
PrivacyDelivery int `json:"privacy_delivery,omitempty"` // 保密发货
QuanGuoLianBao int `json:"quan_guo_lian_bao,omitempty"` // 是否支持全国联保
SecondHand bool `json:"second_hand"` // 是否二手商品
ShangMenAnZhuang string `json:"shang_men_an_zhuang,omitempty"` // 上门安装模版id
ShipmentLimitSecond int64 `json:"shipment_limit_second"` // 承诺发货时间(秒)
ShopGroupID int64 `json:"shop_group_id,omitempty"` // 门店组id
SizeSpecID int64 `json:"size_spec_id,omitempty"` // 尺码表id
SkuList []Sku `json:"sku_list"` // sku对象列表
SkuType int `json:"sku_type,omitempty"` // 库存方式
SongHuoAnZhuang string `json:"song_huo_an_zhuang,omitempty"` // 送货入户并安装模版id
SongHuoRuHu string `json:"song_huo_ru_hu,omitempty"` // 送货入户模版id
TinyName string `json:"tiny_name,omitempty"` // 短标题
TwoPiecesDiscount int `json:"two_pieces_discount,omitempty"` // 满2件折扣
Warehouse string `json:"warehouse,omitempty"` // 保税仓
WarmTips string `json:"warm_tips,omitempty"` // 水果类目温馨提示
ZhiHuanBuXiu int `json:"zhi_huan_bu_xiu,omitempty"` // 只换不修的天数
}
// CarouselVideo 轮播视频
type CarouselVideo struct {
FileID int64 `json:"file_id,omitempty"` // 商品视频id
VideoURL string `json:"video_url,omitempty"` // 商品视频url
}
// ElecGoodsAttributes 卡券类商品属性
type ElecGoodsAttributes struct {
BeginTime int64 `json:"begin_time,omitempty"` // 开始时间
DaysTime int `json:"days_time,omitempty"` // 天数内有效
EndTime int64 `json:"end_time,omitempty"` // 截止时间
TimeType int `json:"time_type,omitempty"` // 卡券核销类型
}
// GoodsProperty 商品属性
type GoodsProperty struct {
GroupID int `json:"group_id,omitempty"` // 属性值分组ID
ImgURL string `json:"img_url,omitempty"` // 图片url
Note string `json:"note,omitempty"` // 备注
ParentSpecID int64 `json:"parent_spec_id,omitempty"` // 父规格ID
RefPid int64 `json:"ref_pid,omitempty"` // 引用属性id
SpecID int64 `json:"spec_id,omitempty"` // 规格ID
TemplatePid int64 `json:"template_pid,omitempty"` // 模板属性id
Value string `json:"value,omitempty"` // 属性值
ValueUnit string `json:"value_unit,omitempty"` // 属性单位
Vid int64 `json:"vid,omitempty"` // 属性值id
//TableInfo []TableValueList `json:"table_info,omitempty"` // 成分表表单信息
}
//// TableValueList 表单内容列表
//type TableValueList struct {
// ColumnType int `json:"column_type,omitempty"` // 列类型 1-材质成分百分比
// Unit string `json:"unit,omitempty"` // 表单单位,材质成分表时为:%
// Value string `json:"value,omitempty"` // 表单值,材质成分表时为:占比百分值
//}
// GoodsTradeAttr 日历商品交易相关信息
type GoodsTradeAttr struct {
AdvancesDays int `json:"advances_days,omitempty"` // 提前预定天数
BookingNotes BookingNotes `json:"booking_notes,omitempty"` // 预订须知
LifeSpan int `json:"life_span,omitempty"` // 卡券有效期
}
// BookingNotes 预订须知
type BookingNotes struct {
URL *string `json:"url,omitempty"` // 预定须知图片地址
}
// GoodsTravelAttr 商品出行信息
type GoodsTravelAttr struct {
NeedTourist bool `json:"need_tourist,omitempty"` // 出行人是否必填
Type int `json:"type,omitempty"` // 日历商品类型
}
// OverseaGoods 海淘商品信息
type OverseaGoods struct {
BondedWarehouseKey string `json:"bonded_warehouse_key"` // 保税仓唯一标识
ConsumptionTaxRate int `json:"consumption_tax_rate,omitempty"` // 消费税率
CustomsBroker string `json:"customs_broker,omitempty"` // 清关服务商
HsCode string `json:"hs_code,omitempty"` // 海关编号
ValueAddedTaxRate int `json:"value_added_tax_rate,omitempty"` // 增值税率
}
// Sku SKU信息
type Sku struct {
IsOnsale int `json:"is_onsale"` // sku上架状态
Length int64 `json:"length,omitempty"` // sku送装参数长度
LimitQuantity int64 `json:"limit_quantity"` // sku购买限制
MultiPrice int64 `json:"multi_price"` // 商品团购价格
OutSkuSn string `json:"out_sku_sn,omitempty"` // 商品sku外部编码
OutSourceSkuID string `json:"out_source_sku_id,omitempty"` // 第三方sku Id
OverseaSku OverseaSku `json:"oversea_sku,omitempty"` // 海淘sku信息
Price int64 `json:"price"` // 商品单买价格
Quantity int64 `json:"quantity"` // 商品sku库存数量
SkuPreSaleTime int `json:"sku_pre_sale_time,omitempty"` // sku预售时间戳
SkuProperties []SkuProperty `json:"sku_properties"` // sku属性
SpecIDList string `json:"spec_id_list"` // 商品规格列表
ThumbURL string `json:"thumb_url"` // sku缩略图
Weight int64 `json:"weight"` // 重量g
}
// OverseaSku 海淘SKU信息
type OverseaSku struct {
MeasurementCode string `json:"measurement_code"` // 计量单位编码
Specifications string `json:"specifications"` // 规格
Taxation int `json:"taxation"` // 税费
}
// SkuProperty SKU属性
type SkuProperty struct {
Punit string `json:"punit"` // 属性单位
RefPid int64 `json:"ref_pid"` // 属性id
Value string `json:"value"` // 属性值
Vid int64 `json:"vid"` // 属性值id
}
/*
* 商品新增接口
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param goodsAddJson[string] 新增商品信息json字符串
* return 商品新增接口响应结构体,错误信息
*/
func pddGoodsAdd(clientId, clientSecret, accessToken string, goodsAddJson string) (string, error) {
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
// 当前时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名参数
params := map[string]interface{}{
"type": "pdd.goods.add", // API类型商品新增接口
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"timestamp": timestamp,
}
if goodsAddJson == "" {
return "", fmt.Errorf("goodsAddJson 参数为空!")
}
// 将JSON参数合并到params中
toParams, err := addStructToParams(goodsAddJson, params)
if err != nil {
return "", err
}
sign := generateSign(toParams, clientSecret)
if sign == "" {
return "", fmt.Errorf("生成 sign 签名错误!")
}
params["sign"] = sign
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second).
Send(params).
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
/*
* 联合拼多多图片上传的商品新增(组合接口)
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param goodsAddJson[string] 新增商品信息json字符串
* return 商品新增接口响应结构体,错误信息
*/
func selfPddGoodsAdd(clientId, clientSecret, accessToken string, filePath string, goodsAddJson string) (string, error) {
// 1. 上传商品图片
upload, err := pddGoodsImgUpload(clientId, clientSecret, accessToken, filePath)
if err != nil {
return "", err
}
// 2. 解析商品添加请求JSON
var goodsAddRequest GoodsAddRequest
if err := json.Unmarshal([]byte(goodsAddJson), &goodsAddRequest); err != nil {
return "", fmt.Errorf("解析 goodsAddJson 失败: %v", err)
}
// 3. 设置图片URL
goodsAddRequest.ImageURL = upload
// 4. 重新序列化为JSON
goodsAddRequestStr, err := json.Marshal(goodsAddRequest)
if err != nil {
return "", fmt.Errorf("序列化 goodsAddRequest 失败:%v", err)
}
// 5. 调用商品添加接口
add, err := pddGoodsAdd(clientId, clientSecret, accessToken, string(goodsAddRequestStr))
if err != nil {
return "", err
}
return add, nil
}
/*
* 批量数据解密脱敏接口
* param clientId[string] 客户端ID
* param clientSecret[string] 客户端密钥
* param accessToken[string] 访问Token
* param reqJson[string] 批量数据解密脱敏接口json字符串
* return 批量数据解密脱敏接口响应结构体,错误信息
*/
func pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken string, reqJson string) (string, error) {
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
// 当前时间戳
timestamp := fmt.Sprintf("%d", time.Now().Unix())
// 生成签名参数
params := map[string]interface{}{
"type": "pdd.open.decrypt.mask.batch", // API类型批量解密脱敏
"data_type": "JSON",
"client_id": clientId,
"access_token": accessToken,
"timestamp": timestamp,
}
params["data_list"] = reqJson // 添加数据列表
sign := generateSign(params, clientSecret)
if sign == "" {
return "", fmt.Errorf("生成 sign 签名错误!")
}
params["sign"] = sign
request := gorequest.New()
resp, body, errs := request.Get(url).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
Set("Accept", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(30 * time.Second).
Send(params).
End()
if len(errs) > 0 {
return "", fmt.Errorf("请求失败: %v", errs)
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
}
// 解析响应
var response map[string]interface{}
if err := json.Unmarshal([]byte(body), &response); err != nil {
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
}
// 异常处理
if response["error_response"] != nil {
var errorWrapper ErrorWrapper
// 解析响应
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
}
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
}
// 转换成json字符串
responseJSON, err := json.Marshal(response)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(responseJSON), nil
}
// =========================== 辅助函数 ============================
// 将结构体的字段添加到 params 映射中
func addStructToParams(req string, params map[string]interface{}) (map[string]interface{}, error) {
// 将JSON字符串解析为map
var tempMap map[string]interface{}
if err := json.Unmarshal([]byte(req), &tempMap); err != nil {
return nil, fmt.Errorf("解析 req json失败%v ", err)
}
// 合并到params
for k, v := range tempMap {
// 只添加非nil的值
if v != nil {
params[k] = v
}
}
return params, nil
}
// ========================== C 导入函数 ===================
// PddGoodsOuterCatMappingGet 类目预测
//
//export PddGoodsOuterCatMappingGet
func PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
outerCatId, outerCatName, outerGoodsName *C.char) *C.char {
// 将C字符串转换为Go字符串
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goOuterCatId := C.GoString(outerCatId)
goOuterCatName := C.GoString(outerCatName)
goOuterGoodsName := C.GoString(outerGoodsName)
// 调用Go函数
info, err := pddGoodsOuterCatMappingGet(goClientId, goClientSecret, goAccessToken, goOuterCatId, goOuterCatName, goOuterGoodsName)
if err != nil {
return C.CString(err.Error()) // 返回错误信息
}
return C.CString(info) // 返回成功信息
}
// PddLogisticsCompaniesGet 快递公司查看
//
//export PddLogisticsCompaniesGet
func PddLogisticsCompaniesGet(clientId, clientSecret *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
info, err := pddLogisticsCompaniesGet(goClientId, goClientSecret)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// PddErpOrderSync erp打单信息同步
//
//export PddErpOrderSync
func PddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
orderSn, orderState, waybillNo *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goLogisticsId := C.GoString(logisticsId)
goOrderSn := C.GoString(orderSn)
goOrderState := C.GoString(orderState)
goWaybillNo := C.GoString(waybillNo)
info, err := pddErpOrderSync(goClientId, goClientSecret, goAccessToken, goLogisticsId, goOrderSn, goOrderState, goWaybillNo)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// PddOrderSynchronization 拼多多订单同步
//
//export PddOrderSynchronization
func PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goLogisticsCompany := C.GoString(logisticsCompany)
goLogisticsOnlineSendJson := C.GoString(logisticsOnlineSendJson)
info, err := pddOrderSynchronization(goClientId, goClientSecret, goAccessToken, goLogisticsCompany, goLogisticsOnlineSendJson)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// PddGoodsImgUpload 商品图片上传接口
//
//export PddGoodsImgUpload
func PddGoodsImgUpload(clientId, clientSecret, accessToken, filePath *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goFilePath := C.GoString(filePath)
info, err := pddGoodsImgUpload(goClientId, goClientSecret, goAccessToken, goFilePath)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// PddGoodsAdd 商品新增接口
//
//export PddGoodsAdd
func PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goGoodsAddJson := C.GoString(goodsAddJson)
info, err := pddGoodsAdd(goClientId, goClientSecret, goAccessToken, goGoodsAddJson)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// SelfPddGoodsAdd 联合拼多多图片上传的商品新增
//
//export SelfPddGoodsAdd
func SelfPddGoodsAdd(clientId, clientSecret, accessToken, filePath, goodsAddJson *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goFilePath := C.GoString(filePath)
goGoodsAddJson := C.GoString(goodsAddJson)
info, err := selfPddGoodsAdd(goClientId, goClientSecret, goAccessToken, goFilePath, goGoodsAddJson)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// PddOpenDecryptMaskBatch 批量数据解密脱敏接口
//
//export PddOpenDecryptMaskBatch
func PddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, reqJson *C.char) *C.char {
goClientId := C.GoString(clientId)
goClientSecret := C.GoString(clientSecret)
goAccessToken := C.GoString(accessToken)
goReqJson := C.GoString(reqJson)
info, err := pddOpenDecryptMaskBatch(goClientId, goClientSecret, goAccessToken, goReqJson)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// FreeCString 释放C字符串内存
//
//export FreeCString
func FreeCString(str *C.char) {
C.free(unsafe.Pointer(str))
}
// main函数
//func main() {
//}