daShangDao_kfzgw-info/kongfz/kongfz.go
2025-12-31 17:56:22 +08:00

2602 lines
84 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

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

package main
/*
#include <stdlib.h>
*/
import "C"
import (
"crypto/md5"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/url"
"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"
// 创建请求
request := gorequest.New()
// 设置代理如果有提供代理URL
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("代理连接失败: %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(body), &data); err != nil {
return nil, fmt.Errorf("解析JSON失败: %w", err)
}
// 检查响应状态
if val, ok := data["status"]; ok && val == 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 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
}
// 获取孔网商品图片和信息(官图和拍图)
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
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
}
// 获取商品信息通过商品详情链接
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
}
// 获取商品信息的快递费(定位到河南)
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
}
// 公用发送请求方法
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
}
// 获取销量榜商品列表(带有Out的都非官放标准接口)
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
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
}
// generateSign 生成签名
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"` // 请求方法
}
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"` // 是否默认
}
// 获取配送方式列表
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
}
// 订单发货
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
}
// 孔网订单同步
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"
}
// 执行订单发货
orderDeliver, err := kongfzOrderDeliver(appId, appSecret, accessToken, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
if err != nil {
return "", err
}
return orderDeliver, err
}
// =============== 辅助函数 ==============
// 获取GMT+8当前时间的字符串格式
func getCurrentTimeGMT8() string {
// 创建北京时间GMT+8
loc, _ := time.LoadLocation("Asia/Shanghai")
now := time.Now().In(loc)
return now.Format("2006-01-02 15:04:05")
}
// 替换所有空白字符为空格
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)
}
// 字符串去重
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()
}
// 验证日期格式并转换为时间戳
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))
}
// 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)
}
// 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() {
}