3403 lines
108 KiB
Go
3403 lines
108 KiB
Go
package main
|
||
|
||
/*
|
||
#include <stdlib.h>
|
||
*/
|
||
import "C"
|
||
import (
|
||
"bytes"
|
||
"crypto/md5"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"math/rand"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"net/url"
|
||
"os"
|
||
"path"
|
||
"path/filepath"
|
||
"regexp"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
"unsafe"
|
||
|
||
"github.com/PuerkitoBio/goquery"
|
||
_ "github.com/go-sql-driver/mysql"
|
||
"github.com/parnurzeal/gorequest"
|
||
)
|
||
|
||
// Config 结构体定义应用程序配置
|
||
type Config struct {
|
||
App struct {
|
||
MaxRetryTimes int `ini:"app.max_retry_times" json:"max_retry_times" default:"3"` // 最大重试次数
|
||
RateLimitDelay time.Duration `ini:"app.rate_limit_delay" json:"rate_limit_delay" default:"500ms"` // 请求延迟
|
||
Size int `ini:"app.size" json:"size" default:"5"` // 默认大小
|
||
DefaultUserAgent string `ini:"app.default_user_agent" json:"default_user_agent" default:"Mozilla/5.0"` // 默认 User-Agent
|
||
} `ini:"app" json:"app"`
|
||
|
||
API struct {
|
||
LoginURL string `ini:"api.login_url" json:"login_url" default:"https://login.kongfz.com/Pc/Login/account"` // 登录 URL
|
||
BookSearchURL string `ini:"api.book_search_url" json:"book_search_url" default:"https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list"` // 图书搜索 URL
|
||
ProductSearchURL string `ini:"api.product_search_url" json:"product_search_url" default:"https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list"` // 商品搜索 URL
|
||
} `ini:"api" json:"api"`
|
||
|
||
Proxy struct {
|
||
Servers string `ini:"proxy.servers" json:"servers" default:"http-dynamic.xiaoxiangdaili.com,http-dynamic-S02.xiaoxiangdaili.com,http-dynamic-S03.xiaoxiangdaili.com,http-dynamic-S04.xiaoxiangdaili.com"` // 代理服务器列表
|
||
Username string `ini:"proxy.username" json:"username" default:"1297757178467602432"` // 代理用户名
|
||
Password string `ini:"proxy.password" json:"password" default:"QgQBvP7f"` // 代理密码
|
||
TailMachineCode string `ini:"proxy.tail_machine_code" json:"tail_machine_code" default:"b7bf22a237ec692f13fcc2c43ee63252"` // 尾机编码
|
||
TailCardKey string `ini:"proxy.tail_card_key" json:"tail_card_key" default:"DL_20_YK_1920acb2129844c2aabade3896560a9b"` // 尾卡密钥
|
||
ProxyFilePath string `ini:"proxy.proxy_file_path" json:"proxy_file_path" default:"dll/proxyConfig.dll"` // 代理配置文件路径
|
||
} `ini:"proxy" json:"proxy"`
|
||
}
|
||
|
||
// BookInfo 图书详情结构体
|
||
type BookInfo struct {
|
||
BookName string `json:"book_name"` // 书名
|
||
Author string `json:"author"` // 作者
|
||
Publisher string `json:"publisher"` // 出版社
|
||
ISBN string `json:"isbn"` // ISBN
|
||
PublicationTime int64 `json:"publication_time"` // 出版时间
|
||
Edition string `json:"edition"` // 版次
|
||
PrintTime string `json:"print_time"` // 印刷时间
|
||
FixPrice string `json:"fix_price"` // 定价
|
||
BindingLayout string `json:"binding_layout"` // 装帧
|
||
Format string `json:"format"` // 开本
|
||
Paper string `json:"paper"` // 纸张
|
||
Pages string `json:"pages"` // 页数
|
||
Wordage string `json:"wordage"` // 字数
|
||
Languages string `json:"languages"` // 语种
|
||
Era string `json:"era"` // 年代
|
||
EngravingMethod string `json:"engraving_method"` // 刻印方式
|
||
Dimensions string `json:"dimensions"` // 尺寸
|
||
VolumeNumber string `json:"volume_number"` // 册数
|
||
BookPic string `json:"book_pic"` // 图书封面图(官图)
|
||
BookPicS string `json:"book_pic_s"` // 图书封面图(实拍图)
|
||
SellingPrice string `json:"selling_price"` // 售价
|
||
Condition string `json:"condition"` // 品相
|
||
ExpressDeliveryFee string `json:"express_delivery_fee"` // 快递费
|
||
Editor string `json:"editor"` // 编辑
|
||
Category string `json:"category"` // 分类
|
||
BuyCount string `json:"buy_count"` // 买过
|
||
SellCount string `json:"sell_count"` // 在卖
|
||
Content string `json:"content"` // 内容
|
||
Mid int64 `json:"mid"` // 商家id
|
||
ItemId int64 `json:"item_id"` // 商品id
|
||
ShopId int64 `json:"shop_id"` // 店铺id
|
||
DetailUrl string `json:"detail_url"` // 商品详情url
|
||
}
|
||
|
||
// APIResponse 通用API响应结构体
|
||
type APIResponse struct {
|
||
Success bool `json:"success"` // 请求是否成功
|
||
Message string `json:"message,omitempty"` // 错误信息(成功时可选)
|
||
Data interface{} `json:"data,omitempty"` // 响应数据(失败时可选)
|
||
}
|
||
|
||
// UserInfo 孔网用户信息结构体
|
||
type UserInfo struct {
|
||
UserID int64 `json:"userId"` // 用户ID
|
||
Nickname string `json:"nickname"` // 用户昵称
|
||
Mobile string `json:"mobile"` // 手机号
|
||
}
|
||
|
||
// 全局配置变量
|
||
var (
|
||
cf Config // 存储应用程序配置
|
||
)
|
||
|
||
// ProductInfo 商品信息结构体
|
||
type ProductInfo struct {
|
||
ItemID string `json:"itemId"` // 商品ID
|
||
BookName string `json:"bookName"` // 书名
|
||
Price string `json:"price"` // 价格
|
||
ShippingFee string `json:"shippingFee"` // 运费
|
||
}
|
||
|
||
// BookDetailResponse 图书详情响应结构体
|
||
type BookDetailResponse struct {
|
||
Status bool `json:"status"` // 状态
|
||
Result BookList `json:"result"` // 结果数据
|
||
ErrMessage string `json:"errMessage"` // 错误信息
|
||
ErrCode int `json:"errCode"` // 错误码
|
||
}
|
||
|
||
// BookList 图书列表结构体
|
||
type BookList struct {
|
||
Current int `json:"current"` // 当前页码
|
||
Data []BookInformation `json:"data"` // 图书数据列表
|
||
Total int `json:"total"` // 总数
|
||
}
|
||
|
||
// BookInformation 图书信息结构体
|
||
type BookInformation struct {
|
||
Author string `json:"author"` // 作者
|
||
BookName string `json:"bookName"` // 书名
|
||
ContentIntroduction string `json:"contentIntroduction"` // 内容简介
|
||
ImgUrl string `json:"imgUrl"` // 图片URL
|
||
Isbn string `json:"isbn"` // ISBN
|
||
ItemUrls ItemUrls `json:"itemUrls"` // 商品URL
|
||
Mid int `json:"mid"` // 商家ID
|
||
NewMinPrice string `json:"newMinPrice"` // 新书最低价
|
||
OldMinPrice string `json:"oldMinPrice"` // 旧书最低价
|
||
Press string `json:"press"` // 出版社
|
||
Price string `json:"price"` // 价格
|
||
PubDate string `json:"pubDate"` // 出版日期
|
||
RiseTag string `json:"riseTag"` // 上涨标签
|
||
AuthorArr []AuthorInfo `json:"authorArr"` // 作者数组
|
||
PressUrl string `json:"pressUrl"` // 出版社URL
|
||
}
|
||
|
||
// AuthorInfo 作者信息结构体
|
||
type AuthorInfo struct {
|
||
Name string `json:"name"` // 作者姓名
|
||
OriName string `json:"oriName"` // 原始姓名
|
||
Nationality string `json:"nationality"` // 国籍
|
||
Role string `json:"role"` // 角色
|
||
Url string `json:"url"` // 作者页面URL
|
||
}
|
||
|
||
// ItemUrls 商品链接结构体
|
||
type ItemUrls struct {
|
||
AppUrl string `json:"appUrl"` // App链接
|
||
MUrl string `json:"mUrl"` // 移动端链接
|
||
MiniUrl string `json:"miniUrl"` // 小程序链接
|
||
PcUrl string `json:"pcUrl"` // PC端链接
|
||
}
|
||
|
||
// ParamsInfo 店铺快递费参数结构体
|
||
type ParamsInfo struct {
|
||
Params []Param `json:"params"` // 参数列表
|
||
Area string `json:"area"` // 地区编码
|
||
}
|
||
|
||
// Param 单个参数结构体
|
||
type Param struct {
|
||
UserId string `json:"userId"` // 用户ID
|
||
ItemId string `json:"itemId"` // 商品ID
|
||
}
|
||
|
||
// DataItem 快递费用响应数据项
|
||
type DataItem struct {
|
||
Fee []FeeInfo `json:"fee"` // 费用列表
|
||
IsSeller bool `json:"isSeller"` // 是否是卖家
|
||
ItemID string `json:"itemId"` // 商品ID
|
||
UserID string `json:"userId"` // 用户ID
|
||
}
|
||
|
||
// FeeInfo 费用信息结构体
|
||
type FeeInfo struct {
|
||
ShippingID string `json:"shippingId"` // 配送方式ID
|
||
ShippingName string `json:"shippingName"` // 配送方式名称
|
||
TotalFee string `json:"totalFee"` // 总费用
|
||
ShippingText string `json:"shippingText"` // 配送说明
|
||
FilterTotalFee string `json:"filterTotalFee"` // 过滤后的总费用
|
||
}
|
||
|
||
// ResponseStruct 快递费响应结构体
|
||
type ResponseStruct struct {
|
||
Status bool `json:"status"` // 状态
|
||
Data []DataItem `json:"data"` // 数据
|
||
Message string `json:"message"` // 消息
|
||
ErrType string `json:"errType"` // 错误类型
|
||
}
|
||
|
||
/*
|
||
* 获取店铺里商品的快递费(定位到河南)
|
||
* param params[ParamsInfo] 店铺快递费参数结构体
|
||
* param proxy[string] 代理服务器IP
|
||
* return 快递费用响应数据项数组,错误信息
|
||
* Error 没有商品信息
|
||
* Error 参数序列化失败
|
||
* Error 请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
*/
|
||
func getGoodsListShippingFee(params ParamsInfo, proxy string) ([]DataItem, error) {
|
||
// 检查参数是否为空
|
||
if params.Params == nil {
|
||
return nil, fmt.Errorf("没有商品信息!")
|
||
}
|
||
// 序列化参数为JSON
|
||
paramsJSON, err := json.Marshal(params)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("参数序列化失败: %v", err)
|
||
}
|
||
// URL 编码参数
|
||
encodedParams := url.QueryEscape(string(paramsJSON))
|
||
// 构建请求URL
|
||
url := fmt.Sprintf("https://shop.kongfz.com/book/shopsearch/getShippingFee?params=%s", encodedParams)
|
||
// 创建请求对象
|
||
request := gorequest.New()
|
||
// 设置代理
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
// 发送GET请求
|
||
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").
|
||
End()
|
||
// 错误处理
|
||
if len(errs) > 0 {
|
||
return nil, fmt.Errorf("请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 解析JSON响应
|
||
responseStruct := ResponseStruct{}
|
||
err = json.Unmarshal([]byte(body), &responseStruct)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %v", err)
|
||
}
|
||
// 提取数据项
|
||
var dataItem []DataItem
|
||
for i := range responseStruct.Data {
|
||
dataItem = append(dataItem, responseStruct.Data[i])
|
||
}
|
||
return dataItem, nil
|
||
}
|
||
|
||
/*
|
||
* 孔网登录
|
||
* param username[string] 孔网用户名
|
||
* param password[string] 孔网密码
|
||
* return token,错误信息
|
||
* Error 登录请求失败
|
||
* Error 登录失败(HTTP状态码: %d)
|
||
* Error 登录成功但未获取到Cookie
|
||
* Error 登录失败: 未找到 PHPSESSID
|
||
* Error 账号或密码错误
|
||
* Error 登录失败
|
||
* Error 登录失败,未知错误!
|
||
*/
|
||
func outLogin(username, password string) (string, error) {
|
||
// 检查用户名和密码是否为空
|
||
if username == "" || password == "" {
|
||
return "", fmt.Errorf("请输入用户名和密码!")
|
||
}
|
||
|
||
// 准备POST请求的表单数据
|
||
formData := map[string]string{
|
||
"loginName": username,
|
||
"loginPass": password,
|
||
"returnUrl": "http://user.kongfz.com/",
|
||
}
|
||
// 孔网登录URL
|
||
loginUrl := "https://login.kongfz.com/Pc/Login/account"
|
||
|
||
// 发送登录请求
|
||
resp, body, errs := gorequest.New().
|
||
Post(loginUrl).
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
|
||
Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8").
|
||
Send(formData).
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
|
||
// 请求错误处理
|
||
if len(errs) > 0 {
|
||
return "", fmt.Errorf("登录请求失败: %v", errs)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", fmt.Errorf("登录失败(HTTP状态码: %d)", resp.StatusCode)
|
||
}
|
||
|
||
// 提取Cookie
|
||
cookie := resp.Header.Get("Set-Cookie")
|
||
// 检查是否登录成功(通过响应内容判断)
|
||
if strings.Contains(body, "window.location.href='https://login.kongfz.cn/Pc/Session/rsync") {
|
||
if cookie == "" {
|
||
return "", fmt.Errorf("登录成功但未获取到Cookie")
|
||
}
|
||
|
||
// 登录成功
|
||
if strings.Contains(cookie, "PHPSESSID=") {
|
||
token := strings.Split(strings.Split(cookie, "PHPSESSID=")[1], ";")[0]
|
||
return token, nil
|
||
}
|
||
|
||
return "", fmt.Errorf("登录失败: 未找到PHPSESSID")
|
||
}
|
||
|
||
// 错误信息
|
||
var res struct {
|
||
Status bool `json:"status"`
|
||
ErrCode int `json:"errCode"`
|
||
ErrInfo string `json:"errInfo"`
|
||
}
|
||
// 解析json
|
||
if err := json.Unmarshal([]byte(body), &res); err == nil {
|
||
if res.ErrCode == 1001 || res.ErrCode == 1005 {
|
||
return "", fmt.Errorf("账号或密码错误!")
|
||
}
|
||
|
||
if res.ErrInfo != "" {
|
||
return "", fmt.Errorf("登录失败: %s", res.ErrInfo)
|
||
}
|
||
}
|
||
|
||
return "", fmt.Errorf("登录失败,未知错误!")
|
||
}
|
||
|
||
/*
|
||
* 获取孔网用户信息
|
||
* param token[string] 孔网token
|
||
* return 孔网用户信息结构体,错误信息
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error 获取用户失败
|
||
*/
|
||
func outGetUserMsg(token string) (*UserInfo, error) {
|
||
// 用户信息URL
|
||
url := "https://user.kongfz.com/User/Index/getUserInfo/"
|
||
|
||
// 发送请求
|
||
resp, body, errs := gorequest.New().
|
||
Get(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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(15 * time.Second).
|
||
End()
|
||
if len(errs) > 0 {
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
|
||
// 响应数据
|
||
var userInfo struct {
|
||
Status bool `json:"status"`
|
||
Data struct {
|
||
UserID int64 `json:"userId"`
|
||
Nickname string `json:"nickname"`
|
||
Mobile string `json:"mobile"`
|
||
}
|
||
}
|
||
// 解析json
|
||
if err := json.Unmarshal([]byte(body), &userInfo); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
|
||
// 创建用户信息对象
|
||
user := &UserInfo{}
|
||
if !userInfo.Status {
|
||
return nil, fmt.Errorf("获取用户失败!")
|
||
}
|
||
user.UserID = userInfo.Data.UserID
|
||
user.Nickname = userInfo.Data.Nickname
|
||
user.Mobile = userInfo.Data.Mobile
|
||
return user, nil
|
||
}
|
||
|
||
/*
|
||
* 获取商品模版-已登的店铺
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param itemId[string] 商品ID
|
||
* return 商品模板信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误
|
||
*/
|
||
func outGetGoodsTplMsg(token, proxy, itemId string) (map[string]interface{}, error) {
|
||
// 判断登录token
|
||
if token == "" {
|
||
return nil, fmt.Errorf("请先登录获取Token")
|
||
}
|
||
// 商品模板URL
|
||
url := fmt.Sprintf("https://seller.kongfz.com/pc/itemInfo/getTplFields?itemId=%s&isClone=1&v=%d",
|
||
itemId, time.Now().Unix())
|
||
|
||
// 创建请求
|
||
request := gorequest.New()
|
||
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, body, errs := request.Get(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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").
|
||
Set("Referer", "https://seller.kongfz.com/").
|
||
Set("Origin", "https://seller.kongfz.com").
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
// 错误处理
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败")
|
||
}
|
||
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
|
||
// 解析json
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &data); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
|
||
// 检查status状态
|
||
if val, ok := data["status"].(float64); ok && val == 1 {
|
||
return data, nil
|
||
}
|
||
return nil, fmt.Errorf("API返回错误: %+v", data)
|
||
}
|
||
|
||
/*
|
||
* 获取商品列表-已登的店铺
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param itemSn[string] 货号
|
||
* param priceMin[string] 价格区间-低
|
||
* param priceMax[string] 价格区间-高
|
||
* param startCreateTime[string] 开始时间
|
||
* param endCreateTime[string] 结束时间
|
||
* param requestType[string] 请求类型
|
||
* param isItemSnEqual[int] 商品ID
|
||
* param page[int] 页数
|
||
* param size[int] 大小
|
||
* return 商品列表响应信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误
|
||
*/
|
||
func outGetGoodsListMsgFromSelfShop(token string, proxy string, itemSn string, priceMin string, priceMax string,
|
||
startCreateTime string, endCreateTime string, requestType string,
|
||
isItemSnEqual int, page int, size int) (map[string]interface{}, error) {
|
||
// 判断登录token
|
||
if token == "" {
|
||
return nil, fmt.Errorf("请先登录获取Token")
|
||
}
|
||
|
||
// 已登店铺的商品信息URL
|
||
url := "https://seller.kongfz.com/pc-gw/book-manage-service/client/pc/goods/unSold/list"
|
||
|
||
// 请求参数
|
||
formData := struct {
|
||
ItemSn string `json:"itemSn"`
|
||
PriceMin string `json:"priceMin"`
|
||
PriceMax string `json:"priceMax"`
|
||
StartCreateTime string `json:"startCreateTime"`
|
||
EndCreateTime string `json:"endCreateTime"`
|
||
RequestType string `json:"requestType,omitempty"`
|
||
IsItemSnEqual int `json:"isItemSnEqual,omitempty"`
|
||
Page int `json:"page,omitempty"`
|
||
Size int `json:"size,omitempty"`
|
||
}{
|
||
ItemSn: itemSn,
|
||
PriceMin: priceMin,
|
||
PriceMax: priceMax,
|
||
StartCreateTime: startCreateTime,
|
||
EndCreateTime: endCreateTime,
|
||
RequestType: requestType,
|
||
IsItemSnEqual: isItemSnEqual,
|
||
Page: page,
|
||
Size: size,
|
||
}
|
||
// 创建请求
|
||
request := gorequest.New()
|
||
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
// 发送POST请求
|
||
resp, body, errs := request.Post(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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, */*").
|
||
Send(formData).
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败")
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
|
||
// 解析json
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &data); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
|
||
if _, ok := data["status"]; ok {
|
||
return data, nil
|
||
}
|
||
return nil, fmt.Errorf("API返回错误: %+v", data)
|
||
}
|
||
|
||
/*
|
||
* 新增商品-已登的店铺
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param formData[string] 商品信息结构体字符串
|
||
* return 新增商品响应信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误
|
||
*/
|
||
func outAddGoods(token, proxy, formData string) (map[string]interface{}, error) {
|
||
// 判断登录token
|
||
if token == "" {
|
||
return nil, fmt.Errorf("请先登录获取Token")
|
||
}
|
||
// 新增商品的URL
|
||
url := "https://seller.kongfz.com/pc/itemInfo/add"
|
||
|
||
var repData map[string]interface{}
|
||
err := json.Unmarshal([]byte(formData), &repData)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("JSON解析失败: %v", err)
|
||
}
|
||
|
||
//// 创建 multipart 请求体
|
||
//body := &bytes.Buffer{}
|
||
//writer := multipart.NewWriter(body)
|
||
//
|
||
//// 写入所有表单字段
|
||
//for key, value := range repData {
|
||
// if err := writer.WriteField(key, value); err != nil {
|
||
// return nil, fmt.Errorf("写入表单字段失败: %w", err)
|
||
// }
|
||
//}
|
||
//
|
||
//// 关闭writer以完成multipart写入
|
||
//if err := writer.Close(); err != nil {
|
||
// return nil, fmt.Errorf("关闭multipart写入器失败: %w", err)
|
||
//}
|
||
|
||
// 创建请求
|
||
request := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, respBody, errs := request.Post(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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("Content-Type", writer.FormDataContentType()).
|
||
Type("multipart").
|
||
Send(repData).
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败: %s", errs)
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 解析JSON响应
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(respBody), &data); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
// 检查响应状态
|
||
if val, ok := data["status"]; ok && fmt.Sprint(val) == "1" {
|
||
return data, nil
|
||
}
|
||
return nil, fmt.Errorf("API返回错误: %+v", data)
|
||
}
|
||
|
||
/*
|
||
* 获取图片key
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* return 获取图片key响应信息结构体,错误信息
|
||
*/
|
||
func outKfzimgKey(token, proxy string) (map[string]interface{}, error) {
|
||
// 判断登录token
|
||
if token == "" {
|
||
return nil, fmt.Errorf("请先登录获取Token")
|
||
}
|
||
// 新增商品的URL
|
||
url := fmt.Sprintf("https://seller.kongfz.com/kfzimg-key?type=%s", "book")
|
||
// 创建请求
|
||
request := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, body, errs := request.Get(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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, */*").
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败: %s", errs)
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
|
||
// 去除括号
|
||
jsonStr := strings.TrimPrefix(body, "(")
|
||
jsonStr = strings.TrimSuffix(jsonStr, ")")
|
||
// 解析JSON响应
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
// 检查响应状态
|
||
if val, ok := data["status"]; ok && val == "success" {
|
||
return data, nil
|
||
}
|
||
return nil, fmt.Errorf("API返回错误: %+v", data)
|
||
}
|
||
|
||
/*
|
||
* 获取孔网图片
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* return 获取孔网图片响应信息结构体,错误信息
|
||
*/
|
||
func outKfzimgUpload(token, proxy, filePath string) (string, error) {
|
||
url := fmt.Sprint("https://seller.kongfz.com/kfzimg-upload")
|
||
fileName := filepath.Base(filePath)
|
||
// 创建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("img", filepath.Base(filePath))
|
||
if err != nil {
|
||
return "", fmt.Errorf("创建表单文件失败: %v", err)
|
||
}
|
||
_, err = part.Write(fileData)
|
||
if err != nil {
|
||
return "", fmt.Errorf("写入文件数据失败: %v", err)
|
||
}
|
||
_ = writer.WriteField("autorotated", "1")
|
||
_ = writer.WriteField("name", fileName)
|
||
_ = writer.Close()
|
||
|
||
key, err := outKfzimgKey(token, proxy)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
imageKey := key["imagekey"]
|
||
|
||
// 在发送请求前添加
|
||
log.Printf("发送请求到: %s", url)
|
||
log.Printf("Cookie: PHPSESSID=%s", token)
|
||
log.Printf("文件: %s", fileName)
|
||
log.Printf("Content-Type: %s", writer.FormDataContentType())
|
||
// 发送POST请求
|
||
request := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, respBody, errs := request.Post(url).
|
||
//Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s; IMGKEY=%s", token, imageKey)).
|
||
Set("Content-Type", writer.FormDataContentType()). // 设置Content-Type
|
||
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", "*/*").
|
||
Set("Host", "seller.kongfz.com").
|
||
Set("Connection", "keep-alive").
|
||
Send(body.String()). // 发送请求体
|
||
Timeout(30 * time.Second).
|
||
End()
|
||
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return "", fmt.Errorf("代理连接失败: %s", errs)
|
||
}
|
||
return "", fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
// 在得到响应后添加
|
||
log.Printf("响应状态码: %d", resp.StatusCode)
|
||
log.Printf("响应头: %v", resp.Header)
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 正则表达式匹配路径
|
||
re := regexp.MustCompile(`(sw/.*?\.jpg)`)
|
||
matches := re.FindStringSubmatch(respBody)
|
||
if len(matches) > 0 {
|
||
return matches[1], nil
|
||
} else {
|
||
return "", fmt.Errorf("未找到匹配的路径")
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 整合添加商品和获取孔网图片
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param filePath[string] 图片路径
|
||
* param formData[string] 商品JSON字符串
|
||
* return 添加商品响应信息结构体,错误信息
|
||
*/
|
||
func outAddGoodsAndFile(token, proxy, filePath, formData string) (string, error) {
|
||
upload, err := outKfzimgUpload(token, proxy, filePath)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
var data map[string]interface{}
|
||
err = json.Unmarshal([]byte(formData), &data)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON解析失败: %v", err)
|
||
}
|
||
data["images[0][imgUrl]"] = upload
|
||
|
||
dataStr, err := json.Marshal(data)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
fmt.Println(string(dataStr))
|
||
goods, err := outAddGoods(token, proxy, string(dataStr))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
goodsStr, err := json.Marshal(goods)
|
||
if err != nil {
|
||
return "", fmt.Errorf("goodsStr JSON序列化失败: %v", err)
|
||
}
|
||
return string(goodsStr), nil
|
||
}
|
||
|
||
/*
|
||
* 删除商品-已登的店铺
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param itemId[string] 商品ID
|
||
* return 删除商品响应信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误
|
||
*/
|
||
func outDelGoodsFromSelfShop(token, proxy, itemId string) (map[string]interface{}, error) {
|
||
// 判断登录token
|
||
if token == "" {
|
||
return nil, fmt.Errorf("请先登录获取Token")
|
||
}
|
||
// 删除商品的URL
|
||
url := "https://seller.kongfz.com/pc-gw/book-manage-service/client/pc/goods/quickUpdate"
|
||
formData := map[string]string{
|
||
"itemId": itemId,
|
||
"updateType": "delete",
|
||
"value": "1",
|
||
}
|
||
// 创建请求
|
||
request := gorequest.New()
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, body, errs := request.Post(url).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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, */*").
|
||
Send(formData).
|
||
Timeout(15 * time.Second).
|
||
End()
|
||
if errs != nil {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败")
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 解析json
|
||
var data map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &data); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
// 判断是否响应成功
|
||
if status, ok := data["status"].(bool); ok && status {
|
||
return data, nil
|
||
}
|
||
// 判断是否响应成功
|
||
if status, ok := data["status"].(float64); ok && status == 1 {
|
||
return data, nil
|
||
}
|
||
|
||
return nil, fmt.Errorf("API返回错误: %+v", data)
|
||
}
|
||
|
||
/*
|
||
* 获取孔网商品图片(官图和拍图)-带有店铺过滤
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param itemId[string] 商品ID
|
||
* return 删除商品响应信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误
|
||
*/
|
||
func outGetImageFilterShopId(token string, proxy string, isbn string, shopId int, isLiveImage int, isReturnMsg int) (map[string]string, error) {
|
||
// 判断是否为官图(isLiveImage=0)
|
||
if isLiveImage == 0 {
|
||
// 图书条目URL
|
||
gtUrl := fmt.Sprintf("%s?keyword=%s",
|
||
"https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list", isbn)
|
||
request := gorequest.New()
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
resp, body, errs := request.Get(gtUrl).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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").
|
||
Set("Referer", "https://item.kongfz.com/").
|
||
Timeout(30 * time.Second).
|
||
End()
|
||
if len(errs) > 0 {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errs {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败")
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errs)
|
||
}
|
||
//检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 解析响应
|
||
var apiGtResp struct {
|
||
Status int `json:"status"`
|
||
ErrType string `json:"errType"`
|
||
Message string `json:"message"`
|
||
SystemTime int64 `json:"systemTime"`
|
||
Data struct {
|
||
ItemResponse struct {
|
||
Total int `json:"total"`
|
||
List []struct {
|
||
BookName string `json:"bookName"`
|
||
Mid int64 `json:"mid"`
|
||
ImgUrlEntity struct {
|
||
BigImgUrl string `json:"bigImgUrl"`
|
||
} `json:"imgUrlEntity"`
|
||
Isbn string `json:"isbn"`
|
||
BookShowInfo []string `json:"bookShowInfo"`
|
||
} `json:"list"`
|
||
} `json:"itemResponse"`
|
||
} `json:"data"`
|
||
}
|
||
if err := json.Unmarshal([]byte(body), &apiGtResp); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
if apiGtResp.ErrType == "102" {
|
||
return nil, fmt.Errorf("错误信息: %w,状态码: %s", apiGtResp.Message, apiGtResp.ErrType)
|
||
}
|
||
var info map[string]string
|
||
// 如果找到条目,返回图片URL
|
||
if apiGtResp.Data.ItemResponse.Total > 0 && len(apiGtResp.Data.ItemResponse.List) > 0 {
|
||
list := apiGtResp.Data.ItemResponse.List[0]
|
||
info = map[string]string{
|
||
"book_name": list.BookName,
|
||
"book_pic": list.ImgUrlEntity.BigImgUrl,
|
||
"isbn": list.Isbn,
|
||
}
|
||
}
|
||
return info, nil
|
||
}
|
||
// 处理实拍图(isLiveImage=1)
|
||
if isLiveImage == 1 {
|
||
size := 10
|
||
// 实拍图
|
||
sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000",
|
||
"https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list", isbn, size)
|
||
//创建HTTP客户端
|
||
requestSpt := gorequest.New()
|
||
//设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
requestSpt.Proxy(proxy)
|
||
}
|
||
// 发送请求
|
||
respSpt, bodySpt, errsSpt := requestSpt.Get(sptUrl).
|
||
Proxy(proxy).
|
||
Set("Cookie", token).
|
||
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").
|
||
Set("Referer", "https://item.kongfz.com/").
|
||
Timeout(30 * time.Second).
|
||
End()
|
||
// 错误处理
|
||
if len(errsSpt) > 0 {
|
||
return nil, fmt.Errorf("请求失败: %v", errsSpt)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if respSpt.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", respSpt.Status)
|
||
}
|
||
|
||
// 解析响应
|
||
var apiSptResp struct {
|
||
Status int `json:"status"`
|
||
ErrType string `json:"errType"`
|
||
Message string `json:"message"`
|
||
SystemTime int64 `json:"systemTime"`
|
||
Data struct {
|
||
ItemResponse struct {
|
||
Total int `json:"total"`
|
||
List []struct {
|
||
ItemId int64 `json:"itemId"`
|
||
Title string `json:"title"`
|
||
ImgUrl string `json:"imgUrl"`
|
||
ImgBigUrl string `json:"imgBigUrl"`
|
||
Author string `json:"author"`
|
||
PubDateText string `json:"pubDateText"`
|
||
Isbn string `json:"isbn"`
|
||
Press string `json:"press"`
|
||
ShopId int64 `json:"shopId"`
|
||
TplRecords []struct {
|
||
Key string `json:"key"`
|
||
Value string `json:"value"`
|
||
} `json:"tplRecords"`
|
||
} `json:"list"`
|
||
} `json:"itemResponse"`
|
||
} `json:"data"`
|
||
}
|
||
// 解析JSON
|
||
if err := json.Unmarshal([]byte(bodySpt), &apiSptResp); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
if apiSptResp.ErrType == "102" {
|
||
return nil, fmt.Errorf("错误信息: %w,状态码: %s", apiSptResp.Message, apiSptResp.ErrType)
|
||
}
|
||
var info map[string]string
|
||
if apiSptResp.Data.ItemResponse.Total > 0 && len(apiSptResp.Data.ItemResponse.List) > 0 {
|
||
// 确定起始索引
|
||
var startIndex int
|
||
if size >= apiSptResp.Data.ItemResponse.Total {
|
||
startIndex = apiSptResp.Data.ItemResponse.Total - 1
|
||
} else {
|
||
startIndex = size - 1
|
||
}
|
||
// 尝试3次获取有效的图片
|
||
for attempt := 0; attempt < 3; attempt++ {
|
||
currentIndex := startIndex - attempt
|
||
// 检查索引是否有效
|
||
if currentIndex < 0 || currentIndex >= len(apiSptResp.Data.ItemResponse.List) {
|
||
log.Printf("[DEBUG] 索引 %d 超出范围,跳过", currentIndex)
|
||
continue
|
||
}
|
||
randomNum := rand.Intn(currentIndex + 1)
|
||
item := apiSptResp.Data.ItemResponse.List[randomNum]
|
||
// 检查图片URL是否存在
|
||
if item.ImgBigUrl == "" {
|
||
log.Printf("[DEBUG] 索引 %d 的图片URL为空,跳过", randomNum)
|
||
continue
|
||
}
|
||
// 过滤店铺
|
||
if item.ShopId == int64(shopId) {
|
||
log.Printf("[DEBUG] 索引 %d 的店铺ID需要过滤,跳过", randomNum)
|
||
continue
|
||
}
|
||
// 返回图片信息
|
||
info = map[string]string{
|
||
"book_name": item.Title,
|
||
"book_pic_s": item.ImgUrl,
|
||
"isbn": item.Isbn,
|
||
}
|
||
return info, nil
|
||
}
|
||
}
|
||
}
|
||
return nil, nil
|
||
}
|
||
|
||
/*
|
||
* 获取孔网商品图片和信息(官图和拍图)
|
||
* param token[string] 孔网token
|
||
* param proxy[string] 代理服务器IP
|
||
* param isbn[string] isbn
|
||
* param isLiveImage[int] 是否官图 0官图 1实拍图
|
||
* param isReturnMsg[int] 是否有其他信息
|
||
* return 孔网商品图片和信息响应信息结构体,错误信息
|
||
* Error 请先登录获取Token
|
||
* Error 代理连接失败
|
||
* Error 查询请求失败
|
||
* Error 解析JSON失败
|
||
* Error 错误信息: %w,状态码: %s
|
||
* Error 查询失败,没有数据!
|
||
*/
|
||
func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int, isReturnMsg int) (*BookInfo, error) {
|
||
// isLiveImage 1实拍图 0官图 ,isReturnMsg 0商品信息
|
||
bookInfo := &BookInfo{}
|
||
// 处理官图(isLiveImage=0)
|
||
if isLiveImage == 0 {
|
||
// 孔网官图请求
|
||
gtUrl := fmt.Sprintf("%s?keyword=%s",
|
||
"https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list", isbn)
|
||
// 创建HTTP客户端
|
||
requestGt := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
requestGt.Proxy(proxy)
|
||
}
|
||
// 发送GET请求
|
||
respGt, bodyGt, errsGt := requestGt.Get(gtUrl).
|
||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||
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").
|
||
Set("Referer", "https://item.kongfz.com/").
|
||
Timeout(30 * time.Second).
|
||
End()
|
||
if len(errsGt) > 0 {
|
||
// 检查是否是代理相关错误
|
||
var isProxyError bool
|
||
var errorDetails []string
|
||
for _, e := range errsGt {
|
||
errorStr := e.Error()
|
||
errorDetails = append(errorDetails, errorStr)
|
||
if strings.Contains(errorStr, "Proxy Authentication Required") ||
|
||
strings.Contains(errorStr, "connectex: A connection attempt failed") ||
|
||
strings.Contains(errorStr, "connectex: No connection could be made") ||
|
||
strings.Contains(errorStr, "proxyconnect tcp") ||
|
||
strings.Contains(errorStr, "timeout") ||
|
||
strings.Contains(errorStr, "connection refused") {
|
||
isProxyError = true
|
||
}
|
||
}
|
||
log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
|
||
if isProxyError {
|
||
// 处理代理失败
|
||
return nil, fmt.Errorf("代理连接失败")
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败: %v", errsGt)
|
||
}
|
||
//检查HTTP状态码
|
||
if respGt.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", respGt.Status)
|
||
}
|
||
// 解析响应
|
||
var apiGtResp struct {
|
||
Status int `json:"status"`
|
||
ErrType string `json:"errType"`
|
||
Message string `json:"message"`
|
||
SystemTime int64 `json:"systemTime"`
|
||
Data struct {
|
||
ItemResponse struct {
|
||
Total int `json:"total"`
|
||
List []struct {
|
||
BookName string `json:"bookName"`
|
||
Mid int64 `json:"mid"`
|
||
ImgUrlEntity struct {
|
||
BigImgUrl string `json:"bigImgUrl"`
|
||
} `json:"imgUrlEntity"`
|
||
Isbn string `json:"isbn"`
|
||
BookShowInfo []string `json:"bookShowInfo"`
|
||
} `json:"list"`
|
||
} `json:"itemResponse"`
|
||
} `json:"data"`
|
||
}
|
||
// 解析JSON
|
||
if err := json.Unmarshal([]byte(bodyGt), &apiGtResp); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
if apiGtResp.ErrType == "102" {
|
||
return nil, fmt.Errorf("错误信息: %w,状态码: %s", apiGtResp.Message, apiGtResp.ErrType)
|
||
}
|
||
// 如果找到条目,返回图片URL
|
||
if apiGtResp.Data.ItemResponse.Total > 0 && len(apiGtResp.Data.ItemResponse.List) > 0 {
|
||
list := apiGtResp.Data.ItemResponse.List[0]
|
||
bookShowInfo := list.BookShowInfo
|
||
bookInfo.BookName = list.BookName
|
||
bookInfo.BookPic = list.ImgUrlEntity.BigImgUrl
|
||
bookInfo.ISBN = list.Isbn
|
||
// 根据长度安全填充字段
|
||
if isReturnMsg == 0 {
|
||
if len(bookShowInfo) > 0 {
|
||
bookInfo.Author = bookShowInfo[0]
|
||
}
|
||
if len(bookShowInfo) > 1 {
|
||
bookInfo.Publisher = bookShowInfo[1]
|
||
}
|
||
if len(bookShowInfo) > 2 {
|
||
bookInfo.PublicationTime = validateDateFormat(bookShowInfo[2])
|
||
}
|
||
if len(bookShowInfo) > 3 {
|
||
bookInfo.BindingLayout = bookShowInfo[3]
|
||
}
|
||
if len(bookShowInfo) > 4 {
|
||
bookInfo.FixPrice = bookShowInfo[4]
|
||
} else {
|
||
log.Printf("[WARN] BookShowInfo 长度不足 (仅 %d 项): %v", len(bookShowInfo), bookShowInfo)
|
||
}
|
||
}
|
||
}
|
||
return bookInfo, nil
|
||
}
|
||
// 处理实拍图(isLiveImage=1)
|
||
if isLiveImage == 1 {
|
||
size := 10
|
||
// 实拍图
|
||
sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000",
|
||
"https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list", isbn, size)
|
||
//创建HTTP客户端
|
||
requestSpt := gorequest.New()
|
||
//设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
requestSpt.Proxy(proxy)
|
||
}
|
||
// 发送请求
|
||
respSpt, bodySpt, errsSpt := requestSpt.Get(sptUrl).
|
||
Proxy(proxy).
|
||
Set("Cookie", token).
|
||
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").
|
||
Set("Referer", "https://item.kongfz.com/").
|
||
Timeout(30 * time.Second).
|
||
End()
|
||
// 错误处理
|
||
if len(errsSpt) > 0 {
|
||
return nil, fmt.Errorf("请求失败: %v", errsSpt)
|
||
}
|
||
// 检查HTTP状态码
|
||
if respSpt.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", respSpt.Status)
|
||
}
|
||
// 解析响应
|
||
var apiSptResp struct {
|
||
Status int `json:"status"`
|
||
ErrType string `json:"errType"`
|
||
Message string `json:"message"`
|
||
SystemTime int64 `json:"systemTime"`
|
||
Data struct {
|
||
ItemResponse struct {
|
||
Total int `json:"total"`
|
||
List []struct {
|
||
ItemId int64 `json:"itemId"`
|
||
Title string `json:"title"`
|
||
ImgUrl string `json:"imgUrl"`
|
||
ImgBigUrl string `json:"imgBigUrl"`
|
||
Author string `json:"author"`
|
||
PubDateText string `json:"pubDateText"`
|
||
Isbn string `json:"isbn"`
|
||
Press string `json:"press"`
|
||
ShopId int64 `json:"shopId"`
|
||
TplRecords []struct {
|
||
Key string `json:"key"`
|
||
Value string `json:"value"`
|
||
} `json:"tplRecords"`
|
||
} `json:"list"`
|
||
} `json:"itemResponse"`
|
||
} `json:"data"`
|
||
}
|
||
// 解析JSON
|
||
if err := json.Unmarshal([]byte(bodySpt), &apiSptResp); err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %w", err)
|
||
}
|
||
if apiSptResp.ErrType == "102" {
|
||
return nil, fmt.Errorf("错误信息: %w,状态码: %s", apiSptResp.Message, apiSptResp.ErrType)
|
||
}
|
||
if apiSptResp.Data.ItemResponse.Total > 0 && len(apiSptResp.Data.ItemResponse.List) > 0 {
|
||
// 确定其实索引
|
||
var startIndex int
|
||
if size >= apiSptResp.Data.ItemResponse.Total {
|
||
startIndex = apiSptResp.Data.ItemResponse.Total - 1
|
||
} else {
|
||
startIndex = size - 1
|
||
}
|
||
// 尝试3次获取有效的图片
|
||
for attempt := 0; attempt < 3; attempt++ {
|
||
currentIndex := startIndex - attempt
|
||
// 检查索引是否有效
|
||
if currentIndex < 0 || currentIndex >= len(apiSptResp.Data.ItemResponse.List) {
|
||
log.Printf("[DEBUG] 索引 %d 超出范围,跳过", currentIndex)
|
||
continue
|
||
}
|
||
// 随机选择商品
|
||
randomNum := rand.Intn(currentIndex + 1)
|
||
item := apiSptResp.Data.ItemResponse.List[randomNum]
|
||
// 检查图片URL是否存在
|
||
if item.ImgBigUrl == "" {
|
||
log.Printf("[DEBUG] 索引 %d 的图片URL为空,跳过", randomNum)
|
||
continue
|
||
}
|
||
// 填充图书信息
|
||
bookInfo.BookPicS = item.ImgUrl
|
||
bookInfo.ISBN = item.Isbn
|
||
// 如果书名为空,使用商品标题
|
||
if bookInfo.BookName == "" {
|
||
bookInfo.BookName = item.Title
|
||
if isReturnMsg == 0 {
|
||
// 安全地获取TplRecords中的值
|
||
if len(item.TplRecords) > 0 {
|
||
bookInfo.Author = item.TplRecords[0].Value
|
||
}
|
||
if len(item.TplRecords) > 1 {
|
||
bookInfo.Publisher = item.TplRecords[1].Value
|
||
}
|
||
if len(item.TplRecords) > 2 {
|
||
bookInfo.PublicationTime = validateDateFormat(item.TplRecords[2].Value)
|
||
}
|
||
if len(item.TplRecords) > 3 {
|
||
bookInfo.BindingLayout = item.TplRecords[3].Value
|
||
}
|
||
}
|
||
}
|
||
return bookInfo, nil
|
||
}
|
||
}
|
||
}
|
||
return nil, fmt.Errorf("查询失败,没有数据!")
|
||
}
|
||
|
||
/*
|
||
* 获取商品列表通过店铺ID
|
||
* param shopId[int] 店铺ID
|
||
* param proxy[string] 代理服务器IP
|
||
* param retPrice[int] 是否需要价格
|
||
* param sortType[string] 排序类型
|
||
* param sort[string] 排序
|
||
* param priceMin[float32] 价格区间-低
|
||
* param priceMax[float32] 价格区间-高
|
||
* param pageNum[int] 页数
|
||
* param returnNum[int] 返回数量
|
||
* return 商品列表响应信息结构体,商品总数,总页数,错误信息
|
||
* Error 无效的排序类型: %s,可选值: sort, putDate, newItem, price
|
||
* Error 无效的排序类型: %s,可选值: desc, asc
|
||
*/
|
||
func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage int, sortType string,
|
||
sort string, priceMin float32, priceMax float32,
|
||
pageNum, returnNum int) (books []BookInfo, goodsNum string, pNum string, err error) {
|
||
// 判断店铺ID
|
||
if shopId == 0 {
|
||
return nil, "", "", fmt.Errorf("店铺编码为空!")
|
||
}
|
||
// 判断是否有图片,设置默认值0
|
||
var isImageStr string
|
||
if isImage == 0 {
|
||
isImageStr = "0"
|
||
} else {
|
||
isImageStr = "1"
|
||
}
|
||
// 判断一页图书数量,设置默认值100
|
||
if returnNum == 0 {
|
||
returnNum = 100
|
||
}
|
||
// 判断页数,设置默认值1
|
||
if pageNum == 0 {
|
||
pageNum = 1
|
||
}
|
||
// 判断排序类型,设置默认值sort
|
||
if sortType == "" {
|
||
sortType = "sort"
|
||
} else {
|
||
validSortTypes := map[string]bool{
|
||
"sort": true,
|
||
"putDate": true,
|
||
"newItem": true,
|
||
"price": true,
|
||
}
|
||
if !validSortTypes[sortType] {
|
||
return nil, "", "", fmt.Errorf("无效的排序类型: %s,可选值: sort, putDate, newItem, price", sortType)
|
||
}
|
||
}
|
||
// 判断排序,设置默认值desc
|
||
if sort == "" {
|
||
sort = "desc"
|
||
} else {
|
||
validSorts := map[string]bool{
|
||
"desc": true,
|
||
"asc": true,
|
||
}
|
||
if !validSorts[sort] {
|
||
return nil, "", "", fmt.Errorf("无效的排序类型: %s,可选值: desc, asc", sort)
|
||
}
|
||
}
|
||
var url string
|
||
var pMin int
|
||
pMin = 0
|
||
var pMax int
|
||
pMax = 0
|
||
// 判断价格下限,设置默认值0
|
||
if priceMin == 0 && priceMax == 0 {
|
||
// 调用的url
|
||
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%d",
|
||
shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, pMax)
|
||
} else if priceMin == 0 {
|
||
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%.2f",
|
||
shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, priceMax)
|
||
} else if priceMax == 0 {
|
||
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%d",
|
||
shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, pMax)
|
||
} else {
|
||
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%.2f",
|
||
shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, priceMax)
|
||
}
|
||
// 发送请求
|
||
response, err := fetchResponse(url, proxy)
|
||
if err != nil {
|
||
return nil, "", "", err
|
||
}
|
||
// 读取响应体
|
||
body, err := io.ReadAll(response.Body)
|
||
if err != nil {
|
||
return nil, "", "", err
|
||
}
|
||
// 解析HTML文档
|
||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
|
||
// 全部商品数量
|
||
num := doc.Find("div.crumbs-nav-main.clearfix").Find("span")
|
||
if match := regexp.MustCompile(`\d+`).FindString(num.Text()); match != "" {
|
||
goodsNum = match
|
||
}
|
||
// 商品页数
|
||
pg := doc.Find("li.pull-right.page_num").Find("span")
|
||
_, split, found := strings.Cut(strings.TrimSpace(pg.Text()), "/")
|
||
if found {
|
||
pNum = split
|
||
} else {
|
||
log.Printf("未找到页数!")
|
||
}
|
||
// 提取商品信息
|
||
infoDiv := doc.Find("div.list-content")
|
||
var params ParamsInfo
|
||
if infoDiv.Length() > 0 {
|
||
item := infoDiv.Find("div.item.clearfix")
|
||
for i := 0; i < item.Length(); i++ {
|
||
s := item.Eq(i)
|
||
book := BookInfo{}
|
||
// 书名
|
||
book.BookName = strings.TrimSpace(s.Find("div.title a.link").Text())
|
||
// 提取ISBN
|
||
book.ISBN = strings.TrimSpace(s.AttrOr("isbn", ""))
|
||
// 店铺ID
|
||
shopid, _ := strconv.Atoi(strings.TrimSpace(s.AttrOr("shopid", "")))
|
||
book.ShopId = int64(shopid)
|
||
// 商品ID
|
||
itemid, _ := strconv.Atoi(strings.TrimSpace(s.AttrOr("itemid", "")))
|
||
book.ItemId = int64(itemid)
|
||
// 详情URL
|
||
book.DetailUrl = s.Find("div.item-img a.img-box").AttrOr("href", "")
|
||
// 价格
|
||
if retPrice == 0 {
|
||
book.SellingPrice = s.Find("div.f_right.red.price").Find("span.bold").Text()
|
||
params.Params = append(params.Params, struct {
|
||
UserId string `json:"userId"`
|
||
ItemId string `json:"itemId"`
|
||
}{UserId: strings.TrimSpace(s.AttrOr("userid", "")), ItemId: strings.TrimSpace(s.AttrOr("itemid", ""))})
|
||
}
|
||
books = append(books, book)
|
||
}
|
||
// 如果需要查询快递费
|
||
if params.Params != nil {
|
||
params.Area = "13003000000"
|
||
dataItem, err := getGoodsListShippingFee(params, proxy)
|
||
if err != nil {
|
||
return nil, "", "", err
|
||
}
|
||
// 将快递费信息填充到图书信息中
|
||
for i := 0; i < len(books); i++ {
|
||
itemId := fmt.Sprintf("%d", books[i].ItemId)
|
||
for _, data := range dataItem {
|
||
if itemId == data.ItemID {
|
||
books[i].ExpressDeliveryFee = data.Fee[0].TotalFee
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return books, goodsNum, pNum, nil
|
||
}
|
||
|
||
/*
|
||
* 获取商品信息通过商品详情链接
|
||
* param detailUrl[string] 详情页url
|
||
* param proxy[string] 代理服务器IP
|
||
* return 商品列表响应信息结构体,错误信息
|
||
* Error 未找到指定的contentSpan信息
|
||
* Error 无效的排序类型: %s,可选值: desc, asc
|
||
*/
|
||
func outGetGoodsMsgByDetailUrl(detailUrl, proxy string) (*BookInfo, error) {
|
||
// 发送请求获取响应
|
||
response, err := fetchResponse(detailUrl, proxy)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 读取响应体
|
||
body, err := io.ReadAll(response.Body)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 解析HTML文档
|
||
document, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
// 获取商品信息的快递费
|
||
fee, err := getBookDetailShippingFee(detailUrl, proxy)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
book := BookInfo{}
|
||
// 书名
|
||
book.BookName = strings.TrimSpace(document.Find("h1.title").Text())
|
||
// 提取作者、出版社等信息
|
||
topDiv := document.Find("div.keywords-define.keywords-define-1000.clear-fix")
|
||
if topDiv.Length() > 0 {
|
||
topDiv.Find("li").Each(func(i int, li *goquery.Selection) {
|
||
titleSpan := li.Find("span.keywords-define-title")
|
||
contentSpan := li.Find("span.keywords-define-txt")
|
||
if contentSpan.Length() == 0 {
|
||
fmt.Printf("未找到指定的contentSpan信息")
|
||
}
|
||
titleText := strings.TrimSpace(titleSpan.Text())
|
||
contentText := strings.TrimSpace(contentSpan.Text())
|
||
titleText = strings.TrimSpace(titleText)
|
||
// 根据标题字段填充对应的图书信息字段
|
||
if strings.Contains(titleText, "作者") {
|
||
book.Author = cleanString(contentText)
|
||
}
|
||
if strings.Contains(titleText, "出版社") {
|
||
book.Publisher = contentText
|
||
}
|
||
if strings.Contains(titleText, "出版人") {
|
||
book.Publisher = contentText
|
||
}
|
||
if strings.Contains(titleText, "ISBN") {
|
||
book.ISBN = contentText
|
||
}
|
||
if strings.Contains(titleText, "出版时间") {
|
||
book.PublicationTime = validateDateFormat(contentText)
|
||
}
|
||
if strings.Contains(titleText, "版次") {
|
||
book.Edition = contentText
|
||
}
|
||
if strings.Contains(titleText, "装帧") {
|
||
book.BindingLayout = contentText
|
||
}
|
||
if strings.Contains(titleText, "开本") {
|
||
book.Format = contentText
|
||
}
|
||
if strings.Contains(titleText, "页数") {
|
||
book.Pages = contentText
|
||
}
|
||
if strings.Contains(titleText, "字数") {
|
||
book.Wordage = contentText
|
||
}
|
||
if strings.Contains(titleText, "纸张") {
|
||
book.Paper = contentText
|
||
}
|
||
if strings.Contains(titleText, "年代") {
|
||
book.Era = contentText
|
||
}
|
||
if strings.Contains(titleText, "刻印方式") {
|
||
book.EngravingMethod = contentText
|
||
}
|
||
if strings.Contains(titleText, "尺寸") {
|
||
book.Dimensions = contentText
|
||
}
|
||
if strings.Contains(titleText, "册数") {
|
||
book.VolumeNumber = contentText
|
||
}
|
||
})
|
||
} else {
|
||
// 备选提取方式
|
||
botDiv := document.Find("div.detail-lists.clear-fix")
|
||
botDiv.Find("li").Each(func(i int, li *goquery.Selection) {
|
||
spanText := strings.TrimSpace(li.Find("span").Text())
|
||
spanText = strings.TrimSpace(spanText)
|
||
if strings.Contains(li.Text(), "作者") {
|
||
book.Author = cleanString(li.Text())
|
||
book.Author = strings.ReplaceAll(book.Author, "作者:", "")
|
||
book.Author = strings.ReplaceAll(book.Author, "著", "")
|
||
}
|
||
if strings.Contains(li.Text(), "出版社") {
|
||
book.Publisher = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "出版时间") {
|
||
book.PublicationTime = validateDateFormat(spanText)
|
||
}
|
||
if strings.Contains(li.Text(), "ISBN") {
|
||
book.ISBN = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "装帧") {
|
||
book.BindingLayout = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "开本") {
|
||
book.Format = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "纸张") {
|
||
book.Paper = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "版次") {
|
||
book.Edition = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "页数") {
|
||
book.Pages = spanText
|
||
}
|
||
if strings.Contains(li.Text(), "字数") {
|
||
book.Wordage = spanText
|
||
}
|
||
})
|
||
}
|
||
|
||
// 提取商品图片
|
||
var imgUrls []string
|
||
tpUl := document.Find("ul.lg-list")
|
||
tpUl.Find("img").Each(func(i int, s *goquery.Selection) {
|
||
dataImgUrl, exists := s.Attr("data-imgurl")
|
||
if exists && dataImgUrl != "" {
|
||
imgUrls = append(imgUrls, dataImgUrl)
|
||
}
|
||
})
|
||
book.BookPicS = strings.Join(imgUrls, ",")
|
||
// 提取售价
|
||
price := document.Find("i.now-price-text").Text()
|
||
priceN := regexp.MustCompile(`(\d+\.?\d*)`)
|
||
if match := priceN.FindStringSubmatch(price); len(match) > 0 {
|
||
book.SellingPrice = match[1]
|
||
}
|
||
// 提取定价
|
||
fixPrice := document.Find("span.origin-price-text.clearfix").Text()
|
||
fixPriceN := regexp.MustCompile(`(\d+\.?\d*)`)
|
||
if match := fixPriceN.FindStringSubmatch(fixPrice); len(match) > 0 {
|
||
book.FixPrice = match[1]
|
||
}
|
||
// 提取品相
|
||
text := document.Find("span.quality-text-cot.clearfix i").Text()
|
||
book.Condition = strings.TrimSpace(text)
|
||
// 设置快递费
|
||
book.ExpressDeliveryFee = fee
|
||
return &book, nil
|
||
}
|
||
|
||
/*
|
||
* 获取商品信息的快递费(定位到河南)
|
||
* param url[string] 获取快递费url
|
||
* param proxy[string] 代理服务器IP
|
||
* return 商品列表响应信息结构体,错误信息
|
||
* Error 无效的店铺编码: %s
|
||
* Error 无效的图书编码: %s
|
||
* Error 请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
*/
|
||
func getBookDetailShippingFee(url, proxy string) (string, error) {
|
||
// 从URL中提取店铺ID和商品ID
|
||
compile := regexp.MustCompile(`kongfz\.com/(\d+)/(\d+)`)
|
||
match := compile.FindStringSubmatch(url)
|
||
var shippingFee string
|
||
var shopId int
|
||
var itemId int
|
||
if len(match) == 3 {
|
||
// 提取店铺ID
|
||
firstNum, err := strconv.Atoi(match[1])
|
||
if err != nil {
|
||
return "", fmt.Errorf("无效的店铺编码: %s", match[1])
|
||
}
|
||
shopId = firstNum
|
||
// 提取商品ID
|
||
secondNum, err := strconv.Atoi(match[2])
|
||
if err != nil {
|
||
return "", fmt.Errorf("无效的图书编码: %s", match[2])
|
||
}
|
||
itemId = secondNum
|
||
}
|
||
// 构建快递费查询URL
|
||
shippingFeeUrl := fmt.Sprintf("https://book.kongfz.com/store-web/pc/v1/mould/calculateFee?area=13003000000&itemId=%d&shopId=%d", itemId, shopId)
|
||
// 创建HTTP客户端
|
||
request := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
// 设置超时和其他配置
|
||
request.Timeout(30 * time.Second)
|
||
// 发送请求
|
||
resp, body, errs := request.Get(shippingFeeUrl).
|
||
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").
|
||
End()
|
||
// 错误处理
|
||
if len(errs) > 0 {
|
||
return "", fmt.Errorf("请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
calculateFee := struct {
|
||
ErrCode int `json:"errCode"`
|
||
ErrMessage string `json:"errMessage"`
|
||
Result struct {
|
||
FeeList []struct {
|
||
FreeCondition string `json:"freeCondition"`
|
||
ShippingID string `json:"shippingId"`
|
||
ShippingName string `json:"shippingName"`
|
||
ShippingValue string `json:"shippingValue"`
|
||
} `json:"feeList"`
|
||
FeeText string `json:"feeText"`
|
||
} `json:"result"`
|
||
Status bool `json:"status"`
|
||
}{}
|
||
// 解析JSON
|
||
err := json.Unmarshal([]byte(body), &calculateFee)
|
||
if err != nil {
|
||
return "", fmt.Errorf("解析JSON失败: %v", err)
|
||
}
|
||
// 提取快递费
|
||
for _, fee := range calculateFee.Result.FeeList {
|
||
shippingFee = fee.ShippingValue
|
||
}
|
||
return shippingFee, nil
|
||
}
|
||
|
||
/*
|
||
* 公用发送请求方法
|
||
* param url[string] url
|
||
* param proxy[string] 代理服务器IP
|
||
* return 商品列表响应信息结构体,错误信息
|
||
* Error 请求失败
|
||
* Error 响应为空 (尝试 %d/%d)
|
||
* Error HTTP状态码: %d,HTTP请求失败: %s (尝试 %d/%d)
|
||
* Error 代理认证失败
|
||
* Error 请求超时,经过 %d 次尝试,超时网址:%s
|
||
* Error 网络连接错误,经过 %d 次尝试,错误网址:%s
|
||
* Error 查询请求失败,经过 %d 次尝试: %v,失败网址:%s
|
||
* Error HTTP错误
|
||
*/
|
||
func fetchResponse(url, proxy string) (*http.Response, error) {
|
||
log.Printf("调用的URL: %s", url)
|
||
maxRetries := 3
|
||
var detailsResp *http.Response
|
||
var errors []error
|
||
// 重试机制
|
||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||
if attempt > 0 {
|
||
log.Printf("第 %d 次重试请求...", attempt)
|
||
// 重试前等待,使用指数退避策略
|
||
waitTime := time.Duration(attempt*attempt) * 1000 // 平方退避
|
||
log.Printf("等待 %v 后重试", waitTime)
|
||
time.Sleep(waitTime)
|
||
}
|
||
// 根据是否有代理发送请求
|
||
if proxy != "" {
|
||
detailsResp, _, errors = gorequest.New().
|
||
Get(url).
|
||
Proxy(proxy).
|
||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
|
||
Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8").
|
||
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
|
||
Timeout(120 * time.Second).
|
||
End()
|
||
}
|
||
if proxy == "" {
|
||
detailsResp, _, errors = gorequest.New().
|
||
Get(url).
|
||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
|
||
Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8").
|
||
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
|
||
Timeout(120 * time.Second).
|
||
End()
|
||
}
|
||
// 检查是否需要重试
|
||
shouldRetry := false
|
||
if len(errors) > 0 {
|
||
shouldRetry = true
|
||
log.Printf("请求失败 (尝试 %d/%d): %v", attempt+1, maxRetries+1, errors)
|
||
} else if detailsResp == nil {
|
||
shouldRetry = true
|
||
log.Printf("响应为空 (尝试 %d/%d)", attempt+1, maxRetries+1)
|
||
} else if detailsResp.StatusCode != http.StatusOK {
|
||
// 只对服务器错误进行重试,不对客户端错误重试
|
||
if detailsResp.StatusCode >= 500 {
|
||
shouldRetry = true
|
||
}
|
||
log.Printf("HTTP状态码: %d,HTTP请求失败: %s (尝试 %d/%d)", detailsResp.StatusCode, detailsResp.Body, attempt+1, maxRetries+1)
|
||
}
|
||
// 如果不需要重试,跳出循环
|
||
if !shouldRetry {
|
||
break
|
||
}
|
||
// 如果是最后一次尝试,不继续重试
|
||
if attempt == maxRetries {
|
||
break
|
||
}
|
||
// 关闭响应体(如果存在)
|
||
if detailsResp != nil && detailsResp.Body != nil {
|
||
detailsResp.Body.Close()
|
||
}
|
||
}
|
||
// 检测请求是否错误
|
||
if len(errors) > 0 {
|
||
var proxyAuthFailed bool
|
||
var timeoutError bool
|
||
var connectionError bool
|
||
// 分析错误类型
|
||
for _, e := range errors {
|
||
errStr := e.Error()
|
||
if strings.Contains(errStr, "Proxy Authentication Required") {
|
||
proxyAuthFailed = true
|
||
}
|
||
if strings.Contains(errStr, "timeout") || strings.Contains(errStr, "i/o timeout") {
|
||
timeoutError = true
|
||
}
|
||
if strings.Contains(errStr, "connection") || strings.Contains(errStr, "connect") {
|
||
connectionError = true
|
||
}
|
||
}
|
||
// 根据错误类型返回相应的错误信息
|
||
if proxyAuthFailed {
|
||
return nil, fmt.Errorf("代理认证失败")
|
||
}
|
||
|
||
if timeoutError {
|
||
return nil, fmt.Errorf("请求超时,经过 %d 次尝试,超时网址:%s", maxRetries+1, url)
|
||
}
|
||
|
||
if connectionError {
|
||
return nil, fmt.Errorf("网络连接错误,经过 %d 次尝试,错误网址:%s", maxRetries+1, url)
|
||
}
|
||
return nil, fmt.Errorf("查询请求失败,经过 %d 次尝试: %v,失败网址:%s", maxRetries+1, errors, url)
|
||
}
|
||
// 检查HTTP状态码
|
||
if detailsResp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", detailsResp.Status)
|
||
}
|
||
return detailsResp, nil
|
||
}
|
||
|
||
/*
|
||
* 获取销量榜商品列表
|
||
* param catId[int] 分类ID
|
||
* param proxy[string] 代理服务器IP
|
||
* return isbn数组,错误信息
|
||
* Error 请求失败
|
||
* Error HTTP错误
|
||
* Error 解析JSON失败
|
||
* Error API返回错误: %s (代码: %d)
|
||
*/
|
||
func outGetTopGoodsListMsg(catId int, proxy string) ([]string, error) {
|
||
// 构建请求URL
|
||
url := fmt.Sprintf("https://item.kongfz.com/api/pc/getSellWellListDetail?page=1&pageSize=100&timeRank=2&catId=%d", catId)
|
||
// 创建HTTP客户端
|
||
request := gorequest.New()
|
||
// 设置代理(如果有提供代理URL)
|
||
if proxy != "" {
|
||
request.Proxy(proxy)
|
||
}
|
||
// 设置超时和其他配置
|
||
request.Timeout(30 * time.Second)
|
||
// 发送GET请求
|
||
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").
|
||
Set("Referer", "https://item.kongfz.com/").
|
||
End()
|
||
// 错误处理
|
||
if len(errs) > 0 {
|
||
return nil, fmt.Errorf("请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||
}
|
||
// 解析响应
|
||
var bookDetailResponse BookDetailResponse
|
||
err := json.Unmarshal([]byte(body), &bookDetailResponse)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("解析JSON失败: %v", err)
|
||
}
|
||
|
||
// 检查响应状态
|
||
if !bookDetailResponse.Status {
|
||
return nil, fmt.Errorf("API返回错误: %s (代码: %d)", bookDetailResponse.ErrMessage, bookDetailResponse.ErrCode)
|
||
}
|
||
|
||
// 提取ISBN列表
|
||
var isbnList []string
|
||
for _, item := range bookDetailResponse.Result.Data {
|
||
if item.Isbn != "" {
|
||
isbnList = append(isbnList, item.Isbn)
|
||
}
|
||
}
|
||
// 去除重复的ISBN
|
||
isbnList = removeDuplicateISBNs(isbnList)
|
||
return isbnList, nil
|
||
}
|
||
|
||
/*
|
||
* 去除重复的ISBN
|
||
* param isbns[[]string] isbn数组
|
||
* return isbn数组
|
||
*/
|
||
func removeDuplicateISBNs(isbns []string) []string {
|
||
seen := make(map[string]bool)
|
||
var result []string
|
||
for _, isbn := range isbns {
|
||
if !seen[isbn] {
|
||
seen[isbn] = true
|
||
result = append(result, isbn)
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
/*
|
||
* 生成签名
|
||
* param params[map[string]interface{}] 生成签名的字段map
|
||
* param appSecret[string] app密钥
|
||
* return 签名
|
||
*/
|
||
func generateSign(params map[string]interface{}, appSecret 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)
|
||
}
|
||
|
||
// 根据签名方法生成签名
|
||
signMethod := "md5"
|
||
if method, ok := params["signMethod"].(string); ok {
|
||
signMethod = strings.ToLower(method)
|
||
}
|
||
|
||
signString := signStr.String()
|
||
|
||
// 使用MD5算法生成签名
|
||
if signMethod == "md5" {
|
||
// MD5算法:md5(appSecret + signString + appSecret)
|
||
data := appSecret + signString + appSecret
|
||
hash := md5.Sum([]byte(data))
|
||
result := strings.ToUpper(fmt.Sprintf("%x", hash))
|
||
//fmt.Printf("Debug: MD5签名结果: %s\n", result)
|
||
return result
|
||
}
|
||
|
||
return ""
|
||
}
|
||
|
||
// KwAPIResponse 孔网API响应结构体
|
||
type KwAPIResponse struct {
|
||
ErrorResponse *ErrorResponse `json:"errorResponse"` // 错误响应
|
||
SuccessResponse []ShippingMethod `json:"successResponse"` // 成功响应
|
||
RequestId string `json:"requestId"` // 请求ID
|
||
RequestMethod string `json:"requestMethod"` // 请求方法
|
||
}
|
||
|
||
// ErrorResponse 孔网API错误响应结构体
|
||
type ErrorResponse struct {
|
||
Code int `json:"code"` // 错误码
|
||
Msg string `json:"msg"` // 错误信息
|
||
SubCode *int `json:"subCode"` // 使用指针类型,因为可能是null
|
||
SubMsg *string `json:"subMsg"` // 使用指针类型,因为可能是null
|
||
}
|
||
|
||
type ShippingMethod struct {
|
||
ShippingId string `json:"shippingId"` // 配送方式ID
|
||
ShippingName string `json:"shippingName"` // 配送方式名称
|
||
IsDefault bool `json:"isDefault"` // 是否默认配送方式
|
||
Companies []Company `json:"companies"` // 快递公司列表
|
||
}
|
||
|
||
type Company struct {
|
||
ShippingCom string `json:"shippingCom"` // 快递公司代号
|
||
ShippingComName string `json:"shippingComName"` // 快递公司名称
|
||
IsDefault bool `json:"isDefault"` // 是否默认
|
||
}
|
||
|
||
/*
|
||
* 获取配送方式列表
|
||
* param appId[int] appkey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* return 配送方式列表结构体字符串,错误信息
|
||
*/
|
||
func kongfzDeliveryMethodList(appId int, appSecret, accessToken string) (string, error) {
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/rest")
|
||
dateTime := getCurrentTimeGMT8()
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.delivery.method.list",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
}
|
||
// 生成签名
|
||
sign := generateSign(params, appSecret)
|
||
|
||
// 构建请求体
|
||
formData := map[string]interface{}{
|
||
"method": "kongfz.delivery.method.list",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"sign": sign,
|
||
"simplify": 0,
|
||
}
|
||
|
||
// 发送POST请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(kUrl).
|
||
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", "*/*").
|
||
Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").
|
||
Type("form"). // 关键:明确指定为表单格式
|
||
Timeout(30 * time.Second).
|
||
Send(formData).
|
||
End()
|
||
// 错误处理
|
||
if len(errs) > 0 {
|
||
return "", fmt.Errorf("登录请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
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["ErrorResponse"] != nil {
|
||
var kwAPIResponse KwAPIResponse
|
||
if err := json.Unmarshal([]byte(body), &kwAPIResponse); err != nil {
|
||
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
return "", fmt.Errorf("请求失败: %v, 错误码: %d", kwAPIResponse.ErrorResponse.Msg, kwAPIResponse.ErrorResponse.Code)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
/*
|
||
* 订单发货
|
||
* param appId[int] appkey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param orderId[int] 订单编号
|
||
* param shippingId[string] 配送方式
|
||
* param shippingCom[string] 快递公司。当shippingId!=noLogistics时,此参数为必填。
|
||
* param shipmentNum[string] 快递单号。当shippingId!=noLogistics时,此参数为必填。
|
||
* param userDefined[string] 用户自定义物流公司。当shippingCom=other时,此参数为必填。
|
||
* param moreShipmentNum[string] 填写更多的快递单号,以逗号分隔。
|
||
* return 订单发货结构体字符串,错误信息
|
||
*/
|
||
func kongfzOrderDeliver(appId int, appSecret, accessToken string,
|
||
orderId int, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum string) (string, error) {
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/rest")
|
||
dateTime := getCurrentTimeGMT8()
|
||
// 参数验证
|
||
if shippingId != "noLogistics" {
|
||
if shippingCom == "" {
|
||
return "", fmt.Errorf("当 shippingId 不等于 noLogistics 时,shippingCom 参数为必填。shippingId是: %v", shippingId)
|
||
}
|
||
if shipmentNum == "" {
|
||
return "", fmt.Errorf("当 shippingId 不等于 noLogistics 时, shipmentNum 参数为必填。shippingId是: %v", shippingId)
|
||
}
|
||
}
|
||
if shipmentNum == "other" {
|
||
if userDefined == "" {
|
||
return "", fmt.Errorf("当 shippingCom 等于 other 时, userDefined 参数为必填。shipmentNum是: %v", shipmentNum)
|
||
}
|
||
}
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.order.deliver",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
"orderId": orderId,
|
||
"shippingId": shippingId,
|
||
"shippingCom": shippingCom,
|
||
"shipmentNum": shipmentNum,
|
||
"userDefined": userDefined,
|
||
"moreShipmentNum": moreShipmentNum,
|
||
}
|
||
// 生成签名
|
||
sign := generateSign(params, appSecret)
|
||
|
||
// 构建请求体
|
||
formData := map[string]interface{}{
|
||
"method": "kongfz.order.deliver",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"sign": sign,
|
||
"simplify": 0,
|
||
"orderId": orderId,
|
||
"shippingId": shippingId,
|
||
"shippingCom": shippingCom,
|
||
"shipmentNum": shipmentNum,
|
||
"userDefined": userDefined,
|
||
"moreShipmentNum": moreShipmentNum,
|
||
}
|
||
// 发送POST请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(kUrl).
|
||
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", "*/*").
|
||
Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").
|
||
Type("form"). // 关键:明确指定为表单格式
|
||
Timeout(30 * time.Second).
|
||
Send(formData).
|
||
End()
|
||
// 错误处理
|
||
if len(errs) > 0 {
|
||
return "", fmt.Errorf("登录请求失败: %v", errs)
|
||
}
|
||
// 检查HTTP状态码
|
||
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["ErrorResponse"] != nil {
|
||
var kwAPIResponse KwAPIResponse
|
||
if err := json.Unmarshal([]byte(body), &kwAPIResponse); err != nil {
|
||
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
return "", fmt.Errorf("请求失败: %v, 错误码: %d", kwAPIResponse.ErrorResponse.Msg, kwAPIResponse.ErrorResponse.Code)
|
||
}
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
/*
|
||
* 孔网订单同步
|
||
* param appId[int] appkey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param shippingComName[string] 快递公司名称
|
||
* param orderId[int] 订单编号
|
||
* param shippingId[string] 配送方式
|
||
* param shippingCom[string] 快递公司。当shippingId!=noLogistics时,此参数为必填。
|
||
* param shipmentNum[string] 快递单号。当shippingId!=noLogistics时,此参数为必填。
|
||
* param userDefined[string] 用户自定义物流公司。当shippingCom=other时,此参数为必填。
|
||
* param moreShipmentNum[string] 填写更多的快递单号,以逗号分隔。
|
||
* return 订单同步结构体字符串,错误信息
|
||
*/
|
||
func kongfzOrderSynchronization(appId int, appSecret, accessToken string, shippingComName string,
|
||
orderId int, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum string) (string, error) {
|
||
// 获取配送方式列表
|
||
deliveryMethodList, err := kongfzDeliveryMethodList(appId, appSecret, accessToken)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
// 解析配送方式
|
||
var kw KwAPIResponse
|
||
if err := json.Unmarshal([]byte(deliveryMethodList), &kw); err != nil {
|
||
return "", fmt.Errorf("解析JSON失败: %v", err)
|
||
}
|
||
// 根据快递公司名称查找对应的配送方式和快递公司代号
|
||
for _, shippingMethod := range kw.SuccessResponse {
|
||
for _, companies := range shippingMethod.Companies {
|
||
if shippingComName == companies.ShippingComName {
|
||
shippingId = shippingMethod.ShippingId
|
||
shippingCom = companies.ShippingCom
|
||
}
|
||
}
|
||
}
|
||
// 如果未找到,使用默认值
|
||
if shippingId == "" {
|
||
shippingId = "express"
|
||
shippingCom = "other"
|
||
userDefined = shippingComName
|
||
}
|
||
// 执行订单发货
|
||
orderDeliver, err := kongfzOrderDeliver(appId, appSecret, accessToken, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return orderDeliver, err
|
||
}
|
||
|
||
/*
|
||
* 上传图片接口
|
||
* param appId[int] appKey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param filePath[string] 图片url/本地图片路径
|
||
* param savePath[string] 图片下载路径,如果是图片url需要下载到本地
|
||
* return 上传图片响应结构体,错误信息
|
||
* Error 解析图片URL失败
|
||
* Error 读取文件失败
|
||
* Error 创建表单文件失败
|
||
* Error 写入文件数据失败
|
||
*/
|
||
func kongfzImageUpload(appId int, appSecret, accessToken string, filePath, savePath string) (string, error) {
|
||
var image string
|
||
var needCleanup bool = false // 标记是否需要清理
|
||
defer func() {
|
||
// 函数结束时删除临时图片
|
||
if needCleanup && image != "" {
|
||
if err := os.Remove(image); err != nil {
|
||
log.Printf("警告:删除临时图片失败 %s: %v", image, err)
|
||
} else {
|
||
log.Printf("已清理临时图片: %s", image)
|
||
}
|
||
}
|
||
}()
|
||
if strings.HasPrefix(filePath, "http://") || strings.HasPrefix(filePath, "https://") {
|
||
// 解析URL
|
||
parse, err := url.Parse(filePath)
|
||
if err != nil {
|
||
return "", fmt.Errorf("解析图片URL失败: %v", err)
|
||
}
|
||
// 获取路径部分
|
||
fPath := parse.Path
|
||
// 获取文件名
|
||
fileName := path.Base(fPath)
|
||
savePath = filepath.Join(savePath, fileName)
|
||
image, err = downloadImage(filePath, savePath)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
needCleanup = true // 标记需要清理
|
||
} else {
|
||
// 本地文件
|
||
image = filePath
|
||
// 本地文件不需要清理,由调用者管理
|
||
}
|
||
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/image/upload")
|
||
dateTime := getCurrentTimeGMT8()
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.image.upload",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
}
|
||
params["bucket"] = "book"
|
||
// 生成签名
|
||
sign := generateSign(params, appSecret)
|
||
|
||
// 创建multipart/form-data请求体
|
||
body := &bytes.Buffer{}
|
||
writer := multipart.NewWriter(body)
|
||
// 读取文件内容
|
||
fileData, err := os.ReadFile(image)
|
||
if err != nil {
|
||
return "", fmt.Errorf("读取文件失败: %v", err)
|
||
}
|
||
// 添加文件字段
|
||
part, err := writer.CreateFormFile("image", filepath.Base(image))
|
||
if err != nil {
|
||
return "", fmt.Errorf("创建表单文件失败: %v", err)
|
||
}
|
||
_, err = part.Write(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(kUrl).
|
||
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)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
/*
|
||
* 添加店铺商品
|
||
* param appId[int] appKey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param shopItemAddJson[string] 店铺商品请求结构体字符串
|
||
* return 添加店铺商品响应结构体,错误信息
|
||
*/
|
||
func kongfzShopItemAdd(appId int, appSecret, accessToken string, shopItemAddJson string) (string, error) {
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/rest")
|
||
dateTime := getCurrentTimeGMT8()
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.shop.item.add",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
}
|
||
|
||
if shopItemAddJson == "" {
|
||
return "", fmt.Errorf("shopItemAddJson 参数为空!")
|
||
}
|
||
toParams, err := addStructToParams(shopItemAddJson, params)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
// 生成签名
|
||
sign := generateSign(toParams, appSecret)
|
||
if sign == "" {
|
||
return "", fmt.Errorf("生成 sign 签名错误!")
|
||
}
|
||
params["sign"] = sign
|
||
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Get(kUrl).
|
||
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)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
/*
|
||
* 查询订单列表
|
||
* param appId[int] appKey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param orderListJson[string] 查询订单请求结构体字符串
|
||
* return 添加店铺商品响应结构体,错误信息
|
||
*/
|
||
func kongfzOrderList(appId int, appSecret, accessToken string, orderListJson string) (string, error) {
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/rest")
|
||
dateTime := getCurrentTimeGMT8()
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.order.list",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
}
|
||
if orderListJson == "" {
|
||
return "", fmt.Errorf("orderListJson 参数为空!")
|
||
}
|
||
toParams, err := addStructToParams(orderListJson, params)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
// 生成签名
|
||
sign := generateSign(toParams, appSecret)
|
||
if sign == "" {
|
||
return "", fmt.Errorf("生成 sign 签名错误!")
|
||
}
|
||
params["sign"] = sign
|
||
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(kUrl).
|
||
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").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
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)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
/*
|
||
* 查询单个订单
|
||
* param appId[int] appKey
|
||
* param appSecret[string] app密钥
|
||
* param accessToken[string] 访问token
|
||
* param userType[string] 查询订单请求结构体字符串
|
||
* param orderId[string] 查询订单请求结构体字符串
|
||
* return 添加店铺商品响应结构体,错误信息
|
||
*/
|
||
func kongfzOrderGet(appId int, appSecret, accessToken string, userType string, orderId int) (string, error) {
|
||
kUrl := fmt.Sprint("https://open.kongfz.com/router/rest")
|
||
dateTime := getCurrentTimeGMT8()
|
||
|
||
// 生成签名参数
|
||
params := map[string]interface{}{
|
||
"method": "kongfz.order.get",
|
||
"appId": appId,
|
||
"accessToken": accessToken,
|
||
"datetime": dateTime,
|
||
"format": "json",
|
||
"v": "1.0",
|
||
"signMethod": "md5",
|
||
"simplify": 0,
|
||
}
|
||
params["userType"] = userType
|
||
params["orderId"] = strconv.Itoa(orderId)
|
||
// 生成签名
|
||
sign := generateSign(params, appSecret)
|
||
if sign == "" {
|
||
return "", fmt.Errorf("生成 sign 签名错误!")
|
||
}
|
||
params["sign"] = sign
|
||
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(kUrl).
|
||
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").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
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)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// =============== 辅助函数 ==============
|
||
/*
|
||
* 将结构体的字段添加到 params 映射中
|
||
* param req[string] 需要合并的json字符串
|
||
* param params[map[string]interface{}] 合并的map
|
||
* return map响应结构体,错误信息
|
||
*/
|
||
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
|
||
}
|
||
|
||
/*
|
||
* 下载图片到本地
|
||
* param filePath[string] 文件路径
|
||
* param savePath[string] 需要保存的路径
|
||
* return 本地图片路径,错误信息
|
||
*/
|
||
func downloadImage(filePath, savePath string) (string, error) {
|
||
// 创建HTTP请求
|
||
request := gorequest.New()
|
||
resp, body, err := request.Get(filePath).
|
||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
|
||
EndBytes()
|
||
if err != nil {
|
||
return "", fmt.Errorf("请求失败: %v", err)
|
||
}
|
||
if resp.StatusCode != 200 {
|
||
return "", fmt.Errorf("HTTP 状态码错误: %d", resp.StatusCode)
|
||
}
|
||
errs := os.WriteFile(savePath, body, 0644)
|
||
if errs != nil {
|
||
return "", fmt.Errorf("保存文件失败: %v", errs)
|
||
}
|
||
return savePath, nil
|
||
}
|
||
|
||
/*
|
||
* 获取GMT+8当前时间的字符串格式
|
||
* return 返回时间字符串,格式:2006-01-02 15:04:05
|
||
*/
|
||
func getCurrentTimeGMT8() string {
|
||
// 创建北京时间(GMT+8)
|
||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||
if err != nil {
|
||
loc = time.FixedZone("GMT+8", 8*60*60)
|
||
}
|
||
now := time.Now().In(loc)
|
||
return now.Format("2006-01-02 15:04:05")
|
||
}
|
||
|
||
/*
|
||
* 替换所有空白字符为空格
|
||
* return 返回字符串
|
||
*/
|
||
func cleanString(s string) string {
|
||
s = strings.ReplaceAll(s, "\n", "")
|
||
s = strings.ReplaceAll(s, "\r", "")
|
||
s = strings.ReplaceAll(s, "\t", "")
|
||
s = strings.ReplaceAll(s, " ", "")
|
||
return removeDuplicates(s)
|
||
}
|
||
|
||
/*
|
||
* 字符串去重
|
||
* return 返回字符串
|
||
*/
|
||
func removeDuplicates(s string) string {
|
||
seen := make(map[rune]bool)
|
||
var result strings.Builder
|
||
for _, r := range s {
|
||
if !seen[r] {
|
||
seen[r] = true
|
||
result.WriteRune(r)
|
||
}
|
||
}
|
||
return result.String()
|
||
}
|
||
|
||
/*
|
||
* 验证日期格式并转换为时间戳
|
||
* param dateStr[string] 时间字符串
|
||
* return 时间戳
|
||
*/
|
||
func validateDateFormat(dateStr string) int64 {
|
||
// 去除前后空格
|
||
dateStr = strings.TrimSpace(dateStr)
|
||
|
||
// 替换各种分隔符为统一的分隔符"-"
|
||
dateStr = regexp.MustCompile(`[/_\\.,\s]+`).ReplaceAllString(dateStr, "-")
|
||
|
||
// 处理纯年份格式 (4位数字)
|
||
if regexp.MustCompile(`^\d{4}$`).MatchString(dateStr) {
|
||
dateStr += "-01-01"
|
||
}
|
||
|
||
// 处理年月格式 (4位数字-1或2位数字)
|
||
if matches := regexp.MustCompile(`^(\d{4})-(\d{1,2})$`).FindStringSubmatch(dateStr); len(matches) == 3 {
|
||
year := matches[1]
|
||
month := matches[2]
|
||
if len(month) == 1 {
|
||
month = "0" + month
|
||
}
|
||
if monthNum, _ := strconv.Atoi(month); monthNum >= 1 && monthNum <= 12 {
|
||
dateStr = year + "-" + month + "-01"
|
||
} else {
|
||
return 0
|
||
}
|
||
}
|
||
|
||
// 处理年月日格式 (4位数字-1或2位数字-1或2位数字)
|
||
if matches := regexp.MustCompile(`^(\d{4})-(\d{1,2})-(\d{1,2})`).FindStringSubmatch(dateStr); len(matches) >= 4 {
|
||
year := matches[1]
|
||
month := matches[2]
|
||
day := matches[3]
|
||
|
||
// 标准化月份和日期为两位数
|
||
if len(month) == 1 {
|
||
month = "0" + month
|
||
}
|
||
if len(day) == 1 {
|
||
day = "0" + day
|
||
}
|
||
|
||
dateStr = year + "-" + month + "-" + day
|
||
}
|
||
|
||
// 加载上海时区
|
||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||
if err != nil {
|
||
// 如果加载时区失败,使用UTC+8作为后备方案
|
||
loc = time.FixedZone("CST", 8*60*60)
|
||
}
|
||
|
||
// 尝试解析为标准日期格式,并指定上海时区
|
||
parsedTime, err := time.ParseInLocation("2006-01-02", dateStr, loc)
|
||
if err != nil {
|
||
return 0
|
||
}
|
||
|
||
// 返回秒级时间戳(UTC时间,但解析时已经考虑了时区偏移)
|
||
return parsedTime.Unix()
|
||
}
|
||
|
||
// 初始化配置--暂时没用
|
||
func initializeConfig(config Config) {
|
||
// 设置全局配置
|
||
cf = config
|
||
}
|
||
|
||
// =================== C 导出函数 =======================
|
||
|
||
// OutLogin 孔网登录
|
||
//
|
||
//export OutLogin
|
||
func OutLogin(username, password *C.char) *C.char {
|
||
goUsername := C.GoString(username)
|
||
goPassword := C.GoString(password)
|
||
respToken, err := outLogin(goUsername, goPassword)
|
||
// 构建响应数据
|
||
resp := struct {
|
||
Token string `json:"token"`
|
||
}{
|
||
Token: respToken,
|
||
}
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: resp,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetUserMsg 获取孔网用户信息
|
||
//
|
||
//export OutGetUserMsg
|
||
func OutGetUserMsg(token *C.char) *C.char {
|
||
goToken := C.GoString(token)
|
||
userInfo, err := outGetUserMsg(goToken)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: userInfo,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetGoodsTplMsg 获取商品模版--已登的店铺
|
||
//
|
||
//export OutGetGoodsTplMsg
|
||
func OutGetGoodsTplMsg(token, proxy, itemId *C.char) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goItemId := C.GoString(itemId)
|
||
info, err := outGetGoodsTplMsg(goToken, goProxy, goItemId)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: info,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetGoodsListMsgFromSelfShop 获取商品列表-已登的店铺
|
||
//
|
||
//export OutGetGoodsListMsgFromSelfShop
|
||
func OutGetGoodsListMsgFromSelfShop(token, proxy, itemSn, priceMin, priceMax *C.char, startCreateTime *C.char,
|
||
endCreateTime *C.char, requestType *C.char, isItemSnEqual C.int, page C.int, size C.int) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goItemSn := C.GoString(itemSn)
|
||
goPriceMin := C.GoString(priceMin)
|
||
goPriceMax := C.GoString(priceMax)
|
||
goStartCreateTime := C.GoString(startCreateTime)
|
||
goEndCreateTime := C.GoString(endCreateTime)
|
||
goRequestType := C.GoString(requestType)
|
||
goIsItemSnEqual := int(isItemSnEqual)
|
||
goPage := int(page)
|
||
goSize := int(size)
|
||
info, err := outGetGoodsListMsgFromSelfShop(goToken, goProxy, goItemSn, goPriceMin, goPriceMax, goStartCreateTime, goEndCreateTime, goRequestType, goIsItemSnEqual, goPage, goSize)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: info,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutAddGoods 新增商品-已登的店铺
|
||
//
|
||
//export OutAddGoods
|
||
func OutAddGoods(token, proxy, formData *C.char) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goFormData := C.GoString(formData)
|
||
info, err := outAddGoods(goToken, goProxy, goFormData)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: info,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutAddGoodsAndFile 整合添加商品和获取孔网图片
|
||
//
|
||
//export OutAddGoodsAndFile
|
||
func OutAddGoodsAndFile(token, proxy, filePath, formData *C.char) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goFilePath := C.GoString(filePath)
|
||
goFormData := C.GoString(formData)
|
||
addGoodsAndFile, err := outAddGoodsAndFile(goToken, goProxy, goFilePath, goFormData)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(addGoodsAndFile)
|
||
}
|
||
|
||
// OutDelGoodsFromSelfShop 删除商品-已登的店铺
|
||
//
|
||
//export OutDelGoodsFromSelfShop
|
||
func OutDelGoodsFromSelfShop(token, proxy, itemId *C.char) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goItemId := C.GoString(itemId)
|
||
info, err := outDelGoodsFromSelfShop(goToken, goProxy, goItemId)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: info,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetImageFilterShopId 获取孔网商品图片和信息(官图和拍图)-带有店铺过滤
|
||
//
|
||
//export OutGetImageFilterShopId
|
||
func OutGetImageFilterShopId(token, proxy, isbn *C.char, shopId C.int, isLiveImage C.int, isReturnMsg C.int) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goIsbn := C.GoString(isbn)
|
||
goShopId := int(shopId)
|
||
goIsLiveImage := int(isLiveImage)
|
||
goIsReturnMsg := int(isReturnMsg)
|
||
info, err := outGetImageFilterShopId(goToken, goProxy, goIsbn, goShopId, goIsLiveImage, goIsReturnMsg)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: info,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetImageByIsbn 获取孔网商品图片和信息(官图和拍图)
|
||
//
|
||
//export OutGetImageByIsbn
|
||
func OutGetImageByIsbn(token, proxy, isbn *C.char, isLiveImage C.int, isReturnMsg C.int) *C.char {
|
||
goToken := C.GoString(token)
|
||
goProxy := C.GoString(proxy)
|
||
goIsbn := C.GoString(isbn)
|
||
goIsLiveImage := int(isLiveImage)
|
||
goIsReturnMsg := int(isReturnMsg)
|
||
bookInfo, err := outGetImageByIsbn(goToken, goProxy, goIsbn, goIsLiveImage, goIsReturnMsg)
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: bookInfo,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID
|
||
//
|
||
//export OutGetGoodsListMsgByShopId
|
||
func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, retPrice C.int, isImage C.int, sortType *C.char, sort *C.char, priceMin C.float, priceMax C.float, pageNum, returnNum C.int) *C.char {
|
||
goShopId := int(shopId)
|
||
goProxy := C.GoString(proxy)
|
||
goRetPrice := int(retPrice)
|
||
goIsImage := int(isImage)
|
||
goSortType := C.GoString(sortType)
|
||
goSort := C.GoString(sort)
|
||
goPriceMin := float32(priceMin)
|
||
goPriceMax := float32(priceMax)
|
||
goPageNum := int(pageNum)
|
||
goReturnNum := int(returnNum)
|
||
books, num, pNum, err := outGetGoodsListMsgByShopId(goShopId, goProxy, goRetPrice, goIsImage,
|
||
goSortType, goSort, goPriceMin, goPriceMax, goPageNum, goReturnNum)
|
||
// 构建统一格式的响应
|
||
bookInfo := struct {
|
||
GoodsNum string `json:"goods_num,omitempty"`
|
||
PNum string `json:"pnum,omitempty"`
|
||
BookInfo interface{} `json:"book_info,omitempty"`
|
||
}{
|
||
GoodsNum: num,
|
||
PNum: pNum,
|
||
BookInfo: books,
|
||
}
|
||
// 构建API响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: bookInfo,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetGoodsMsgByDetailUrl 获取商品信息通过商品详情链接
|
||
//
|
||
//export OutGetGoodsMsgByDetailUrl
|
||
func OutGetGoodsMsgByDetailUrl(detailUrl, proxy *C.char) *C.char {
|
||
goDetailUrl := C.GoString(detailUrl)
|
||
goProxy := C.GoString(proxy)
|
||
response, err := outGetGoodsMsgByDetailUrl(goDetailUrl, goProxy)
|
||
// 构建统一格式的响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// OutGetTopGoodsListMsg 获取销量榜商品列表
|
||
//
|
||
//export OutGetTopGoodsListMsg
|
||
func OutGetTopGoodsListMsg(catId C.int, proxy *C.char) *C.char {
|
||
goCatId := int(catId)
|
||
goProxy := C.GoString(proxy)
|
||
response, err := outGetTopGoodsListMsg(goCatId, goProxy)
|
||
// 构建统一格式的响应
|
||
var apiResponse APIResponse
|
||
if err != nil {
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
apiResponse = APIResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, marshalErr := json.Marshal(apiResponse)
|
||
if marshalErr != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
apiResponse = APIResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
|
||
}
|
||
errorJson, _ := json.Marshal(apiResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// KongfzDeliveryMethodList 获取配送方式列表
|
||
//
|
||
//export KongfzDeliveryMethodList
|
||
func KongfzDeliveryMethodList(appId C.int, appSecret, accessToken *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
list, err := kongfzDeliveryMethodList(goAppId, goAppSecret, goAccessToken)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(list)
|
||
}
|
||
|
||
// KongfzOrderDeliver 订单发货
|
||
//
|
||
//export KongfzOrderDeliver
|
||
func KongfzOrderDeliver(appId C.int, appSecret, accessToken *C.char,
|
||
orderId C.int, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goOrderId := int(orderId)
|
||
goShippingId := C.GoString(shippingId)
|
||
goShippingCom := C.GoString(shippingCom)
|
||
goShipmentNum := C.GoString(shipmentNum)
|
||
goUserDefined := C.GoString(userDefined)
|
||
goMoreShipmentNum := C.GoString(moreShipmentNum)
|
||
deliver, err := kongfzOrderDeliver(goAppId, goAppSecret, goAccessToken,
|
||
goOrderId, goShippingId, goShippingCom, goShipmentNum, goUserDefined, goMoreShipmentNum)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(deliver)
|
||
}
|
||
|
||
// KongfzOrderSynchronization 孔网订单同步
|
||
//
|
||
//export KongfzOrderSynchronization
|
||
func KongfzOrderSynchronization(appId C.int, appSecret, accessToken, shippingComName *C.char,
|
||
orderId C.int, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goShippingComName := C.GoString(shippingComName)
|
||
goOrderId := int(orderId)
|
||
goShippingId := C.GoString(shippingId)
|
||
goShippingCom := C.GoString(shippingCom)
|
||
goShipmentNum := C.GoString(shipmentNum)
|
||
goUserDefined := C.GoString(userDefined)
|
||
goMoreShipmentNum := C.GoString(moreShipmentNum)
|
||
synchronization, err := kongfzOrderSynchronization(goAppId, goAppSecret, goAccessToken, goShippingComName,
|
||
goOrderId, goShippingId, goShippingCom, goShipmentNum, goUserDefined, goMoreShipmentNum)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(synchronization)
|
||
}
|
||
|
||
// KongfzImageUpload 上传图片接口
|
||
//
|
||
//export KongfzImageUpload
|
||
func KongfzImageUpload(appId C.int, appSecret, accessToken, filePath, savePath *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goFilePath := C.GoString(filePath)
|
||
goSavePath := C.GoString(savePath)
|
||
upload, err := kongfzImageUpload(goAppId, goAppSecret, goAccessToken, goFilePath, goSavePath)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(upload)
|
||
}
|
||
|
||
// KongfzShopItemAdd 添加店铺商品
|
||
//
|
||
//export KongfzShopItemAdd
|
||
func KongfzShopItemAdd(appId C.int, appSecret, accessToken, shopItemAddJson *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goShopItemAddJson := C.GoString(shopItemAddJson)
|
||
upload, err := kongfzShopItemAdd(goAppId, goAppSecret, goAccessToken, goShopItemAddJson)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(upload)
|
||
}
|
||
|
||
// KongfzOrderList 查询订单列表
|
||
//
|
||
//export KongfzOrderList
|
||
func KongfzOrderList(appId C.int, appSecret, accessToken *C.char, orderListJson *C.char) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goOrderListJson := C.GoString(orderListJson)
|
||
list, err := kongfzOrderList(goAppId, goAppSecret, goAccessToken, goOrderListJson)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(list)
|
||
}
|
||
|
||
// KongfzOrderGet 查询单个订单
|
||
//
|
||
//export KongfzOrderGet
|
||
func KongfzOrderGet(appId C.int, appSecret, accessToken *C.char, userType *C.char, orderId C.int) *C.char {
|
||
goAppId := int(appId)
|
||
goAppSecret := C.GoString(appSecret)
|
||
goAccessToken := C.GoString(accessToken)
|
||
goUserType := C.GoString(userType)
|
||
goOrderId := int(orderId)
|
||
orderGet, err := kongfzOrderGet(goAppId, goAppSecret, goAccessToken, goUserType, goOrderId)
|
||
if err != nil {
|
||
return C.CString(fmt.Sprint(err))
|
||
}
|
||
return C.CString(orderGet)
|
||
}
|
||
|
||
// Initialize 初始化配置
|
||
//
|
||
//export Initialize
|
||
func Initialize(configJSON *C.char) *C.char {
|
||
configStr := C.GoString(configJSON)
|
||
log.Printf("[DEBUG] 接收到的配置JSON: %s", configStr)
|
||
|
||
var config Config
|
||
if err := json.Unmarshal([]byte(configStr), &config); err != nil {
|
||
return C.CString(fmt.Sprintf(`{"success":false,"message":"配置解析失败: %v"}`, err))
|
||
}
|
||
initializeConfig(config)
|
||
|
||
return C.CString(`{"success":true,"message":"初始化成功"}`)
|
||
}
|
||
|
||
// FreeCString 释放C字符串内存
|
||
//
|
||
//export FreeCString
|
||
func FreeCString(str *C.char) {
|
||
C.free(unsafe.Pointer(str))
|
||
}
|
||
|
||
// 空main函数,编译DLL时需要
|
||
func main() {
|
||
}
|