initial commit

This commit is contained in:
Cai1Cai1 2025-11-24 13:50:54 +08:00
parent e32d25abd4
commit 3c0c91bf3b
17 changed files with 8821 additions and 3 deletions

6
.gitignore vendored
View File

@ -27,6 +27,6 @@ go.work.sum
# env file # env file
.env .env
# Editor/IDE Editor/IDE
# .idea/ .idea/
# .vscode/ .vscode/

202
bookImage.go Normal file
View File

@ -0,0 +1,202 @@
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 全局变量
var (
// 数据库连接配置
DBUsername = "root" // 用户名
DBPassword = "Long6166@@" // 密码
DBHost = "nj-cynosdbmysql-grp-1v6vxn5f.sql.tencentcdb.com" // 主机
DBPort = 26247 // 端口
DBDataBase = "book_center" // 数据库
DBCharset = "utf8mb4" // 字符集
//连接池配置
MaxOpenConns = 20
MaxIdleConns = 10
)
// 连接数据库
func connectDB() (*sql.DB, error) {
// 数据源名称格式
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
DBUsername,
DBPassword,
DBHost,
DBPort,
DBDataBase,
DBCharset)
// 连接数据库
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, fmt.Errorf("打开数据库连接失败: %v", err)
}
// 设置连接池参数
db.SetMaxOpenConns(MaxOpenConns) // 最大打开连接数
db.SetMaxIdleConns(MaxIdleConns) // 最大空闲连接数
// 测试链接
err = db.Ping()
if err != nil {
return nil, fmt.Errorf("数据库连接测试失败: %v", err)
}
return db, nil
}
func selectBookIsbn(db *sql.DB, bookId int) error {
//// 执行语句
//query := `SELECT isbn, MAX(create_time) as max_create_time
//FROM shop_goods_rejection
//WHERE isbn IS NOT NULL
//GROUP BY isbn`
//
//exec, err := db.Exec(query)
//fmt.Println(exec, err)
return nil
}
//func main() {
//db, err := connectDB()
//if err != nil {
// log.Fatal("数据库连接失败:", err)
//}
//
//books, err := GetAllCategoryBooks()
//if err != nil {
// fmt.Printf("", err)
//}
//if len(books) == 0 {
// fmt.Printf("数组是空!")
//}
//// 查询数据库中不存在的ISBN
//missingISBNs, err := findMissingISBNs(db, books)
//if err != nil {
// log.Fatal("查询失败:", err)
//}
//log.Printf("数据库中不存在的ISBN数量: %d", len(missingISBNs))
//fmt.Println("数据库中不存在的ISBN数组:")
//for i, isbn := range missingISBNs {
// fmt.Printf("%d. %s\n", i+1, isbn)
//}
//
//missingISBNsString := strings.Join(missingISBNs, "\n")
//
//// 这里您可以将 missingISBNs 数组用于后续处理
//// 比如保存到文件、插入到另一个表等
//fmt.Printf("\n新的missingISBNs字符串: %v\n", missingISBNsString)
//}
// 查询数据库中不存在的ISBN
func findMissingISBNs(db *sql.DB, books []string) ([]string, error) {
if len(books) == 0 {
return []string{}, nil
}
// 查询数据库中存在的ISBN
existingISBNs, err := queryExistingISBNs(db, books)
if err != nil {
return nil, err
}
// 创建现有ISBN的映射用于快速查找
existingMap := make(map[string]bool)
for _, isbn := range existingISBNs {
existingMap[isbn] = true
}
// 创建新数组存储数据库中不存在的ISBN
missingISBNs := make([]string, 0)
for _, isbn := range books {
if !existingMap[isbn] {
missingISBNs = append(missingISBNs, isbn)
}
}
return missingISBNs, nil
}
// 查询数据库中存在的ISBN
func queryExistingISBNs(db *sql.DB, isbns []string) ([]string, error) {
if len(isbns) == 0 {
return []string{}, nil
}
// 分批处理避免SQL语句过长
batchSize := 1000
allExistingISBNs := make([]string, 0)
for i := 0; i < len(isbns); i += batchSize {
end := i + batchSize
if end > len(isbns) {
end = len(isbns)
}
batchISBNs := isbns[i:end]
batchExisting, err := queryBatchISBNs(db, batchISBNs)
if err != nil {
return nil, err
}
allExistingISBNs = append(allExistingISBNs, batchExisting...)
}
return allExistingISBNs, nil
}
// 批量查询ISBN
func queryBatchISBNs(db *sql.DB, isbns []string) ([]string, error) {
if len(isbns) == 0 {
return []string{}, nil
}
// 构建IN查询的占位符
placeholders := make([]string, len(isbns))
args := make([]interface{}, len(isbns))
for i, isbn := range isbns {
placeholders[i] = "?"
args[i] = isbn
}
query := fmt.Sprintf(`
SELECT isbn
FROM book_center
WHERE isbn IN (%s)
AND del_flag = 0
`, joinPlaceholders(placeholders, ","))
rows, err := db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf("查询数据库失败: %v", err)
}
defer rows.Close()
var existingISBNs []string
for rows.Next() {
var isbn string
if err := rows.Scan(&isbn); err != nil {
return nil, fmt.Errorf("扫描结果失败: %v", err)
}
existingISBNs = append(existingISBNs, isbn)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("遍历结果失败: %v", err)
}
return existingISBNs, nil
}
// joinPlaceholders 连接占位符
func joinPlaceholders(placeholders []string, sep string) string {
if len(placeholders) == 0 {
return ""
}
result := placeholders[0]
for i := 1; i < len(placeholders); i++ {
result += sep + placeholders[i]
}
return result
}

299
bookNameError.go Normal file
View File

@ -0,0 +1,299 @@
package main
import (
"database/sql"
"fmt"
"regexp"
"strings"
_ "github.com/go-sql-driver/mysql"
)
// func main() {
// // 构建数据库连接字符串
// dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
// "root", "Long6166@@", "nj-cynosdbmysql-grp-1v6vxn5f.sql.tencentcdb.com", "26247", "book_center")
// // 连接数据库
// db, err := sql.Open("mysql", dsn)
// if err != nil {
// log.Fatal("数据库连接失败:", err)
// }
// defer db.Close()
//
// // 测试数据库连接
// err = db.Ping()
// if err != nil {
// log.Fatal("数据库连接测试失败:", err)
// }
// fmt.Println("数据库连接成功")
//
// // 查询包含"影印版"或"(影印版)"的书名
// books, err := queryBooksWithCopyVersion(db)
// if err != nil {
// log.Fatal("查询失败:", err)
// }
//
// fmt.Printf("找到 %d 本包含'影印版'或'(影印版)'的书\n", len(books))
//
// if len(books) == 0 {
// fmt.Println("没有找到需要更新的图书")
// return
// }
//
// // 更新书名,删除"影印版"和"(影印版)"
// updatedCount, err := updateBookNames(db, books)
// if err != nil {
// log.Fatal("更新失败:", err)
// }
//
// fmt.Printf("成功更新 %d 本书的书名\n", updatedCount)
// }
//
// Book 结构体对应 book_center 表
type Book struct {
ID int64
BookName string
ISBN string
}
// queryBooksWithCopyVersion 查询包含"影印版"或"(影印版)"的书名
func queryBooksWithCopyVersion(db *sql.DB) ([]Book, error) {
query := `
SELECT id, book_name, isbn
FROM book_center
WHERE book_name LIKE '%影印版%'
AND del_flag = 0
`
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var books []Book
for rows.Next() {
var book Book
err := rows.Scan(&book.ID, &book.BookName, &book.ISBN)
if err != nil {
return nil, err
}
books = append(books, book)
}
return books, nil
}
// cleanBookName 清理书名,删除所有影印版相关文字
func cleanBookName(bookName string) string {
// 定义需要删除的模式
patterns := []string{
"(影印版)", // 全角括号
"(影印版)", // 半角括号
"影印版", // 无括号
"【影印版】", // 方头括号
"[影印版]", // 方括号
}
newName := bookName
// 逐个删除模式
for _, pattern := range patterns {
newName = strings.ReplaceAll(newName, pattern, "")
}
// 删除多余的空格
newName = strings.TrimSpace(newName)
newName = regexp.MustCompile(`\s+`).ReplaceAllString(newName, " ")
return newName
}
// updateBookNames 更新书名,删除所有影印版相关文字
func updateBookNames(db *sql.DB, books []Book) (int, error) {
tx, err := db.Begin()
if err != nil {
return 0, err
}
defer tx.Rollback()
updateStmt, err := tx.Prepare(`
UPDATE book_center
SET book_name = ?, update_time = UNIX_TIMESTAMP()
WHERE id = ? AND isbn = ?
`)
if err != nil {
return 0, err
}
defer updateStmt.Close()
updatedCount := 0
for _, book := range books {
// 清理书名
newBookName := cleanBookName(book.BookName)
// 如果书名没有变化,跳过更新
if newBookName == book.BookName {
fmt.Printf("跳过: 书名无变化 (ID: %d, ISBN: %s)\n", book.ID, book.ISBN)
continue
}
// 如果书名超过字段长度限制,截断
if len(newBookName) > 400 {
newBookName = newBookName[:400]
fmt.Printf("警告: 书名超长,已截断 (ID: %d)\n", book.ID)
}
result, err := updateStmt.Exec(newBookName, book.ID, book.ISBN)
if err != nil {
fmt.Printf("更新失败 (ID: %d): %v\n", book.ID, err)
continue
}
rowsAffected, err := result.RowsAffected()
if err != nil {
fmt.Printf("获取影响行数失败 (ID: %d): %v\n", book.ID, err)
continue
}
if rowsAffected > 0 {
updatedCount++
fmt.Printf("更新成功: ID=%d, ISBN=%s\n", book.ID, book.ISBN)
fmt.Printf(" 原书名: %s\n", book.BookName)
fmt.Printf(" 新书名: %s\n", newBookName)
fmt.Println(" ---")
}
}
err = tx.Commit()
if err != nil {
return 0, err
}
return updatedCount, nil
}
// 没用了
//// 获取销量榜所有分类中图书的isbn
//func GetAllCategoryBooks() ([]string, error) {
// // 1. 获取分类列表
// categories, err := GetSalesBookInfo()
// if err != nil {
// return nil, fmt.Errorf("获取分类列表失败: %v", err)
// }
// // 初始化返回的ISBN数组
// var allISBNs []string
//
// for i, category := range categories {
// fmt.Printf("\n=== 正在处理分类 %d/%d: %s (ID: %d) ===\n", i+1, len(categories), category.Key, category.Value)
// // 根据分类ID获取图书详情
// bookDetails, err := GetBookDetailsByCategory(category.Value)
// if err != nil {
// fmt.Printf("获取分类 %s 的图书信息失败: %v\n", category.Key, err)
// continue
// }
// // 将当前分类的图书数据添加到结果map中
// if bookDetails != nil && bookDetails.Result.Data != nil {
// for _, book := range bookDetails.Result.Data {
// if book.Isbn != "" {
// allISBNs = append(allISBNs, book.Isbn)
// fmt.Printf("添加到ISBN列表: %s\n", book.Isbn)
// }
// }
// fmt.Printf("分类 %s 获取到 %d 本图书\n", category.Key, len(bookDetails.Result.Data))
// } else {
// fmt.Printf("分类 %s 没有获取到图书数据\n", category.Key)
// }
// time.Sleep(500 * time.Microsecond)
// }
// // 去重处理(可选)
// allISBNs = removeDuplicateISBNs(allISBNs)
// // 打印统计信息
// fmt.Printf("\n=== 总计: %d 个分类, %d 个唯一ISBN ===\n", len(categories), len(allISBNs))
// return allISBNs, nil
//}
//
//// 获取销量榜的图书信息
//func GetSalesBookInfo() ([]SalesCategory, error) {
// // 构建请求URL
// url := "https://item.kongfz.com/api/Pc/getSellWellCatList"
// // 创建HTTP客户端
// client := &http.Client{
// Timeout: 30 * time.Second,
// }
// // 创建请求
// req, err := http.NewRequest("GET", url, nil)
// if err != nil {
// return nil, fmt.Errorf("创建请求失败: %v", err)
// }
// // 设置请求头,模拟浏览器请求
// req.Header.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")
// req.Header.Set("Accept", "application/json, text/plain, */*")
// req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
// req.Header.Set("Referer", "https://item.kongfz.com/")
// // 发送请求
// resp, err := client.Do(req)
// if err != nil {
// return nil, fmt.Errorf("请求失败: %v", err)
// }
// defer resp.Body.Close()
// // 检查HTTP状态码
// if resp.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
// }
// // 读取响应体
// body, err := io.ReadAll(resp.Body)
// if err != nil {
// return nil, fmt.Errorf("读取响应失败: %v", err)
// }
// // 解析JSON响应
// var salesCategoryResponse SalesCategoryResponse
// err = json.Unmarshal(body, &salesCategoryResponse)
// if err != nil {
// return nil, fmt.Errorf("解析JSON失败: %v", err)
// }
// return salesCategoryResponse.Result, nil
//}
//
//// 根据分类ID获取图书详情
//func GetBookDetailsByCategory(catId int) (*BookDetailResponse, error) {
// // 构建请求URL
// url := fmt.Sprintf("https://item.kongfz.com/api/pc/getSellWellListDetail?page=1&pageSize=100&timeRank=2&catId=%d", catId)
// // 创建HTTP客户端
// client := &http.Client{
// Timeout: 30 * time.Second,
// }
// // 创建请求
// req, err := http.NewRequest("GET", url, nil)
// if err != nil {
// return nil, fmt.Errorf("创建请求失败: %v", err)
// }
// // 设置请求头,模拟浏览器请求
// req.Header.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")
// req.Header.Set("Accept", "application/json, text/plain, */*")
// req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
// req.Header.Set("Referer", "https://item.kongfz.com/")
// // 发送请求
// resp, err := client.Do(req)
// if err != nil {
// return nil, fmt.Errorf("请求失败: %v", err)
// }
// defer resp.Body.Close()
// // 检查HTTP状态码
// if resp.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
// }
// // 读取响应体
// body, err := io.ReadAll(resp.Body)
// if err != nil {
// return nil, fmt.Errorf("读取响应失败: %v", err)
// }
// var bookDetailResponse BookDetailResponse
// err = json.Unmarshal(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)
// }
// return &bookDetailResponse, nil
//}

59
config.ini Normal file
View File

@ -0,0 +1,59 @@
# 应用配置
[app]
# 最大重试次数
max_retry_times = 3
# 间隔休眠时间
rate_limit_delay = 500ms
# 线程数
size = 5
# 默认用户代理
default_user_agent = Mozilla/5.0
# 代理配置
[proxy]
# 小象代理服务器列表(逗号分隔)
servers = http-dynamic.xiaoxiangdaili.com,http-dynamic-S02.xiaoxiangdaili.com,http-dynamic-S03.xiaoxiangdaili.com,http-dynamic-S04.xiaoxiangdaili.com
# 代理账号
username = 1297757178467602432
# 代理密码
password = QgQBvP7f
# 尾巴代理机器码
tail_machine_code = b7bf22a237ec692f13fcc2c43ee63252
# 尾巴代理卡密
tail_card_key = DL_20_YK_1920acb2129844c2aabade3896560a9b
# config配置dll
proxy_file_path = dll/proxyConfig.dll
# API接口配置
[api]
# 登录接口地址
login_url = https://login.kongfz.com/Pc/Login/account
# 图书搜索接口地址
book_search_url = https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list
# 产品搜索接口地址
product_search_url = https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list
# 服务器配置
[server]
# 服务监听端口
port = 9999
# 图片配置
[image]
# 图片保存目录
target_dir = /file/goods_img/
# 图片保存目录(本地测试路径)
; target_dir = ./images/
# 图片访问域名
domain = book.center.image.buzhiyushu.cn
# 数据库配置
[database]
# 数据库用户名
username = newAdmin
# 数据库密码
password = bYPp8SbBe5F7nz2i
# 数据库主机
host = 146.56.227.42:3306
# 数据库名称
name = newadmin

118
dll/kongfz.h Normal file
View File

@ -0,0 +1,118 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "main.go"
#include <stdlib.h>
// proxyConfig.dll 函数声明
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
extern void FreeCString(char* str);
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// 获取商品图片(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// 获取销量榜商品列表(带有Out的都非官放标准接口)
//
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
extern __declspec(dllexport) char* Initialize(char* configJSON);
// 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

104
dll/proxyConfig.h Normal file
View File

@ -0,0 +1,104 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "proxyConfig.go"
#include <stdlib.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// 导出函数:获取代理健康状态(用于调试)
//
extern __declspec(dllexport) char* GetProxyHealth(void);
// 导出函数:代理类型管理器
//
extern __declspec(dllexport) char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
// 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

120
dll/test.h Normal file
View File

@ -0,0 +1,120 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "kfztp.go"
#include <stdlib.h>
// proxyConfig.dll 函数声明
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
extern void FreeCString(char* str);
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// 获取商品图片(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetImageByIsbn(char* isbn, char* proxy, _Bool isLiveImage, _Bool isReturnMsg);
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, _Bool isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// 获取销量榜商品列表(带有Out的都非官放标准接口)
//
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
extern __declspec(dllexport) char* GetKFZSPTImageURL(char* proxyType, char* username, char* password, char* machineCode, char* isbn);
extern __declspec(dllexport) char* GetKFZGTImageURL(char* proxyType, char* username, char* password, char* machineCode, char* isbn);
extern __declspec(dllexport) char* Initialize(char* configJSON);
// 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

BIN
dll/xkongfz.dll~ Normal file

Binary file not shown.

122
dll/xkongfz.h Normal file
View File

@ -0,0 +1,122 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "main.go"
#include <stdlib.h>
// proxyConfig.dll 函数声明
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
extern void FreeCString(char* str);
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// 登录(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutLogin(char* username, char* password);
// 获取商品图片(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
//
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// 获取销量榜商品列表(带有Out的都非官放标准接口)
//
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
extern __declspec(dllexport) char* Initialize(char* configJSON);
// 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

29
go.mod Normal file
View File

@ -0,0 +1,29 @@
module kfzgw-info
go 1.25
require (
github.com/PuerkitoBio/goquery v1.10.3
github.com/chromedp/chromedp v0.14.2
github.com/go-ini/ini v1.67.0
github.com/go-sql-driver/mysql v1.9.3
github.com/parnurzeal/gorequest v0.3.0
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/elazarl/goproxy v1.7.2 // indirect
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/moul/http2curl v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/smartystreets/goconvey v1.8.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.34.0 // indirect
)

124
go.sum Normal file
View File

@ -0,0 +1,124 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E=
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM=
github.com/chromedp/chromedp v0.14.2/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs=
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

378
iniConfigUtil.go Normal file
View File

@ -0,0 +1,378 @@
// Package utils 提供配置文件加载工具支持从INI文件加载配置到结构体
// 支持类型: int, string, bool, float, time.Duration 及其切片类型
// 特性:
// - 支持默认值标签 `default`
// - 支持INI路径标签 `ini:"section.key"`
// - 支持时间单位转换标签 `multiplier`
// - 自动处理切片类型(逗号分隔)
package main
import (
"log"
"os"
"reflect"
"regexp"
"strconv"
"time"
"github.com/go-ini/ini"
)
// LoadConfig 从INI文件加载配置到结构体
// 参数:
//
// configPtr: 指向配置结构体的指针(必须是结构体指针)
// filename: INI配置文件的路径
//
// 返回:
//
// error: 加载成功返回nil失败返回ConfigError详细错误信息
//
// 用法说明:
// 1. 定义配置结构体使用标签声明INI映射关系和默认值
// 2. 调用LoadConfig(&config, "config.ini")
// 3. 检查错误并应用配置
//
// 示例结构体:
//
// type AppConfig struct {
// Port int `ini:"server.port" default:"8080"`
// Timeout time.Duration `ini:"server.timeout" multiplier:"1s"`
// Features []string `ini:"server.features"`
// }
func LoadConfig(configPtr interface{}, filename string) error {
// 验证输入必须是指针
if reflect.TypeOf(configPtr).Kind() != reflect.Ptr {
return &ConfigError{Message: "configPtr必须是指向结构体的指针"} // 返回错误
}
// 验证输入必须指向结构体
configValue := reflect.ValueOf(configPtr).Elem()
// 确保输入是结构体
if configValue.Kind() != reflect.Struct {
return &ConfigError{Message: "configPtr必须指向结构体"} // 返回错误
}
// 设置默认值(如果结构体有默认值)
setDefaultValues(configValue)
// 检查配置文件是否存在
if _, err := os.Stat(filename); os.IsNotExist(err) {
log.Printf("配置文件不存在: %s, 使用默认值", filename) // 打印信息
return nil // 返回错误
}
// 加载INI文件
iniCfg, err := ini.Load(filename)
// 处理错误
if err != nil {
return &ConfigError{Message: "加载配置文件失败", Cause: err} // 返回错误
}
// 映射配置到结构体
if err := mapConfig(iniCfg, configValue); err != nil {
return err // 返回错误
}
// 处理特殊类型如time.Duration
processSpecialTypes(configValue)
// 返回成功
return nil
}
// setDefaultValues 设置结构体字段的默认值
// 遍历结构体字段,检测`default`标签并设置初始值
// setDefaultValues 设置结构体字段的默认值
func setDefaultValues(configValue reflect.Value) {
// 获取结构体类型
configType := configValue.Type()
// 遍历字段
for i := 0; i < configType.NumField(); i++ {
// 获取字段信息
field := configType.Field(i)
// 获取字段值
fieldValue := configValue.Field(i)
// 如果字段是结构体,递归处理
if fieldValue.Kind() == reflect.Struct {
setDefaultValues(fieldValue)
continue
}
// 如果字段已经设置了值,跳过
if !fieldValue.IsZero() {
continue // 跳过
}
// 检查是否有默认值标签
if defaultValue, ok := field.Tag.Lookup("default"); ok {
setValueFromString(fieldValue, defaultValue) // 设置字段值
}
}
}
// mapConfig 将INI配置映射到结构体字段
// 解析`ini`标签获取section和key读取对应配置值
func mapConfig(iniCfg *ini.File, configValue reflect.Value) error {
// 获取结构体类型
configType := configValue.Type()
// 遍历字段
for i := 0; i < configType.NumField(); i++ {
field := configType.Field(i) // 获取字段信息
fieldValue := configValue.Field(i) // 获取字段值
// 如果字段是结构体,递归处理
if fieldValue.Kind() == reflect.Struct {
if err := mapConfig(iniCfg, fieldValue); err != nil {
return err
}
continue
}
// 获取INI标签
iniTag, ok := field.Tag.Lookup("ini")
// 如果没有INI标签跳过这个字段
if !ok || iniTag == "" {
continue // 跳过
}
// 解析INI键名支持section.key格式
sectionName, keyName := parseIniTag(iniTag)
// 获取INI值
section, err := iniCfg.GetSection(sectionName)
// 如果section不存在
if err != nil {
continue // 跳过这个字段
}
// 获取INI键值
key, err := section.GetKey(keyName)
// 如果key不存在
if err != nil {
continue //跳过这个字段
}
// 设置结构体字段值
if err := setFieldValue(fieldValue, key); err != nil {
// 返回错误
return &ConfigError{
Message: "设置字段值失败", // 返回错误信息
Cause: err, // 返回错误原因
Field: field.Name, // 返回字段名称
Tag: iniTag, // 返回标签
}
}
}
return nil
}
// parseIniTag 解析INI标签格式
// 输入: "section.key" 格式的字符串
// 返回: (section名称, key名称)
func parseIniTag(tag string) (section, key string) {
// 默认section
section = "DEFAULT"
// 默认key
key = tag
// 检查是否有section前缀
if parts := regexp.MustCompile(`^(\w+)\.(\w+)$`).FindStringSubmatch(tag); len(parts) == 3 {
section = parts[1] // 设置section
key = parts[2] // 设置key
}
// 返回section和key
return section, key
}
// setFieldValue 根据INI键值设置结构体字段值
// 自动处理基础类型和time.Duration类型转换
func setFieldValue(fieldValue reflect.Value, key *ini.Key) error {
// 检查字段类型
switch fieldValue.Kind() {
case reflect.String: // 字符串类型
fieldValue.SetString(key.String()) // 设置字段值
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
// 特殊处理time.Duration类型
if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
duration, err := time.ParseDuration(key.String()) // 解析字符串为time.Duration
// 处理错误
if err != nil {
return err // 返回错误
}
fieldValue.SetInt(int64(duration)) // 设置字段值
} else {
intValue, err := key.Int() // 获取整数值
// 处理错误
if err != nil {
return err // 返回错误
}
fieldValue.SetInt(int64(intValue)) // 设置字段值
}
case reflect.Bool: // 布尔类型
boolValue, err := key.Bool() // 获取布尔值
// 处理错误
if err != nil {
return err // 返回错误
}
fieldValue.SetBool(boolValue) // 设置字段值
case reflect.Float32, reflect.Float64: // 浮点类型
floatValue, err := key.Float64() // 获取浮点值
// 处理错误
if err != nil {
return err // 返回错误
}
fieldValue.SetFloat(floatValue) // 设置字段值
case reflect.Slice: // 切片类型
return setSliceValue(fieldValue, key) // 处理切片类型字段
default: // 其他类型
return &ConfigError{Message: "不支持的字段类型", FieldType: fieldValue.Type().String()} // 返回错误
}
// 返回成功
return nil
}
// setSliceValue 设置切片类型字段值
// 将逗号分隔的字符串解析为指定类型的切片
func setSliceValue(fieldValue reflect.Value, key *ini.Key) error {
// 获取切片元素类型
sliceType := fieldValue.Type().Elem()
// 获取逗号分隔的值
values := key.Strings(",")
// 创建新切片
slice := reflect.MakeSlice(fieldValue.Type(), len(values), len(values))
// 遍历值
for i, val := range values {
elemValue := reflect.New(sliceType).Elem() // 创建新元素
// 根据切片元素类型设置值
switch sliceType.Kind() {
case reflect.String: // 字符串类型
elemValue.SetString(val) // 设置字段值
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
intVal, err := strconv.ParseInt(val, 10, 64) // 解析字符串为整数
// 处理错误
if err != nil {
return err // 返回错误
}
elemValue.SetInt(intVal) // 设置字段值
case reflect.Float32, reflect.Float64: // 浮点类型
floatVal, err := strconv.ParseFloat(val, 64) // 解析字符串为浮点数
// 处理错误
if err != nil {
return err // 返回错误
}
elemValue.SetFloat(floatVal) // 设置字段值
case reflect.Bool: // 布尔类型
boolVal, err := strconv.ParseBool(val) // 解析字符串为布尔值
// 处理错误
if err != nil {
return err // 返回错误
}
elemValue.SetBool(boolVal) // 设置字段值
default:
return &ConfigError{Message: "不支持的切片元素类型", FieldType: sliceType.String()} // 返回错误
}
slice.Index(i).Set(elemValue) // 设置切片元素
}
// 设置切片字段值
fieldValue.Set(slice)
// 返回成功
return nil
}
// setValueFromString 从字符串解析值到结构体字段(用于默认值)
// 支持基础类型转换,不处理复杂类型
func setValueFromString(fieldValue reflect.Value, valueStr string) {
switch fieldValue.Kind() {
case reflect.String:
fieldValue.SetString(valueStr)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if intValue, err := strconv.ParseInt(valueStr, 10, 64); err == nil {
fieldValue.SetInt(intValue)
}
case reflect.Bool:
if boolValue, err := strconv.ParseBool(valueStr); err == nil {
fieldValue.SetBool(boolValue)
}
case reflect.Float32, reflect.Float64:
if floatValue, err := strconv.ParseFloat(valueStr, 64); err == nil {
fieldValue.SetFloat(floatValue)
}
case reflect.Slice:
// 切片类型需要特殊处理,这里简化处理
default:
// 保留原panic调用但修改提示信息
panic("未处理的默认值类型")
}
}
// processSpecialTypes 处理特殊类型转换
// 当前支持time.Duration的倍数转换使用multiplier标签
// processSpecialTypes 处理特殊类型转换
func processSpecialTypes(configValue reflect.Value) {
// 获取结构体类型
configType := configValue.Type()
// 遍历结构体字段
for i := 0; i < configType.NumField(); i++ {
field := configType.Field(i) // 获取字段
fieldValue := configValue.Field(i) // 获取字段值
// 如果字段是结构体,递归处理
if fieldValue.Kind() == reflect.Struct {
processSpecialTypes(fieldValue)
continue
}
// 处理time.Duration类型的倍数转换
if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
// 检查是否存在multiplier标签
if multiplier, ok := field.Tag.Lookup("multiplier"); ok {
// 解析multiplier标签
if mult, err := time.ParseDuration(multiplier); err == nil {
duration := time.Duration(fieldValue.Int()) // 将字段值转换为time.Duration
fieldValue.SetInt(int64(duration * mult)) // 将字段值设置为转换后的时间
}
}
}
}
}
// ConfigError 自定义配置错误类型
// 包含错误原因、字段信息和原始错误
type ConfigError struct {
Message string
Cause error
Field string
FieldType string
Tag string
}
// Error 实现error接口提供详细错误信息
func (e *ConfigError) Error() string {
msg := "配置错误: " + e.Message
if e.Field != "" {
msg += " [字段: " + e.Field + "]"
}
if e.FieldType != "" {
msg += " [类型: " + e.FieldType + "]"
}
if e.Tag != "" {
msg += " [标签: " + e.Tag + "]"
}
if e.Cause != nil {
msg += " - " + e.Cause.Error()
}
return msg
}

2011
kfztp.go Normal file

File diff suppressed because it is too large Load Diff

2997
main.go Normal file

File diff suppressed because it is too large Load Diff

736
md/main.md Normal file
View File

@ -0,0 +1,736 @@
# 针对kongfz.dll文件使用教程
## 1.需要先创建dll工具实例
### 1. 加载DLL用于调用dll文件代码示例
```go
func main(){
// 加载DLL
manager, err := NewDLLManager("main.dll")
if err != nil {
log.Fatalf("初始化DLL管理器失败: %v", err)
}
// 确保字段被释放但是需要注意如果加这个可能把你的dll文件释放掉了导致程序崩溃
defer manager.Close()
}
type DLLManager struct {
dll *syscall.DLL
}
func NewDLLManager(dllPath string) (*DLLManager, error) {
dll, err := syscall.LoadDLL(dllPath)
if err != nil {
return nil, fmt.Errorf("加载DLL失败: %v", err)
}
return &DLLManager{dll: dll}, nil
}
func (m *DLLManager) Close() {
if m.dll != nil {
m.dll.Release()
}
}
func (m *DLLManager) Initialize(configJSON string) (string, error) {
return m.callFunction("Initialize", configJSON)
}
// 修改后的 GetKFZGTImageURL - 需要5个参数
func (m *DLLManager) GetKFZGTImageURL(proxyType, username, password, machineCode, isbn string) (string, error) {
return m.callFunctionFiveArgs("GetKFZGTImageURL",
proxyType, username, password, machineCode, isbn)
}
// 修改后的 GetKFZSPTImageURL - 需要5个参数
func (m *DLLManager) GetKFZSPTImageURL(proxyType, username, password, machineCode, isbn string) (string, error) {
return m.callFunctionFiveArgs("GetKFZSPTImageURL",
proxyType, username, password, machineCode, isbn)
}
// 修改后的 GetKFZShopBookInfo - 需要12个参数
func (m *DLLManager) GetKFZShopBookInfo(proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp string) (string, error) {
return m.callFunctionTwelveArgs("GetKFZShopBookInfo",
proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp)
}
```
### 2.初始化配置信息,代码示例:
````go
func main(){
// 使用默认配置初始化
config := createDefaultConfig()
configJSON, err := json.Marshal(config)
if err != nil {
log.Fatalf("序列化配置失败: %v", err)
}
result, err := manager.Initialize(string(configJSON))
if err != nil {
log.Fatalf("初始化失败: %v", err)
}
var initResp APIResponses
if err := json.Unmarshal([]byte(result), &initResp); err != nil {
log.Fatalf("解析初始化响应失败: %v", err)
}
if !initResp.Success {
log.Fatalf("初始化失败: %s", initResp.Message)
}
}
// 创建默认配置示例
func createDefaultConfig() Configs {
var configs Configs
// App配置
configs.App.MaxRetryTimes = 3
configs.App.RateLimitDelay = 1000
configs.App.Size = 10
configs.App.DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
// API配置
configs.API.LoginURL = "https://login.kongfz.com/Pc/Login/account"
configs.API.BookSearchURL = "https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list"
configs.API.ProductSearchURL = "https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list"
// 代理配置(需要根据实际情况修改)
configs.Proxy.Servers = "http-dynamic.xiaoxiangdaili.com,http-dynamic-S02.xiaoxiangdaili.com,http-dynamic-S03.xiaoxiangdaili.com,http-dynamic-S04.xiaoxiangdaili.com"
configs.Proxy.Username = "1297757178467602432"
configs.Proxy.Password = "QgQBvP7f"
configs.Proxy.TailMachineCode = "b7bf22a237ec692f13fcc2c43ee63252"
configs.Proxy.TailCardKey = "DL_20_YK_1920acb2129844c2aabade3896560a9b"
configs.Proxy.ProxyFilePath = "dll/proxyConfig.dll"
return configs
}
// 配置结构
type Configs struct {
App struct {
MaxRetryTimes int `json:"max_retry_times"`
RateLimitDelay int `json:"rate_limit_delay"`
Size int `json:"size"`
DefaultUserAgent string `json:"default_user_agent"`
} `json:"app"`
API struct {
LoginURL string `json:"login_url"`
BookSearchURL string `json:"book_search_url"`
ProductSearchURL string `json:"product_search_url"`
} `json:"api"`
Proxy struct {
Servers string `json:"servers"`
Username string `json:"username"`
Password string `json:"password"`
TailMachineCode string `json:"tail_machine_code"`
TailCardKey string `json:"tail_card_key"`
ProxyFilePath string `json:"proxy_file_path"`
} `json:"proxy"`
}
````
### 直接调用接口,代码示例:入参、返回参数都在下面
```go
func main() {
// 调用方法示例
shopResult, err := manager.GetKFZShopBookInfo(
proxyType, username, password, machineCode,
shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp,
)
}
```
---
## 接口详情
### 1. 获取孔网官图
**请求信息**
gtResult, err := manager.GetKFZGTImageURL(testCase.isbn)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|------|--------|----|-----------|
| proxyType | string | 是 | 代理类型(小象代理CALF_ELEPHANT_PROXY,内置代理TAIL_PROXY) |
| username | string | 是 | 小象代理账号 |
| password | string | 是 | 小象代理密码 |
| machineCode | string | 是 | 内置代理机械码 |
| isbn | string | 是 | 图书的 ISBN 号 |
**响应数据**
| 字段名 | 类型 | 说明 | 示例值 |
|--------|------|------|---------|
| `success` | boolean | 请求是否成功 | `true` |
| `message` | string | 响应消息 | `"查询成功"` |
| `data` | array | 图书信息列表 | - |
**图书信息字段说明(data数据)**
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `author` | string | 作者 |
| `binding_layout` | string | 装帧布局 |
| `book_name` | string | 图书名称 |
| `book_pic` | string | 图书图片(大图) |
| `book_pic_s` | string | 图书图片(小图) |
| `buyCount` | string | 购买数量 |
| `category` | string | 分类 |
| `condition` | string | 图书状况 |
| `content` | string | 内容简介 |
| `edition` | string | 版次 |
| `editor` | string | 编者 |
| `express_delivery_fee` | string | 快递费用 |
| `fix_price` | string | 定价 |
| `format` | string | 开本 |
| `isbn` | string | ISBN号 |
| `item_id` | number | 商品ID |
| `languages` | string | 语言 |
| `mid` | number | 商家ID |
| `pages` | string | 页数 |
| `paper` | string | 纸张 |
| `print_time` | string | 印刷时间 |
| `publication_time` | number | 出版时间(时间戳) |
| `publisher` | string | 出版社 |
| `sellCount` | string | 销售数量 |
| `shop_id` | number | 店铺ID |
| `wordage` | string | 字数 |
**响应示例**
```json
{
"success": true,
"message": "查询成功",
"data": {
"author": "",
"binding_layout": "",
"book_name": "",
"book_pic": "",
"book_pic_s": "",
"buyCount": "",
"category": "",
"condition": "",
"content": "",
"edition": "",
"editor": "",
"express_delivery_fee": "",
"fix_price": "",
"format": "",
"isbn": "",
"item_id": 0,
"languages": "",
"mid": 0,
"pages": "",
"paper": "",
"print_time": "",
"publication_time": 0,
"publisher": "",
"sellCount": "",
"shop_id": 0,
"wordage": ""
}
}
```
### 2. 获取孔网实拍图
**请求信息**
sptResult, err := manager.GetKFZSPTImageURL(testCase.isbn)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|------|--------|----|------------|
| proxyType | string | 是 | 代理类型(小象代理CALF_ELEPHANT_PROXY,内置代理TAIL_PROXY) |
| username | string | 是 | 小象代理账号 |
| password | string | 是 | 小象代理密码 |
| machineCode | string | 是 | 内置代理机械码 |
| isbn | string | 是 | 图书的 ISBN 号 |
**响应数据**
| 字段名 | 类型 | 说明 | 示例值 |
|--------|------|------|---------|
| `success` | boolean | 请求是否成功 | `true` |
| `message` | string | 响应消息 | `"查询成功"` |
| `data` | array | 图书信息列表 | - |
**图书信息字段说明(data数据)**
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `author` | string | 作者 |
| `binding_layout` | string | 装帧布局 |
| `book_name` | string | 图书名称 |
| `book_pic` | string | 图书图片(大图) |
| `book_pic_s` | string | 图书图片(小图) |
| `buyCount` | string | 购买数量 |
| `category` | string | 分类 |
| `condition` | string | 图书状况 |
| `content` | string | 内容简介 |
| `edition` | string | 版次 |
| `editor` | string | 编者 |
| `express_delivery_fee` | string | 快递费用 |
| `fix_price` | string | 定价 |
| `format` | string | 开本 |
| `isbn` | string | ISBN号 |
| `item_id` | number | 商品ID |
| `languages` | string | 语言 |
| `mid` | number | 商家ID |
| `pages` | string | 页数 |
| `paper` | string | 纸张 |
| `print_time` | string | 印刷时间 |
| `publication_time` | number | 出版时间(时间戳) |
| `publisher` | string | 出版社 |
| `sellCount` | string | 销售数量 |
| `shop_id` | number | 店铺ID |
| `wordage` | string | 字数 |
**响应示例**
```json
{
"success": true,
"message": "查询成功",
"data": {
"author": "",
"binding_layout": "",
"book_name": "",
"book_pic": "",
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/adaaecbf/a1e5df0a21cfdbba_s.jpg",
"buyCount": "",
"category": "",
"condition": "",
"content": "",
"edition": "",
"editor": "",
"express_delivery_fee": "",
"fix_price": "",
"format": "",
"isbn": "",
"item_id": 0,
"languages": "",
"mid": 0,
"pages": "",
"paper": "",
"print_time": "",
"publication_time": 0,
"publisher": "",
"sellCount": "",
"shop_id": 0,
"wordage": ""
}
}
```
### 3. 获取店铺图书信息
**请求信息**
shopResult, err := manager.GetKFZShopBookInfo(
proxyType, //代理类型
username, //小象代理账号
password, //小象代理密码
machineCode, //内置代理机械码
shopId,
isImage,
bookNum,
pageNum,
sortType,
sort,
priceDown,
priceUp,
)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|----|------------------------------------------------|
| fetchMode | string | 是 | 是否使用代理 (使用代理proxy 不使用代理direct) |
| proxyType | string | 是 | 代理类型(小象代理CALF_ELEPHANT_PROXY,内置代理TAIL_PROXY) |
| username | string | 是 | 小象代理账号 |
| password | string | 是 | 小象代理密码 |
| machineCode | string | 是 | 内置代理机械码 |
| shopId | string | 是 | 店铺编号 |
| isImage | string | 否 | 是否有图片 |
| bookNum | string | 否 | 图书数量 |
| pageNum | string | 否 | 页数 |
| sortType | string | 否 | 排序类型(sort,putDate,newItem,price只有这四种) |
| sort | string | 否 | 排序方式(asc,desc) |
| priceUp | string | 否 | 最低价格 |
| priceDown | string | 否 | 最高价格 |
**响应数据**
| 字段名 | 类型 | 说明 | 示例值 |
|--------|------|--------|---------|
| `success` | boolean | 请求是否成功 | `true` |
| `message` | string | 响应消息 | `"查询成功"` |
| `goods_num` | string | 商品数量 | `"查询成功"` |
| `pnum` | string | 店铺商品页数 | `"查询成功"` |
| `data` | array | 图书信息列表 | - |
**图书信息字段说明(data数据)**
| 字段名 | 类型 | 说明 |
|--------|------|--------|
| `book_name` | string | 书名 |
| `author` | string | 作者 |
| `publisher` | string | 出版社 |
| `isbn` | string | ISBN号 |
| `publication_time` | number | 出版时间(时间戳) |
| `edition` | string | 版次 |
| `print_time` | string | 印刷时间 |
| `fix_price` | string | 定价 |
| `binding_layout` | string | 装帧 |
| `format` | string | 开本 |
| `paper` | string | 纸张 |
| `pages` | string | 页数 |
| `wordage` | string | 字数 |
| `languages` | string | 语种 |
| `era` | string | 年代 |
| `engraving_method` | string | 刻印方式 |
| `dimensions` | string | 尺寸 |
| `volume_number` | string | 册数 |
| `book_pic` | string | 图书图片(官图) |
| `book_pic_s` | string | 图书图片(实拍图) |
| `selling_price` | string | 售价 |
| `condition` | string | 品相 |
| `express_delivery_fee` | string | 快递费 |
| `editor` | string | 编辑 |
| `category` | string | 分类 |
| `buy_count` | string | 买过 |
| `sell_count` | string | 买过 |
| `content` | string | 内容 |
| `mid` | number | 商家ID |
| `item_id` | number | 商品ID |
| `shop_id` | number | 店铺ID |
**响应示例**
```json
{
"success": true,
"message": "查询成功",
"goods_num": "",
"pnum": "",
"data": [
{
"author": "",
"binding_layout": "",
"book_name": "",
"book_pic": "",
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/cddbcbda/6833c1d27437d121_s.jpg",
"buyCount": "",
"category": "",
"condition": "",
"content": "",
"edition": "",
"editor": "",
"express_delivery_fee": "",
"fix_price": "",
"format": "",
"isbn": "",
"item_id": 0,
"languages": "",
"mid": 0,
"pages": "",
"paper": "",
"print_time": "",
"publication_time": 0,
"publisher": "",
"sellCount": "",
"shop_id": 0,
"wordage": ""
}
]
}
```
### 4. 根据url获取单个图书详情信息
**请求信息**
shopResult, err := manager.GetUrlBookDetails(
fetchMode,
proxyType,
username,
password,
machineCode,
url
)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|----|------------------------------------------------|
| fetchMode | string | 是 | 是否使用代理 (使用代理proxy 不使用代理direct) |
| proxyType | string | 是 | 代理类型(小象代理CALF_ELEPHANT_PROXY,内置代理TAIL_PROXY) |
| username | string | 是 | 小象代理账号 |
| password | string | 是 | 小象代理密码 |
| machineCode | string | 是 | 内置代理机械码 |
| url | string | 是 | 传入详情页的url |
**响应数据**
| 字段名 | 类型 | 说明 | 示例值 |
|--------|------|--------|---------|
| `success` | boolean | 请求是否成功 | `true` |
| `message` | string | 响应消息 | `"查询成功"` |
| `goods_num` | string | 商品数量 | `"查询成功"` |
| `pnum` | string | 店铺商品页数 | `"查询成功"` |
| `data` | array | 图书信息列表 | - |
**图书信息字段说明(data数据)**
| 字段名 | 类型 | 说明 |
|--------|------|--------|
| `book_name` | string | 书名 |
| `author` | string | 作者 |
| `publisher` | string | 出版社 |
| `isbn` | string | ISBN号 |
| `publication_time` | number | 出版时间(时间戳) |
| `edition` | string | 版次 |
| `print_time` | string | 印刷时间 |
| `fix_price` | string | 定价 |
| `binding_layout` | string | 装帧 |
| `format` | string | 开本 |
| `paper` | string | 纸张 |
| `pages` | string | 页数 |
| `wordage` | string | 字数 |
| `languages` | string | 语种 |
| `era` | string | 年代 |
| `engraving_method` | string | 刻印方式 |
| `dimensions` | string | 尺寸 |
| `volume_number` | string | 册数 |
| `book_pic` | string | 图书图片(官图) |
| `book_pic_s` | string | 图书图片(实拍图) |
| `selling_price` | string | 售价 |
| `condition` | string | 品相 |
| `express_delivery_fee` | string | 快递费 |
| `editor` | string | 编辑 |
| `category` | string | 分类 |
| `buy_count` | string | 买过 |
| `sell_count` | string | 买过 |
| `content` | string | 内容 |
| `mid` | number | 商家ID |
| `item_id` | number | 商品ID |
| `shop_id` | number | 店铺ID |
**响应示例**
```json
{
"success": true,
"message": "查询成功",
"goods_num": "",
"pnum": "",
"data": [
{
"author": "",
"binding_layout": "",
"book_name": "",
"book_pic": "",
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/cddbcbda/6833c1d27437d121_s.jpg",
"buyCount": "",
"category": "",
"condition": "",
"content": "",
"edition": "",
"editor": "",
"express_delivery_fee": "",
"fix_price": "",
"format": "",
"isbn": "",
"item_id": 0,
"languages": "",
"mid": 0,
"pages": "",
"paper": "",
"print_time": "",
"publication_time": 0,
"publisher": "",
"sellCount": "",
"shop_id": 0,
"wordage": ""
}
]
}
```
### 5. 初始化
**请求信息**
manager.Initialize(
configJSON,
)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|--------|--------|----|------|
| configJSON | string | 是 | 配置信息 |
**响应数据**
| 字段名 | 类型 | 说明 | 示例值 |
|--------|------|--------|---------|
| `success` | boolean | 请求是否成功 | `true` |
| `message` | string | 响应消息 | `"查询成功"` |
**图书信息字段说明(data数据)**
| 字段名 | 类型 | 说明 |
|--------|------|--------|
| `book_name` | string | 书名 |
| `author` | string | 作者 |
| `publisher` | string | 出版社 |
| `isbn` | string | ISBN号 |
| `publication_time` | number | 出版时间(时间戳) |
| `edition` | string | 版次 |
| `print_time` | string | 印刷时间 |
| `fix_price` | string | 定价 |
| `binding_layout` | string | 装帧 |
| `format` | string | 开本 |
| `paper` | string | 纸张 |
| `pages` | string | 页数 |
| `wordage` | string | 字数 |
| `languages` | string | 语种 |
| `era` | string | 年代 |
| `engraving_method` | string | 刻印方式 |
| `dimensions` | string | 尺寸 |
| `volume_number` | string | 册数 |
| `book_pic` | string | 图书图片(官图) |
| `book_pic_s` | string | 图书图片(实拍图) |
| `selling_price` | string | 售价 |
| `condition` | string | 品相 |
| `express_delivery_fee` | string | 快递费 |
| `editor` | string | 编辑 |
| `category` | string | 分类 |
| `buy_count` | string | 买过 |
| `sell_count` | string | 买过 |
| `content` | string | 内容 |
| `mid` | number | 商家ID |
| `item_id` | number | 商品ID |
| `shop_id` | number | 店铺ID |
# proxyConfig.dll文件使用讲解
由于是kongfz.dll文件去调用proxyConfig.dll文件里面的函数无需再去调用。
除非直接使用proxyConfig.dll文件。
## 1.直接使用proxyConfig.dll文件应先加载DLL
```go
func main(){
// 加载DLL
manager, err := NewDLLManager("文件路径")
if err != nil {
log.Fatalf("初始化DLL管理器失败: %v", err)
}
// 确保字段被释放但是需要注意如果加这个可能把你的dll文件释放掉了导致程序崩溃
defer manager.Close()
}
type DLLManager struct {
dll *syscall.DLL
}
func NewDLLManager(dllPath string) (*DLLManager, error) {
dll, err := syscall.LoadDLL(dllPath)
if err != nil {
return nil, fmt.Errorf("加载DLL失败: %v", err)
}
return &DLLManager{dll: dll}, nil
}
func (m *DLLManager) Close() {
if m.dll != nil {
m.dll.Release()
}
}
// ProxyTypeManager 调用代理类型管理器
func (m *ProxyConfigManager) ProxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
proc, err := m.dll.FindProc("ProxyTypeManager")
if err != nil {
return "", fmt.Errorf("找不到函数 ProxyTypeManager: %v", err)
}
// 准备参数
proxyTypePtr, _ := syscall.BytePtrFromString(proxyType)
usernamePtr, _ := syscall.BytePtrFromString(username)
passwordPtr, _ := syscall.BytePtrFromString(password)
machineCodePtr, _ := syscall.BytePtrFromString(machineCode)
// 调用函数
r1, _, err := proc.Call(
uintptr(unsafe.Pointer(proxyTypePtr)),
uintptr(unsafe.Pointer(usernamePtr)),
uintptr(unsafe.Pointer(passwordPtr)),
uintptr(unsafe.Pointer(machineCodePtr)),
)
if err != nil && err.Error() != "The operation completed successfully." {
return "", fmt.Errorf("调用 ProxyTypeManager 失败: %v", err)
}
// 转换结果
result := (*byte)(unsafe.Pointer(r1))
var resultBytes []byte
for i := 0; ; i++ {
bytePtr := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))
if *bytePtr == 0 {
break
}
resultBytes = append(resultBytes, *bytePtr)
}
// 释放内存
freeProc, _ := m.dll.FindProc("FreeCString")
if freeProc != nil {
freeProc.Call(r1)
}
return string(resultBytes), nil
}
```
## 直接调用ProxyTypeManager函数方法
```go
typeManager, err := globalProxyManager.ProxyTypeManager(
proxyType,
username,
password,
machineCode,
)
```
**请求信息**
typeManager, err := globalProxyManager.ProxyTypeManager(
proxyType,
username,
password,
machineCode,
)
**请求参数**
| 参数名 | 类型 | 必填 | 说明 |
|------|--------|----|------------|
| proxyType | string | 是 | 代理类型(小象代理CALF_ELEPHANT_PROXY,内置代理TAIL_PROXY) |
| username | string | 是 | 小象代理账号 |
| password | string | 是 | 小象代理密码 |
| machineCode | string | 是 | 内置代理机械码 |
**响应信息**
string字符串 http://18434270290:JWt15lWW@111.179.77.244:44751

510
proxyConfig.go Normal file
View File

@ -0,0 +1,510 @@
package main
// #include <stdlib.h>
import "C"
import (
"crypto/md5"
"encoding/json"
"fmt"
"github.com/parnurzeal/gorequest"
"log"
"math/rand"
"net/url"
"strings"
"sync"
"time"
)
// 代理类型常量
const (
CalfElephantProxyType = "CALF_ELEPHANT_PROXY"
TailProxyType = "TAIL_PROXY"
)
// 代理服务器列表
var (
servers = []string{
"http-dynamic.xiaoxiangdaili.com",
"http-dynamic-S02.xiaoxiangdaili.com",
"http-dynamic-S03.xiaoxiangdaili.com",
"http-dynamic-S04.xiaoxiangdaili.com",
}
randMutex sync.Mutex
globalRand *rand.Rand
// 代理健康状态管理
proxyHealthMaps = make(map[string]*ProxyHealth)
proxyHealthMutex sync.RWMutex
)
// ProxyManager 代理管理器结构体
type ProxyManager struct {
servers []string `json:"servers"` // 代理服务器列表
username string `json:"username"` // 代理账号
password string `json:"password"` // 代理密码
tailCardSecret string `json:"tail_card_secret"` // 尾巴代理卡密
proxyType string `json:"proxy_type"` // 代理类型 CALF_ELEPHANT_PROXY/TAIL_PROXY
}
// 代理健康状态
type ProxyHealth struct {
SuccessCount int // 成功次数
FailCount int // 失败次数
LastCheck time.Time // 最后检查时间
ResponseTime time.Duration // 响应时间
IsHealthy bool // 是否健康
}
func init() {
// 创建全局的随机数生成器
globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}
// 获取代理URL
func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
switch proxyType {
case CalfElephantProxyType:
return buildCalfElephantProxyURL(username, password)
case TailProxyType:
return buildTailProxyURL(machineCode)
default:
return "", fmt.Errorf("不支持的代理类型: %s", proxyType)
}
}
// 构建小象代理URL
func buildCalfElephantProxyURL(username, password string) (string, error) {
server := randomServer()
proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
url.QueryEscape(username),
url.QueryEscape(password),
server,
10030)
// 检测代理可用性
if err := checkProxyHealth(proxyURL); err != nil {
log.Printf("[WARN] 代理 %s 检测失败: %v", server, err)
// 尝试下一个代理服务器
return tryNextCalfElephantProxy(username, password, server)
}
log.Printf("[INFO] 使用小象代理: %s", server)
return proxyURL, nil
}
// 尝试下一个小象代理服务器
func tryNextCalfElephantProxy(username, password, failedServer string) (string, error) {
// 创建服务器副本并排除失败的服务器
availableServers := make([]string, 0)
for _, server := range servers {
if server != failedServer {
availableServers = append(availableServers, server)
}
}
if len(availableServers) == 0 {
return "", fmt.Errorf("所有小象代理服务器都不可用")
}
// 随机尝试可用服务器
for _, server := range shuffleServers(availableServers) {
proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
url.QueryEscape(username),
url.QueryEscape(password),
server,
10030)
if err := checkProxyHealth(proxyURL); err == nil {
log.Printf("[INFO] 切换到可用代理: %s", server)
return proxyURL, nil
}
log.Printf("[WARN] 代理 %s 检测失败", server)
}
return "", fmt.Errorf("所有可用的小象代理服务器都检测失败")
}
// 构建内置代理URL
func buildTailProxyURL(machineCode string) (string, error) {
proxies, err := getProxies(machineCode)
if err != nil {
return "", err
}
if len(proxies) == 0 {
return "", fmt.Errorf("未获取到有效代理")
}
// 过滤并选择健康的代理
healthyProxies := filterHealthyProxies(proxies)
if len(healthyProxies) > 0 {
proxyURL := "http://" + randomElement(healthyProxies)
log.Printf("[INFO] 使用健康尾巴代理: %s", proxyURL)
return proxyURL, nil
}
// 如果没有健康代理,检测所有代理
log.Printf("[INFO] 未找到健康代理,开始检测代理可用性...")
return findWorkingTailProxy(proxies)
}
// 过滤健康代理
func filterHealthyProxies(proxies []string) []string {
proxyHealthMutex.RLock()
defer proxyHealthMutex.RUnlock()
var healthy []string
for _, proxy := range proxies {
if health, exists := proxyHealthMaps[proxy]; exists && health.IsHealthy {
// 检查是否在最近检查过5分钟内
if time.Since(health.LastCheck) < 5*time.Minute {
healthy = append(healthy, proxy)
}
}
}
return healthy
}
// 查找可用的尾巴代理
func findWorkingTailProxy(proxies []string) (string, error) {
// 打乱代理顺序
shuffledProxies := shuffleServers(proxies)
// 并发检测代理
type proxyResult struct {
proxy string
err error
}
ch := make(chan proxyResult, len(shuffledProxies))
var wg sync.WaitGroup
// 限制并发数
sem := make(chan struct{}, 5)
for _, proxy := range shuffledProxies {
wg.Add(1)
go func(p string) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
proxyURL := "http://" + p
err := checkProxyHealth(proxyURL)
ch <- proxyResult{proxy: p, err: err}
}(proxy)
}
wg.Wait()
close(ch)
// 收集结果
var workingProxies []string
for result := range ch {
if result.err == nil {
workingProxies = append(workingProxies, result.proxy)
// 更新健康状态
updateProxyHealth(result.proxy, true, 0)
} else {
updateProxyHealth(result.proxy, false, 0)
}
}
if len(workingProxies) > 0 {
selected := randomElement(workingProxies)
log.Printf("[INFO] 找到可用代理: %s (共 %d 个可用)", selected, len(workingProxies))
return "http://" + selected, nil
}
return "", fmt.Errorf("所有尾巴代理都不可用")
}
// 检测代理健康状态
func checkProxyHealth(proxyURL string) error {
start := time.Now()
req := gorequest.New().Proxy(proxyURL).Timeout(10 * time.Second)
resp, _, errs := req.Get("https://shop.kongfz.com/").End()
responseTime := time.Since(start)
if len(errs) > 0 {
updateProxyHealth(proxyURL, false, responseTime)
return fmt.Errorf("代理连接失败: %v", errs)
}
if resp.StatusCode != 200 {
updateProxyHealth(proxyURL, false, responseTime)
return fmt.Errorf("代理响应状态码错误: %d", resp.StatusCode)
}
updateProxyHealth(proxyURL, true, responseTime)
log.Printf("[DEBUG] 代理 %s 检测成功, 响应时间: %v", getProxyHost(proxyURL), responseTime)
return nil
}
// 更新代理健康状态
func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration) {
proxyHealthMutex.Lock()
defer proxyHealthMutex.Unlock()
host := getProxyHost(proxyURL)
if _, exists := proxyHealthMaps[host]; !exists {
proxyHealthMaps[host] = &ProxyHealth{}
}
health := proxyHealthMaps[host]
health.LastCheck = time.Now()
if success {
health.SuccessCount++
health.FailCount = 0
health.ResponseTime = responseTime
health.IsHealthy = true
} else {
health.FailCount++
health.SuccessCount = 0
// 连续失败3次标记为不健康
if health.FailCount >= 3 {
health.IsHealthy = false
}
}
}
// 获取代理主机名
func getProxyHost(proxyURL string) string {
if strings.HasPrefix(proxyURL, "http://") {
proxyURL = proxyURL[7:]
}
// 去除认证信息
if atIndex := strings.Index(proxyURL, "@"); atIndex != -1 {
proxyURL = proxyURL[atIndex+1:]
}
// 去除端口
if colonIndex := strings.Index(proxyURL, ":"); colonIndex != -1 {
proxyURL = proxyURL[:colonIndex]
}
return proxyURL
}
// 随机代理服务器
func randomServer() string {
return randomElement(servers)
}
// 线程安全的随机元素选择
func randomElement(slice []string) string {
randMutex.Lock()
defer randMutex.Unlock()
return slice[globalRand.Intn(len(slice))]
}
// 打乱服务器顺序
func shuffleServers(servers []string) []string {
randMutex.Lock()
defer randMutex.Unlock()
shuffled := make([]string, len(servers))
copy(shuffled, servers)
globalRand.Shuffle(len(shuffled), func(i, j int) {
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
})
return shuffled
}
// 检查卡密是否过期
func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
code, err := getMachineCode(tailCardSecret)
if err != nil {
return false, fmt.Errorf("请求错误: %v", err)
}
targetTime, err := time.Parse("2006-01-02 15:04:05", code.Data.IpExpTime)
if err != nil {
return false, fmt.Errorf("时间格式错误: %v", err)
}
currentTime := time.Now()
if targetTime.After(currentTime) {
return true, nil
} else {
return false, fmt.Errorf("卡密已经过期!过期时间: %v", targetTime)
}
}
// 定义响应结构体
type getMachineCodeResp struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
MachineCode string `json:"machine_code"`
IpExpTime string `json:"ip_exp_time"`
IpThread int `json:"ip_thread"`
IpCardCode string `json:"ip_card_code"`
} `json:"data"`
}
// 查询机器码
func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
url := "http://114.66.2.223:7842/api/proxies/ip_show"
data := map[string]interface{}{
"ip_card_code": tailCardSecret,
"agent_id": 9999,
}
_, body, errs := gorequest.New().Post(url).Send(data).End()
if len(errs) > 0 {
return nil, fmt.Errorf("查询机器码失败: %v", errs)
}
var resp getMachineCodeResp
if err := json.Unmarshal([]byte(body), &resp); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
if resp.Code == 201 {
machineCode, err := rechargeCard(tailCardSecret, resp.Data.MachineCode)
if err != nil {
return nil, err
}
resp.Data.MachineCode = machineCode
return &resp, nil
} else if resp.Code == 200 {
return &resp, nil
} else {
return nil, fmt.Errorf("查询机器码失败: %s", resp.Message)
}
}
// 充值卡密
func rechargeCard(tailCardSecret, machineCode string) (string, error) {
url := "http://114.66.2.223:7842/api/proxies/ip_recharge"
data := map[string]interface{}{
"machine_code": machineCode,
"ip_card_code": tailCardSecret,
"agent_id": 9999,
}
_, body, errs := gorequest.New().Post(url).Send(data).End()
if len(errs) > 0 {
return "", fmt.Errorf("充值卡密失败: %v", errs)
}
var resp struct {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 返回消息
Data struct {
IpExpTime string `json:"ip_exp_time"` // 过期时间
IpThread int `json:"ip_thread"` // 线程
MachineCode string `json:"machine_code"` // 机器码
} `json:"data"`
}
if err := json.Unmarshal([]byte(body), &resp); err != nil {
return "", fmt.Errorf("解析响应失败: %v", err)
}
if resp.Code != 200 {
return "", fmt.Errorf("充值卡密失败: %s", resp.Message)
}
return resp.Data.MachineCode, nil
}
// 获取代理服务器列表
func getProxies(machineCode string) ([]string, error) {
log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
// 生成签名
sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode))))
GetProxiesUrl := fmt.Sprintf("http://114.66.2.223:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999",
machineCode, sign)
req := gorequest.New().Get(GetProxiesUrl).Timeout(20 * time.Second)
_, body, errs := req.End()
if len(errs) > 0 {
return nil, fmt.Errorf("获取代理失败: %v", errs)
}
// 检查是否为JSON错误响应
if strings.HasPrefix(strings.TrimSpace(body), "{") && strings.HasSuffix(strings.TrimSpace(body), "}") {
// 尝试解析为JSON错误响应
var errorResp struct {
Code int `json:"code"`
Message string `json:"message"`
}
if err := json.Unmarshal([]byte(body), &errorResp); err == nil {
return nil, fmt.Errorf("获取代理失败: %s (错误码: %d)", errorResp.Message, errorResp.Code)
}
}
// 解析响应
lines := strings.Split(strings.TrimSpace(body), "\n")
var proxies []string
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "{") {
proxies = append(proxies, line)
}
}
if len(proxies) == 0 {
return nil, fmt.Errorf("未获取到有效代理")
}
log.Printf("[INFO] 获取到 %d 个代理", len(proxies))
return proxies, nil
}
// 初始化代理信息
func initProxy() {
}
// 导出函数:获取代理健康状态(用于调试)
//
//export GetProxyHealth
func GetProxyHealth() *C.char {
proxyHealthMutex.RLock()
defer proxyHealthMutex.RUnlock()
healthInfo := make(map[string]interface{})
for proxy, health := range proxyHealthMaps {
healthInfo[proxy] = map[string]interface{}{
"success_count": health.SuccessCount,
"fail_count": health.FailCount,
"last_check": health.LastCheck.Format(time.RFC3339),
"response_time": health.ResponseTime.String(),
"is_healthy": health.IsHealthy,
}
}
jsonData, err := json.Marshal(healthInfo)
if err != nil {
return C.CString(fmt.Sprintf(`{"error": "序列化健康信息失败: %v"}`, err))
}
return C.CString(string(jsonData))
}
// 导出函数:代理类型管理器
//
//export ProxyTypeManager
func ProxyTypeManager(proxyType, username, password, machineCode *C.char) *C.char {
goProxyType := C.GoString(proxyType)
goUsername := C.GoString(username)
goPassword := C.GoString(password)
goMachineCode := C.GoString(machineCode)
log.Printf("[DEBUG] 代理类型管理器调用: type=%s", goProxyType)
proxyURL, err := proxyTypeManager(goProxyType, goUsername, goPassword, goMachineCode)
if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err)
log.Printf("[ERROR] 代理类型管理器错误: %v", err)
return C.CString(errorMsg)
}
log.Printf("[DEBUG] 代理类型管理器返回: %s", proxyURL)
return C.CString(proxyURL)
}
//// 导出函数释放C字符串内存
////
////export FreeCString
//func FreeCString(str *C.char) {
// C.free(unsafe.Pointer(str))
//}
//
//func main() {
//
//}

1009
zjdydll.go Normal file

File diff suppressed because it is too large Load Diff