diff --git a/dll/kongfz.dll b/dll/kongfz.dll index 50952df..68fea3d 100644 Binary files a/dll/kongfz.dll and b/dll/kongfz.dll differ diff --git a/dll/kongfz.h b/dll/kongfz.h index 67b24cb..7a6bc6a 100644 --- a/dll/kongfz.h +++ b/dll/kongfz.h @@ -23,11 +23,11 @@ extern const char *_GoStringPtr(_GoString_ s); #line 3 "main.go" - #include +#include - // proxyConfig.dll 函数声明 - extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); - extern void FreeCString(char* str); +// proxyConfig.dll 函数声明 +extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); +extern void FreeCString(char* str); #line 1 "cgo-generated-wrapper" @@ -116,13 +116,17 @@ extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* pr // extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData); +// 获取图片URL(官图和拍图)带有店铺过滤 +// +extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* isbn, int shopId, char* proxy, int isLiveImage, int isReturnMsg); + // 获取商品图片(带有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); +extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum); // 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口) // diff --git a/dll/xkongfz.dll b/dll/proxy.dll similarity index 63% rename from dll/xkongfz.dll rename to dll/proxy.dll index 23a978c..3afeee7 100644 Binary files a/dll/xkongfz.dll and b/dll/proxy.dll differ diff --git a/dll/test.h b/dll/proxy.h similarity index 60% rename from dll/test.h rename to dll/proxy.h index 427c73f..171e8ae 100644 --- a/dll/test.h +++ b/dll/proxy.h @@ -21,13 +21,8 @@ extern const char *_GoStringPtr(_GoString_ s); /* Start of preamble from import "C" comments. */ -#line 3 "kfztp.go" - -#include - -// proxyConfig.dll 函数声明 -extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); -extern void FreeCString(char* str); +#line 3 "proxyConfig.go" + #include #line 1 "cgo-generated-wrapper" @@ -92,24 +87,13 @@ extern "C" { #endif -// 获取商品图片(带有Out的都非官方标准接口) +// 导出函数:获取代理健康状态(用于调试) // -extern __declspec(dllexport) char* OutGetImageByIsbn(char* isbn, char* proxy, _Bool isLiveImage, _Bool isReturnMsg); +extern __declspec(dllexport) char* GetProxyHealth(void); -// 获取商品列表通过店铺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); +extern __declspec(dllexport) char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); // 导出函数:释放C字符串内存 // diff --git a/dyso.go b/dyso.go new file mode 100644 index 0000000..902f93f --- /dev/null +++ b/dyso.go @@ -0,0 +1,496 @@ +package main + +/* +#cgo linux LDFLAGS: -ldl +#cgo windows LDFLAGS: -lkernel32 + +#include + +#ifdef _WIN32 +#include +static void* dlopen(const char* filename, int flags) { + return (void*)LoadLibraryA(filename); +} +static void dlclose(void* handle) { + FreeLibrary((HMODULE)handle); +} +static void* dlsym(void* handle, const char* name) { + return (void*)GetProcAddress((HMODULE)handle, name); +} +static const char* dlerror() { + static char buf[256]; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, buf, sizeof(buf), NULL); + return buf; +} +#define RTLD_LAZY 0 +#else +#include +#endif +*/ +import "C" +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "path/filepath" + "runtime" + "unsafe" +) + +// 配置结构 +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"` +} + +// API响应结构 +type APIResponseSO struct { + Success bool `json:"success"` + Message string `json:"message,omitempty"` + GoodsNum string `json:"goods_num,omitempty"` + PNum string `json:"pnum,omitempty"` + Data interface{} `json:"data,omitempty"` + Error string `json:"error,omitempty"` +} + +// APIResp 简化的响应结构体 +type APIRespSO struct { + Success bool `json:"success"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +type SOManager struct { + handle unsafe.Pointer +} + +func NewSOManager(soPath string) (*SOManager, error) { + // 根据平台调整库文件扩展名 + if runtime.GOOS == "windows" { + soPath = changeExtensionToDLL(soPath) + } + // 使用 dlopen 加载 .so 文件 + cSoPath := C.CString(soPath) + defer C.free(unsafe.Pointer(cSoPath)) + + handle := C.dlopen(cSoPath, C.RTLD_LAZY) + if handle == nil { + return nil, fmt.Errorf("加载SO文件失败: %s", C.GoString(C.dlerror())) + } + + return &SOManager{handle: handle}, nil +} + +func (m *SOManager) Close() { + if m.handle != nil { + C.dlclose(m.handle) + } +} + +func changeExtensionToDLL(path string) string { + ext := filepath.Ext(path) + if ext == ".so" { + return path[:len(path)-len(ext)] + ".dll" + } + return path +} + +// 获取函数指针 +func (m *SOManager) getFunction(funcName string) (unsafe.Pointer, error) { + cFuncName := C.CString(funcName) + defer C.free(unsafe.Pointer(cFuncName)) + + symbol := C.dlsym(m.handle, cFuncName) + if symbol == nil { + return nil, fmt.Errorf("找不到函数 %s: %s", funcName, C.GoString(C.dlerror())) + } + return symbol, nil +} + +// 初始化 +func (m *SOManager) Initialize(configJSON string) (string, error) { + function, err := m.getFunction("Initialize") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char) *C.char)(unsafe.Pointer(&function)) + configJSONC := C.CString(configJSON) + defer C.free(unsafe.Pointer(configJSONC)) + result := (*funcPtr)(configJSONC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 登录 +func (m *SOManager) OutLogin(username, password string) (string, error) { + function, err := m.getFunction("OutLogin") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char) *C.char)(unsafe.Pointer(&function)) + usernameC := C.CString(username) + passwordC := C.CString(password) + defer C.free(unsafe.Pointer(usernameC)) + defer C.free(unsafe.Pointer(passwordC)) + result := (*funcPtr)(usernameC, passwordC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取用户信息 +func (m *SOManager) OutGetUserMsg(token string) (string, error) { + function, err := m.getFunction("OutGetUserMsg") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + defer C.free(unsafe.Pointer(tokenC)) + result := (*funcPtr)(tokenC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取商品模版 +func (m *SOManager) OutGetGoodsTplMsg(token, itemId, proxy string) (string, error) { + function, err := m.getFunction("OutGetGoodsTplMsg") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, *C.char) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + itemIdC := C.CString(itemId) + proxyC := C.CString(proxy) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(itemIdC)) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(tokenC, itemIdC, proxyC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取商品列表-已登的店铺 +func (m *SOManager) OutGetGoodsListMsgFromSelfShop(token string, proxy string, + itemSn string, priceMin string, priceMax string, startCreateTime int, + endCreateTime int, requestType string, isItemSnEqual int, page int, size int) (string, error) { + function, err := m.getFunction("OutGetGoodsListMsgFromSelfShop") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, *C.char, *C.char, *C.char, C.int, C.int, *C.char, C.int, C.int, C.int) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + proxyC := C.CString(proxy) + itemSnC := C.CString(itemSn) + priceMinC := C.CString(priceMin) + priceMaxC := C.CString(priceMax) + startCreateTimeC := C.int(startCreateTime) + endCreateTimeC := C.int(endCreateTime) + requestTypeC := C.CString(requestType) + isItemSnEqualC := C.int(isItemSnEqual) + pageC := C.int(page) + sizeC := C.int(size) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(proxyC)) + defer C.free(unsafe.Pointer(itemSnC)) + defer C.free(unsafe.Pointer(priceMinC)) + defer C.free(unsafe.Pointer(priceMaxC)) + defer C.free(unsafe.Pointer(requestTypeC)) + result := (*funcPtr)(tokenC, proxyC, itemSnC, priceMinC, priceMaxC, startCreateTimeC, + endCreateTimeC, requestTypeC, isItemSnEqualC, pageC, sizeC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 删除商品-已登的店铺 +func (m *SOManager) OutDelGoodsFromSelfShop(token, itemId, proxy string) (string, error) { + function, err := m.getFunction("OutDelGoodsFromSelfShop") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, *C.char) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + itemIdC := C.CString(itemId) + proxyC := C.CString(proxy) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(itemIdC)) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(tokenC, itemIdC, proxyC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 新增商品 +func (m *SOManager) OutAddGoods(token, proxy, formData string) (string, error) { + function, err := m.getFunction("OutAddGoods") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, *C.char) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + proxyC := C.CString(proxy) + formDataC := C.CString(formData) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(proxyC)) + defer C.free(unsafe.Pointer(formDataC)) + result := (*funcPtr)(tokenC, proxyC, formDataC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取图片URL(官图和拍图)带有店铺过滤 +func (m *SOManager) OutGetImageFilterShopId(token string, isbn string, shopId int, proxy string, isLiveImage int, isReturnMsg int) (string, error) { + function, err := m.getFunction("OutGetImageFilterShopId") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, C.int, *C.char, C.int, C.int) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + isbnC := C.CString(isbn) + shopIdC := C.int(shopId) + proxyC := C.CString(proxy) + isLiveImageC := C.int(isLiveImage) + isReturnMsgC := C.int(isReturnMsg) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(isbnC)) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(tokenC, isbnC, shopIdC, proxyC, isLiveImageC, isReturnMsgC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取商品图片 +func (m *SOManager) OutGetImageByIsbn(token string, isbn string, proxy string, isLiveImage int, isReturnMsg int) (string, error) { + function, err := m.getFunction("OutGetImageByIsbn") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char, *C.char, C.int, C.int) *C.char)(unsafe.Pointer(&function)) + tokenC := C.CString(token) + isbnC := C.CString(isbn) + proxyC := C.CString(proxy) + isLiveImageC := C.int(isLiveImage) + isReturnMsgC := C.int(isReturnMsg) + defer C.free(unsafe.Pointer(tokenC)) + defer C.free(unsafe.Pointer(isbnC)) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(tokenC, isbnC, proxyC, isLiveImageC, isReturnMsgC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取商品列表通过店铺ID +func (m *SOManager) OutGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage int, sortType string, + sort string, priceMin float32, priceMax float32, pageNum, returnNum int) (string, error) { + function, err := m.getFunction("OutGetGoodsListMsgByShopId") + if err != nil { + return "", err + } + funcPtr := (*func(C.int, *C.char, C.int, C.int, *C.char, *C.char, C.double, C.double, C.int, C.int) *C.char)(unsafe.Pointer(&function)) + shopIdC := C.int(shopId) + proxyC := C.CString(proxy) + retPriceC := C.int(retPrice) + isImageC := C.int(isImage) + sortTypeC := C.CString(sortType) + sortC := C.CString(sort) + priceMinC := C.double(priceMin) + priceMaxC := C.double(priceMax) + pageNumC := C.int(pageNum) + returnNumC := C.int(returnNum) + defer C.free(unsafe.Pointer(proxyC)) + defer C.free(unsafe.Pointer(sortTypeC)) + defer C.free(unsafe.Pointer(sortC)) + result := (*funcPtr)(shopIdC, proxyC, retPriceC, isImageC, sortTypeC, sortC, priceMinC, priceMaxC, pageNumC, returnNumC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取商品信息通过商品详情链接 +func (m *SOManager) OutGetGoodsMsgByDetailUrl(detailUrl, proxy string) (string, error) { + function, err := m.getFunction("OutGetGoodsMsgByDetailUrl") + if err != nil { + return "", err + } + funcPtr := (*func(*C.char, *C.char) *C.char)(unsafe.Pointer(&function)) + detailUrlC := C.CString(detailUrl) + proxyC := C.CString(proxy) + defer C.free(unsafe.Pointer(detailUrlC)) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(detailUrlC, proxyC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 获取销量榜商品列表 +func (m *SOManager) OutGetTopGoodsListMsg(catId int, proxy string) (string, error) { + function, err := m.getFunction("OutGetTopGoodsListMsg") + if err != nil { + return "", err + } + funcPtr := (*func(C.int, *C.char) *C.char)(unsafe.Pointer(&function)) + catIdC := C.int(catId) + proxyC := C.CString(proxy) + defer C.free(unsafe.Pointer(proxyC)) + result := (*funcPtr)(catIdC, proxyC) + defer C.free(unsafe.Pointer(result)) + return C.GoString(result), nil +} + +// 创建默认配置 +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 = "so/proxyConfig.so" + return configs +} + +// 获取当前可执行文件所在目录 +func getExecutableDir() string { + exePath, err := os.Executable() + if err != nil { + return "." + } + return filepath.Dir(exePath) +} + +func main() { + http.HandleFunc("/api/kfzShopBookInfo", handleGetKFZShopBookInfo) + + port := "8080" + log.Printf("🚀 服务器启动在端口 %s", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + log.Fatalf("服务器启动失败: %v", err) + } +} + +func handleGetKFZShopBookInfo(w http.ResponseWriter, r *http.Request) { + // 设置响应头 + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + // 只支持GET请求 + if r.Method != http.MethodGet { + sendErrorResponse(w, http.StatusMethodNotAllowed, "只支持GET方法") + return + } + + // 加载SO + manager, err := NewSOManager("so/kongfz.so") + if err != nil { + log.Printf("初始化SO管理器失败: %v", err) + sendErrorResponse(w, http.StatusInternalServerError, "初始化SO管理器失败") + return + } + defer manager.Close() + log.Println("✅ SO加载成功") + // 使用默认配置初始化 + config := createDefaultConfig() + configJSON, err := json.Marshal(config) + if err != nil { + log.Printf("序列化配置失败: %v", err) + sendErrorResponse(w, http.StatusInternalServerError, "序列化配置失败") + return + } + result, err := manager.Initialize(string(configJSON)) + if err != nil { + log.Printf("初始化失败: %v", err) + sendErrorResponse(w, http.StatusInternalServerError, "初始化失败") + return + } + var initResp APIResponseSO + if err := json.Unmarshal([]byte(result), &initResp); err != nil { + log.Printf("解析初始化响应失败: %v", err) + sendErrorResponse(w, http.StatusInternalServerError, "解析初始化响应失败") + return + } + if !initResp.Success { + log.Printf("初始化失败: %s", initResp.Message) + sendErrorResponse(w, http.StatusInternalServerError, "初始化失败: "+initResp.Message) + return + } + log.Println("✅ SO初始化成功") + + // 登录示例 + user, err := manager.OutLogin("18904056801", "Long6166@@") + if err != nil { + log.Printf("登录失败: %v", err) + } else { + log.Printf("登录结果: %s", user) + } + + // 解析token + var data APIResponseSO + if err := json.Unmarshal([]byte(user), &data); err != nil { + log.Printf("解析登录响应失败: %v", err) + } else { + var token string + if dataMap, ok := data.Data.(map[string]interface{}); ok { + if tk, exists := dataMap["token"]; exists { + token = tk.(string) + log.Printf("获取到Token: %s", token) + + // 使用token获取用户信息 + msg, err := manager.OutGetUserMsg(token) + if err != nil { + log.Printf("获取用户信息失败: %v", err) + } else { + log.Printf("用户信息: %s", msg) + } + } else { + log.Println("Token 不存在") + } + } else { + log.Println("Data 格式不正确") + } + } + // 返回成功响应 + response := APIRespSO{ + Success: true, + Message: "SO调用成功", + } + json.NewEncoder(w).Encode(response) +} + +// 发送错误响应 +func sendErrorResponse(w http.ResponseWriter, statusCode int, message string) { + response := APIRespSO{ + Success: false, + Message: message, + } + w.WriteHeader(statusCode) + json.NewEncoder(w).Encode(response) +} diff --git a/iniConfigUtil.go b/iniConfigUtil.go deleted file mode 100644 index 05e3f54..0000000 --- a/iniConfigUtil.go +++ /dev/null @@ -1,378 +0,0 @@ -// 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 -} diff --git a/kfzgw_linux b/kfzgw_linux new file mode 100644 index 0000000..f74c2c0 Binary files /dev/null and b/kfzgw_linux differ diff --git a/dll/test.dll b/kfzgw_so_linux similarity index 58% rename from dll/test.dll rename to kfzgw_so_linux index c99fc3d..25660e4 100644 Binary files a/dll/test.dll and b/kfzgw_so_linux differ diff --git a/kfztp.go b/kfztp.go index 39d76f2..b9fd946 100644 --- a/kfztp.go +++ b/kfztp.go @@ -1,2011 +1,135 @@ package main -import "C" +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "time" + + "github.com/parnurzeal/gorequest" +) + +// 获取商品模版 +func outGetGoodsTplMsgLinux(token, itemId, proxy string) (map[string]interface{}, error) { + if token == "" { + return nil, fmt.Errorf("请先登录获取Token") + } + + url := fmt.Sprintf("https://seller.kongfz.com/pc/itemInfo/getTplFields?itemId=%s&isClone=1&v=%d", + itemId, time.Now().Unix()) + + log.Printf("请求URL: %s", url) + + // 创建HTTP客户端 + request := gorequest.New() + + // 设置代理(如果有提供代理URL) + if proxy != "" { + request.Proxy(proxy) + log.Printf("使用代理: %s", 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(30 * time.Second). + End() + + if len(errs) > 0 { + return nil, fmt.Errorf("请求失败: %v", errs) + } + + // 检查HTTP状态码 + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP错误: %d - %s", resp.StatusCode, resp.Status) + } + + var data map[string]interface{} + if err := json.Unmarshal([]byte(body), &data); err != nil { + return nil, fmt.Errorf("解析JSON失败: %w", err) + } + + // 检查API响应状态 + if val, ok := data["status"].(float64); ok && val == 1 { + return data, nil + } + + return nil, fmt.Errorf("API返回错误: %+v", data) +} -///* -//#include -// -//// proxyConfig.dll 函数声明 -//extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); -//extern void FreeCString(char* str); -//*/ -//import "C" -//import ( -// "context" -// "encoding/json" -// "fmt" -// "io" -// "log" -// "net/http" -// "os" -// "path/filepath" -// "regexp" -// "strconv" -// "strings" -// "sync" -// "syscall" -// "time" -// "unsafe" -// -// "github.com/PuerkitoBio/goquery" -// "github.com/chromedp/chromedp" -// "github.com/parnurzeal/gorequest" -//) -// -//const ( -// UseProxy = "proxy" // 使用代理 -// NotProxy = "direct" // 不用代理代理 -//) -// -//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"` -// } `ini:"app" json:"app"` -// -// API struct { -// LoginURL string `ini:"api.login_url" json:"login_url" default:"https://login.kongfz.com/Pc/Login/account"` -// 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"` -// 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"` -// } `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"` -//} -// -//// 条目详情结构体 -//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 -//} -// -//// API响应结构 -//type APIResponse struct { -// Success bool `json:"success"` -// Message string `json:"message,omitempty"` -// GoodsNum string `json:"goods_num,omitempty"` -// PNum string `json:"pnum,omitempty"` -// Data interface{} `json:"data,omitempty"` -// Error string `json:"error,omitempty"` -//} -// -//// 全局变量 -//var ( -// cf Config // 配置信息 -// tailProxyMu sync.Mutex // 互斥锁 -// proxyFailCount int -// -// // 全局代理管理器 -// globalProxyManager *ProxyConfigManager -// proxyManagerOnce sync.Once -// proxyManagerInitErr error -//) -// -//// ProductInfo 商品信息结构 -//type ProductInfo struct { -// ItemID string `json:"itemId"` -// BookName string `json:"bookName"` -// Price string `json:"price"` -// ShippingFee string `json:"shippingFee"` -//} -// -//// ProductResponse 响应结构 -//type ProductResponse struct { -// Success bool `json:"success"` -// Message string `json:"message,omitempty"` -// Data []ProductInfo `json:"data,omitempty"` -//} -// -//// 并行获取详情的结果结构 -//type DetailResult struct { -// URL string -// Doc *goquery.Document -// Error error -// Index int -//} -// -//// 第一阶段:收集基本信息并识别需要详情的项目 -//type BookItem struct { -// Book BookInfo -// Selection *goquery.Selection -// HasDetail bool -// DetailURL string -// Index int -//} -// -//// 销量榜目录分类列表响应结构体 -//type SalesCategoryResponse struct { -// Status bool `json:"status"` -// Result []SalesCategory `json:"result"` -// ErrMessage string `json:"errMessage"` -// ErrCode int `json:"errCode"` -//} -// -//// 分类项结构体 -//type SalesCategory struct { -// Key string `json:"key"` -// Value int `json:"value"` -//} -// -//// 图书详情响应结构体 -//type BookDetailResponse struct { -// Status bool `json:"status"` -// Result BookList `json:"result"` -// ErrMessage string `json:"errMessage"` -// ErrCode int `json:"errCode"` -//} -// -//// 图书列表结构体 -//type BookList struct { -// Current int `json:"current"` -// Data []BookInformation `json:"data"` -// Total int `json:"total"` -//} -// -//// 图书信息结构体 -//type BookInformation struct { -// Author string `json:"author"` -// BookName string `json:"bookName"` -// ContentIntroduction string `json:"contentIntroduction"` -// ImgUrl string `json:"imgUrl"` -// Isbn string `json:"isbn"` -// ItemUrls ItemUrls `json:"itemUrls"` -// Mid int `json:"mid"` -// 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"` -//} -// -//// 作者信息结构体 -//type AuthorInfo struct { -// Name string `json:"name"` -// OriName string `json:"oriName"` -// Nationality string `json:"nationality"` -// Role string `json:"role"` -// Url string `json:"url"` -//} -// -//// 商品链接结构体 -//type ItemUrls struct { -// AppUrl string `json:"appUrl"` -// MUrl string `json:"mUrl"` -// MiniUrl string `json:"miniUrl"` -// PcUrl string `json:"pcUrl"` -//} -// -//// ProxyConfigManager 代理配置DLL管理器 -//type ProxyConfigManager struct { -// dll *syscall.DLL -//} -// -//func NewProxyConfigManager(dllPath string) (*ProxyConfigManager, error) { -// dll, err := syscall.LoadDLL(dllPath) -// if err != nil { -// return nil, fmt.Errorf("加载代理配置DLL失败: %v", err) -// } -// return &ProxyConfigManager{dll: dll}, nil -//} -// -//func (m *ProxyConfigManager) 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 -//} -// -//// 获取销量榜所有分类中图书的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 -//} -// -//// 辅助函数:去除重复的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 -//} -// -//// 获取销量榜的图书信息 -//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 -//} -// -//// 通过店铺ID获取孔网实拍图和条目信息 -//func getKFZShopBookInfo(fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp string) (books []BookInfo, goodsNum string, pNum string, err error) { -// // 判断店铺ID -// if shopId == "" { -// return nil, "", "", fmt.Errorf("店铺编码为空!") -// } -// // 判断是否有图片,设置默认值0 -// if isImage == "" { -// isImage = "0" -// } else { -// _, isImageErr := strconv.Atoi(isImage) -// if isImageErr != nil { -// return nil, "", "", fmt.Errorf("是否有图片输入必须是数字!", isImageErr) -// } -// } -// // 判断一页图书数量,设置默认值100 -// if bookNum == "" { -// bookNum = "100" -// } else { -// isBookNum, bookNumErr := strconv.Atoi(bookNum) -// if bookNumErr != nil { -// return nil, "", "", fmt.Errorf("图书数量输入必须是数字!", bookNumErr) -// } -// if isBookNum < 0 && isBookNum > 100 { -// return nil, "", "", fmt.Errorf("图书数量输入错误:只能出入(0~100区间)") -// } -// } -// // 判断页数,设置默认值1 -// if pageNum == "" { -// pageNum = "1" -// } else { -// _, pageNumErr := strconv.Atoi(pageNum) -// if pageNumErr != nil { -// return nil, "", "", fmt.Errorf("页数输入必须是数字!", pageNumErr) -// } -// } -// // 判断排序类型,设置默认值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) -// } -// } -// // 判断价格下限,设置默认值0 -// if priceDown == "" { -// priceDown = "0" -// } else { -// _, priceDownErr := strconv.ParseFloat(priceDown, 64) -// if priceDownErr != nil { -// return nil, "", "", fmt.Errorf("价格输入必须是数字!", priceDownErr) -// } -// } -// // 判断价格上限,设置默认值0 -// if priceUp == "" { -// priceUp = "0" -// } else { -// _, priceUpErr := strconv.ParseFloat(priceUp, 64) -// if priceUpErr != nil { -// return nil, "", "", fmt.Errorf("价格输入必须是数字!", priceUpErr) -// } -// } -// // 调用的url -// url := fmt.Sprintf("https://shop.kongfz.com/%s/all/%s_%s_0_0_%s_%s_%s_%s_%s", -// shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp) -// log.Printf("调用的URL: %s", url) -// // 并行获取商品信息(使用优化后的Chromedp) -// response, err := FetchProductInfoWithChromedp(url) -// if err != nil { -// fmt.Printf("❌ 获取商品信息失败: %v", err) -// } -// // 发送请求 -// doc, err := fetchDocument(fetchMode, proxyType, username, password, machineCode, url) -// -// // 全部商品数量 -// 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") -// if infoDiv.Length() > 0 { -// item := infoDiv.Find("div.item.clearfix") -// var bookItems []BookItem -// -// for i := 0; i < item.Length(); i++ { -// s := item.Eq(i) -// book := BookInfo{} -// // 书名 -// book.BookName = strings.TrimSpace(s.Find("div.title a.link").Text()) -// // 作者 -// book.Author = extractNormalText(s, "作者") -// // 出版社 -// book.Publisher = extractNormalText(s, "出版社") -// // 出版时间,提取出版时间并转换为时间戳 -// pubTimeStr := extractNormalText(s, "出版时间") -// if pubTimeStr != "" { -// // 尝试不同的时间格式 -// formats := []string{"2006-01", "2006-01-02", "2006"} -// for _, format := range formats { -// if t, err := time.Parse(format, pubTimeStr); err == nil { -// book.PublicationTime = t.Unix() -// break -// } -// } -// } -// // 提取ISBN -// book.ISBN = strings.TrimSpace(s.AttrOr("isbn", "")) -// // 版次 -// book.Edition = extractNormalText(s, "版次") -// // 印刷时间 -// book.PrintTime = extractNormalText(s, "印刷时间") -// // 装帧 -// book.BindingLayout = extractNormalText(s, "装帧") -// // 开本 -// book.Format = extractNormalText(s, "开本") -// // 纸张 -// book.Paper = extractNormalText(s, "纸张") -// // 字数 -// book.Wordage = extractNormalText(s, "字数") -// // 售价,提取价格(优先从元素属性获取) -// book.SellingPrice = s.AttrOr("price", "") -// if book.SellingPrice == "" { -// book.SellingPrice = strings.TrimSpace(s.Find("div.price span.bold").Text()) -// } -// // 提取图片 -// if img, exists := s.Find("div.item-img img").Attr("src"); exists { -// book.BookPicS = img -// } -// // 快递费 -// val, exists := s.Attr("itemid") -// for in, p := range response.Data { -// if exists { -// if val == p.ItemID { -// book.ExpressDeliveryFee = p.ShippingFee -// in = in + 1 -// } -// } -// } -// // 提取品相 -// book.Condition = strings.TrimSpace(s.Find("div.quality").Text()) -// -// // 检查是否需要获取详情 -// hasDetail := s.Find("div.zl-isbn-info").Length() > 0 -// detailURl := "" -// if hasDetail { -// detailURl = s.Find("a.img-box").AttrOr("href", "") -// } -// -// bookItems = append(bookItems, BookItem{ -// Book: book, -// Selection: s, -// HasDetail: hasDetail, -// DetailURL: detailURl, -// Index: i, -// }) -// } -// -// detailResults := fetchAllDetailsParallel(fetchMode, proxyType, username, password, machineCode, bookItems) -// books = mergeBookDetails(bookItems, detailResults) -// } -// return books, goodsNum, pNum, nil -//} -// -//// 并行获取所有详情 -//func fetchAllDetailsParallel(fetchMode, proxyType, username, password, machineCode string, bookItems []BookItem) map[string]*DetailResult { -// // 收集所有需要获取详情的URL -// var detailTasks []struct { -// URL string -// Index int -// } -// -// for _, item := range bookItems { -// if item.HasDetail && item.DetailURL != "" { -// detailTasks = append(detailTasks, struct { -// URL string -// Index int -// }{ -// URL: item.DetailURL, -// Index: item.Index, -// }) -// } -// } -// -// if len(detailTasks) == 0 { -// return make(map[string]*DetailResult) -// } -// -// type taskResult struct { -// URL string -// Doc *goquery.Document -// Error error -// Index int -// } -// -// ch := make(chan taskResult, len(detailTasks)) -// var wg sync.WaitGroup -// -// // 限制并发数,避免过多连接 -// sem := make(chan struct{}, 5) // 最大5个并发 -// -// for _, task := range detailTasks { -// wg.Add(1) -// go func(url string, index int) { -// defer wg.Done() -// sem <- struct{}{} // 获取信号量 -// defer func() { <-sem }() // 释放信号量 -// -// doc, err := fetchDocument(fetchMode, proxyType, username, password, machineCode, url) -// ch <- taskResult{ -// URL: url, -// Doc: doc, -// Error: err, -// Index: index, -// } -// }(task.URL, task.Index) -// } -// -// wg.Wait() -// close(ch) -// -// results := make(map[string]*DetailResult) -// for res := range ch { -// results[res.URL] = &DetailResult{ -// URL: res.URL, -// Doc: res.Doc, -// Error: res.Error, -// Index: res.Index, -// } -// } -// -// return results -//} -// -//// 获取Document文档(带重试机制) -//func fetchDocument(fetchMode, proxyType, username, password, machineCode, url string) (*goquery.Document, error) { -// log.Printf("调用的URL: %s", url) -// maxRetries := cf.App.MaxRetryTimes -// var detailsResp *http.Response -// var errors []error -// var detailsBody []byte -// for attempt := 0; attempt < maxRetries; attempt++ { -// if attempt > 0 { -// log.Printf("第 %d 次重试请求...", attempt) -// // 重试前等待,使用指数退避策略 -// waitTime := time.Duration(attempt*attempt) * cf.App.RateLimitDelay // 平方退避 -// log.Printf("等待 %v 后重试", waitTime) -// time.Sleep(waitTime) -// } -// if fetchMode == NotProxy { -// 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() -// } else if fetchMode == UseProxy { -// detailsReq := XxProxyRequest(proxyType, username, password, machineCode). -// 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"). -// Set("Connection", "close") // 每次请求关闭连接 -// detailsResp, _, errors = detailsReq.End() -// } else { -// errors = append(errors, fmt.Errorf("请求失败,选择模式错误!")) -// } -// // 检查是否需要重试 -// 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) -// } -// if detailsResp.StatusCode != http.StatusOK { -// return nil, fmt.Errorf("HTTP错误: %s", detailsResp.Status) -// } -// -// detailsBody, err := io.ReadAll(detailsResp.Body) -// if err != nil { -// return nil, err -// } -// -// return goquery.NewDocumentFromReader(strings.NewReader(string(detailsBody))) -//} -// -//// 合并基本信息和详情 -//func mergeBookDetails(bookItems []BookItem, detailResults map[string]*DetailResult) []BookInfo { -// var books []BookInfo -// -// for _, item := range bookItems { -// book := item.Book -// -// // 如果有详情且获取成功,补充详细信息 -// if item.HasDetail && item.DetailURL != "" { -// if result, exists := detailResults[item.DetailURL]; exists && result.Error == nil && result.Doc != nil { -// detailsDiv := result.Doc.Find("div.detail-lists.clear-fix") -// if detailsDiv.Length() > 0 { -// selection := detailsDiv.Find("li") -// book.Author = extractNormalDetails(selection, "作者") -// book.Publisher = extractNormalDetails(selection, "出版社") -// detailsTime := extractNormalDetails(selection, "出版时间") -// if detailsTime != "" { -// formats := []string{"2006-01", "2006-01-02", "2006"} -// for _, format := range formats { -// if t, err := time.Parse(format, detailsTime); err == nil { -// book.PublicationTime = t.Unix() -// break -// } -// } -// } -// book.ISBN = extractNormalDetails(selection, "ISBN") -// book.Edition = extractNormalDetails(selection, "版次") -// book.FixPrice = extractNormalDetails(selection, "定价") -// book.BindingLayout = extractNormalDetails(selection, "装帧") -// book.Format = extractNormalDetails(selection, "开本") -// book.Pages = extractNormalDetails(selection, "页数") -// book.Wordage = extractNormalDetails(selection, "字数") -// } -// } -// } -// -// books = append(books, book) -// } -// return books -//} -// -//// 根据url获取单个图书详情信息 -//func getUrlBookDetails(fetchMode, proxyType, username, password, machineCode, url string) (books []BookInfo, err error) { -// document, err := fetchDocument(fetchMode, proxyType, username, password, machineCode, url) -// if err != nil { -// return nil, err -// } -// -// fee, err := FetchBookDetailsShippingFee(url) -// 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) -// // 快递费 -// if fee != "" { -// if fee == "包邮" { -// book.ExpressDeliveryFee = "0" -// } else { -// book.ExpressDeliveryFee = fee -// } -// } -// books = append(books, book) -// return books, nil -//} -// -//// 获取店铺页面的所有快递费用 -//func FetchProductInfoWithChromedp(url string) (*ProductResponse, error) { -// // 创建上下文 -// opts := append(chromedp.DefaultExecAllocatorOptions[:], -// chromedp.Flag("headless", true), -// chromedp.Flag("disable-gpu", true), -// chromedp.Flag("no-sandbox", true), -// chromedp.Flag("disable-dev-shm-usage", true), -// chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"), -// // 禁用图片加载,加快速度 -// chromedp.Flag("blink-settings", "imagesEnabled=false"), -// ) -// // 启动浏览器 -// allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) -// defer cancel() -// // 创建浏览器上下文 -// ctx, cancel := chromedp.NewContext(allocCtx) -// defer cancel() -// // 设置超时 -// ctx, cancel = context.WithTimeout(ctx, 60*time.Second) -// defer cancel() -// log.Printf("[CHROMEDP] 开始导航到URL: %s", url) -// // 第一步:先导航到页面并等待完全加载 -// err := chromedp.Run(ctx, -// // 导航到目标页面 -// chromedp.Navigate(url), -// -// // 等待页面初步加载 -// chromedp.WaitReady("body", chromedp.ByQuery), -// chromedp.Sleep(3*time.Second), -// -// // 第二步:刷新页面,重新加载动态内容 -// chromedp.Reload(), -// -// // 等待刷新后页面加载 -// chromedp.WaitReady("body", chromedp.ByQuery), -// chromedp.Sleep(5*time.Second), -// -// // 等待关键元素出现 -// chromedp.WaitVisible(".item.clearfix", chromedp.ByQuery), -// -// // 滚动页面以触发动态加载 -// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight / 3)`, nil), -// chromedp.Sleep(2*time.Second), -// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight / 2)`, nil), -// chromedp.Sleep(2*time.Second), -// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight)`, nil), -// chromedp.Sleep(3*time.Second), -// chromedp.Evaluate(`window.scrollTo(0, 0)`, nil), -// chromedp.Sleep(1*time.Second), -// ) -// if err != nil { -// log.Printf("[CHROMEDP] 页面导航失败: %v", err) -// return nil, fmt.Errorf("页面导航失败: %v", err) -// } -// log.Printf("[CHROMEDP] 页面导航完成,开始提取数据") -// var evalResult map[string]interface{} -// // 执行任务 -// err = chromedp.Run(ctx, -// // 导航到目标页面 -// chromedp.Navigate(url), -// -// // 执行 JavaScript 来提取商品信息 -// chromedp.Evaluate(`(function() { -// console.log("开始提取商品信息..."); -// try { -// let productData = []; -// // 查找所有商品项 - 针对孔夫子旧书网的特定选择器 -// const itemSelectors = [ -// '.item.clearfix', -// '.list-item', -// '.product-item', -// '.goods-item', -// '[class*="item"]' -// ]; -// let items = []; -// for (let selector of itemSelectors) { -// const found = document.querySelectorAll(selector); -// if (found.length > 0) { -// items = found; -// console.log("使用选择器:", selector, "找到项目数:", items.length); -// break; -// } -// } -// -// if (items.length === 0) { -// // 如果没有找到特定选择器,尝试查找任何看起来像商品的项目 -// items = document.querySelectorAll('div[class*="item"], li[class*="item"]'); -// console.log("使用通用选择器找到项目数:", items.length); -// } -// console.log("总共找到商品项:", items.length); -// // 遍历每个商品项 -// items.forEach((item, index) => { -// // 提取 itemid -// let itemId = ''; -// -// // 方法1: 从元素属性获取 -// if (item.hasAttribute('itemid')) { -// itemId = item.getAttribute('itemid'); -// } -// -// // 方法2: 从数据属性获取 -// if (!itemId && item.hasAttribute('data-itemid')) { -// itemId = item.getAttribute('data-itemid'); -// } -// -// // 方法3: 从ID属性获取 -// if (!itemId && item.hasAttribute('id')) { -// const id = item.getAttribute('id'); -// if (id.includes('item') || id.includes('product')) { -// itemId = id; -// } -// } -// -// // 方法4: 从链接中提取itemid -// if (!itemId) { -// const links = item.querySelectorAll('a[href*="item"], a[href*="product"]'); -// for (let link of links) { -// const href = link.getAttribute('href'); -// if (href) { -// const itemMatch = href.match(/(?:item|product)[_-]?(\d+)/i); -// if (itemMatch) { -// itemId = itemMatch[1]; -// break; -// } -// } -// } -// } -// -// // 方法5: 从子元素中查找itemid -// if (!itemId) { -// const childWithItemId = item.querySelector('[itemid], [data-itemid]'); -// if (childWithItemId) { -// if (childWithItemId.hasAttribute('itemid')) { -// itemId = childWithItemId.getAttribute('itemid'); -// } else if (childWithItemId.hasAttribute('data-itemid')) { -// itemId = childWithItemId.getAttribute('data-itemid'); -// } -// } -// } -// -// // 提取书名 -// let bookName = ''; -// const titleSelectors = [ -// '.title a', -// '.book-title', -// '.item-title', -// '.name a', -// 'h3 a', -// 'h4 a', -// '.link', -// 'a[title]' -// ]; -// -// for (let selector of titleSelectors) { -// const titleEl = item.querySelector(selector); -// if (titleEl) { -// bookName = titleEl.textContent.trim(); -// if (bookName && bookName.length > 1) { -// break; -// } -// } -// } -// -// // 如果没找到,尝试在item内查找任何文本作为书名 -// if (!bookName) { -// const textContent = item.textContent; -// // 尝试提取看起来像书名的文本(较长的文本块) -// const lines = textContent.split('\n').map(line => line.trim()).filter(line => line.length > 5); -// if (lines.length > 0) { -// bookName = lines[0]; -// } -// } -// -// // 提取价格 -// let price = ''; -// const priceSelectors = [ -// '.price span', -// '.price .bold', -// '.current-price', -// '.sell-price', -// '.cost', -// '.money', -// '[class*="price"]', -// 'strong' -// ]; -// -// for (let selector of priceSelectors) { -// const priceEl = item.querySelector(selector); -// if (priceEl) { -// let priceText = priceEl.textContent.trim(); -// // 清理价格文本,保留数字和小数点 -// priceText = priceText.replace(/[^\d\.]/g, ''); -// if (priceText && !isNaN(parseFloat(priceText))) { -// price = '¥' + priceText; -// break; -// } -// } -// } -// -// // 如果没找到价格,尝试在文本中查找价格模式 -// if (!price) { -// const itemText = item.textContent; -// const priceMatch = itemText.match(/[¥¥]?\s*(\d+\.?\d*)/); -// if (priceMatch) { -// price = '¥' + priceMatch[1]; -// } -// } -// -// // 提取快递费用 -// let shippingFee = ''; -// const shippingSelectors = [ -// '.ship-fee', -// '.shipping-fee', -// '.express-fee', -// '.freight', -// '.postage', -// '[class*="fee"]', -// '[class*="快递"]', -// '[class*="运费"]' -// ]; -// -// for (let selector of shippingSelectors) { -// const shippingEl = item.querySelector(selector); -// if (shippingEl) { -// shippingFee = shippingEl.textContent.trim(); -// if (shippingFee) break; -// } -// } -// -// // 如果没找到运费元素,尝试在文本中查找运费关键词 -// if (!shippingFee) { -// const itemText = item.textContent; -// const feeMatches = itemText.match(/(运费[::]\s*[^\s\n]+)|(快递[::]\s*[^\s\n]+)|(邮费[::]\s*[^\s\n]+)/); -// if (feeMatches) { -// shippingFee = feeMatches[0]; -// } else if (itemText.includes('包邮') || itemText.includes('免运费')) { -// shippingFee = '包邮'; -// } else { -// shippingFee = '运费待确认'; -// } -// } -// -// // 清理书名(移除过长的文本) -// if (bookName && bookName.length > 100) { -// bookName = bookName.substring(0, 100) + '...'; -// } -// -// // 如果还没有itemid,生成一个基于索引的ID -// if (!itemId) { -// itemId = 'item_' + (index + 1); -// } -// -// // 添加到数据中 -// productData.push({ -// itemId: itemId, -// bookName: bookName || '商品${index + 1}', -// price: price || '价格待确认', -// shippingFee: shippingFee -// }); -// }); -// -// // 过滤掉明显无效的数据 -// productData = productData.filter(item => -// (item.bookName !== '商品1' || item.price !== '价格待确认') && -// item.bookName && item.bookName.length > 0 -// ); -// -// console.log("处理后商品数量:", productData.length); -// if (productData.length > 0) { -// console.log("前3个商品示例:", productData.slice(0, 3)); -// } -// -// return { -// success: true, -// message: "成功提取商品信息", -// data: productData -// }; -// -// } catch(error) { -// console.error("提取商品信息时出错:", error); -// return { -// success: false, -// message: "提取商品信息时出错: " + error.message, -// data: [] -// }; -// } -// })()`, &evalResult), -// ) -// -// if err != nil { -// return nil, fmt.Errorf("chromedp执行失败: %v", err) -// } -// -// // 处理 JavaScript 执行结果 -// response := &ProductResponse{} -// -// // 转换结果数据 -// if success, ok := evalResult["success"].(bool); ok { -// response.Success = success -// } -// if message, ok := evalResult["message"].(string); ok { -// response.Message = message -// } -// if data, ok := evalResult["data"].([]interface{}); ok { -// for _, item := range data { -// if productMap, ok := item.(map[string]interface{}); ok { -// product := ProductInfo{} -// if itemId, ok := productMap["itemId"].(string); ok { -// product.ItemID = itemId -// } -// if bookName, ok := productMap["bookName"].(string); ok { -// product.BookName = bookName -// } -// if price, ok := productMap["price"].(string); ok { -// product.Price = price -// } -// if shippingFee, ok := productMap["shippingFee"].(string); ok { -// info := extractShippingInfo(shippingFee) -// product.ShippingFee = info -// } -// response.Data = append(response.Data, product) -// } -// } -// } -// -// return response, nil -//} -// -//// 获取详情页的快递费用 -//func FetchBookDetailsShippingFee(url string) (string, error) { -// // 创建上下文 -// opts := append(chromedp.DefaultExecAllocatorOptions[:], -// chromedp.Flag("headless", true), -// chromedp.Flag("disable-gpu", true), -// chromedp.Flag("no-sandbox", true), -// chromedp.Flag("disable-dev-shm-usage", true), -// chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"), -// // 禁用图片加载,加快速度 -// chromedp.Flag("blink-settings", "imagesEnabled=false"), -// ) -// // 启动浏览器 -// allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) -// defer cancel() -// // 创建浏览器上下文 -// ctx, cancel := chromedp.NewContext(allocCtx) -// defer cancel() -// // 设置超时 -// ctx, cancel = context.WithTimeout(ctx, 60*time.Second) -// defer cancel() -// log.Printf("[CHROMEDP] 开始导航到URL: %s", url) -// var result string -// -// // 增强的 JavaScript 脚本 -// enhancedJSScript := ` -// (function() { -// // 查找包含快递费用的元素 -// const elements = document.querySelectorAll('*'); -// let shippingFee = null; -// -// elements.forEach(element => { -// if (element.textContent.includes('快递¥')) { -// const match = element.textContent.match(/快递¥(\d+\.\d{2})/); -// if (match) { -// shippingFee = match[1]; -// console.log('找到快递费用: ¥' + shippingFee); -// } -// } -// // 匹配格式2: 快递:包邮 -// else if (element.textContent.includes('快递:包邮') || element.textContent.includes('快递:包邮')) { -// shippingFee = '包邮'; -// console.log('找到快递费用: 包邮'); -// } -// // 匹配格式3: 快递 包邮 (包含空格变体) -// else if (element.textContent.match(/快递[::\s]包邮/)) { -// shippingFee = '包邮'; -// console.log('找到快递费用: 包邮'); -// } -// }); -// -// if (!shippingFee) { -// // 如果没找到,尝试搜索整个页面 -// const pageText = document.body.innerText; -// const match = pageText.match(/快递¥(\d+\.\d{2})/); -// if (match) { -// shippingFee = match[1]; -// console.log('找到快递费用: ¥' + shippingFee); -// } -// // 尝试匹配包邮 -// else if (pageText.match(/快递[::\s]包邮/)) { -// shippingFee = '包邮'; -// console.log('找到快递费用: 包邮'); -// } -// // 尝试其他包邮表述 -// else if (pageText.includes('包邮') && pageText.includes('快递')) { -// shippingFee = '包邮'; -// console.log('找到快递费用: 包邮'); -// } -// else { -// console.log('未找到快递费用信息'); -// } -// } -// return shippingFee; -// })();` -// // 先导航到页面并等待完全加载 -// err := chromedp.Run(ctx, -// // 导航到目标页面 -// chromedp.Navigate(url), -// // 刷新页面,重新加载动态内容 -// chromedp.Reload(), -// // 等待刷新后页面加载 -// chromedp.WaitReady("body", chromedp.ByQuery), -// chromedp.Sleep(8*time.Second), -// // 执行 JavaScript 来提取商品信息 -// chromedp.Evaluate(enhancedJSScript, &result), -// ) -// if err != nil { -// log.Printf("[CHROMEDP] 页面导航失败: %v", err) -// return "", fmt.Errorf("页面导航失败: %v", err) -// } -// log.Printf("[CHROMEDP] 页面导航完成,开始提取数据") -// -// if err != nil { -// return "", fmt.Errorf("chromedp执行失败: %v", err) -// } -// return result, nil -//} -// -//// 获取孔网实拍图(查询商品列表) -//func getKFZSPTImageURL(proxyType, username, password, machineCode, isbn string) (*BookInfo, error) { -// url := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000", cf.API.ProductSearchURL, isbn, cf.App.Size) -// -// req := XxProxyRequest(proxyType, username, password, machineCode). -// Get(url). -// Set("User-Agent", cf.App.DefaultUserAgent). -// Set("Accept", "*/*") -// resp, body, errs := req.End() -// log.Printf("[DEBUG] 获取图书列表 URL: %s", url) -// -// // 只在有响应内容时才打印,避免日志过长 -// if len(body) > 0 && len(body) < 500 { -// log.Printf("[DEBUG] 获取图书列表响应: %s", body) -// } else if len(body) > 0 { -// log.Printf("[DEBUG] 获取图书列表响应长度: %d 字符", len(body)) -// } else { -// log.Printf("[DEBUG] 获取图书列表响应为空") -// } -// -// // 处理请求错误 -// if len(errs) > 0 { -// // 检查是否是代理认证失败 -// var proxyAuthFailed bool -// for _, e := range errs { -// if strings.Contains(e.Error(), "Proxy Authentication Required") { -// proxyAuthFailed = true -// break -// } -// } -// if proxyAuthFailed { -// return nil, fmt.Errorf("代理认证失败") -// } -// return nil, fmt.Errorf("查询请求失败: %v", errs) -// } -// -// // 检查HTTP状态码 -// if resp.StatusCode != http.StatusOK { -// space := strings.TrimSpace(resp.Status) -// return nil, fmt.Errorf("HTTP错误: %s", space) -// } -// -// // 解析响应 -// var apiResp 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 { -// Title string `json:"title"` -// ImgUrl string `json:"imgUrl"` -// ImgBigUrl string `json:"imgBigUrl"` -// ItemId int64 `json:"itemId"` -// ShopId int64 `json:"shopId"` -// TplRecords []struct { -// Key string `json:"key"` -// Value string `json:"value"` -// } `json:"tplRecords"` -// } `json:"list"` -// } `json:"itemResponse"` -// } `json:"data"` -// } -// -// if err := json.Unmarshal([]byte(body), &apiResp); err != nil { -// return nil, fmt.Errorf("解析JSON失败: %w", err) -// } -// -// // 如果找到商品,返回图片URL -// if apiResp.Data.ItemResponse.Total > 0 && len(apiResp.Data.ItemResponse.List) > 0 { -// // 确定起始索引 -// var startIndex int -// if cf.App.Size >= apiResp.Data.ItemResponse.Total { -// startIndex = apiResp.Data.ItemResponse.Total - 1 -// } else { -// startIndex = cf.App.Size - 1 -// } -// -// // 从指定索引开始,向前查找有效图片,最多重试3次 -// for attempt := 0; attempt < 3; attempt++ { -// currentIndex := startIndex - attempt -// -// // 检查索引是否有效 -// if currentIndex < 0 || currentIndex >= len(apiResp.Data.ItemResponse.List) { -// log.Printf("[DEBUG] 索引 %d 超出范围,跳过", currentIndex) -// continue -// } -// -// item := apiResp.Data.ItemResponse.List[currentIndex] -// -// // 检查图片URL是否存在 -// if item.ImgBigUrl == "" { -// log.Printf("[DEBUG] 索引 %d 的图片URL为空,跳过", currentIndex) -// continue -// } -// -// info := &BookInfo{} -// info.BookName = item.Title -// info.BookPicS = item.ImgUrl -// info.ItemId = item.ItemId -// info.ShopId = item.ShopId -// -// // 安全地获取TplRecords中的值 -// if len(item.TplRecords) > 0 { -// info.Author = item.TplRecords[0].Value -// } -// if len(item.TplRecords) > 1 { -// info.Publisher = item.TplRecords[1].Value -// } -// if len(item.TplRecords) > 2 { -// info.PublicationTime = validateDateFormat(item.TplRecords[2].Value) -// } -// if len(item.TplRecords) > 3 { -// info.BindingLayout = item.TplRecords[3].Value -// } -// -// // 成功时重置代理失败计数器 -// resetProxyFailCount() -// return info, nil -// } -// // 如果所有尝试都失败了,返回错误 -// log.Printf("[WARN] 经过3次尝试,未找到有效图片") -// return nil, fmt.Errorf("未找到有效图片,已尝试3次") -// } -// return nil, nil -//} -// -//// 获取孔网官图(查询图书条目) -//func getKFZGTImageURL(proxyType, username, password, machineCode, isbn string) (*BookInfo, error) { -// url := fmt.Sprintf("%s?keyword=%s", cf.API.BookSearchURL, isbn) -// -// info := &BookInfo{} -// -// req := XxProxyRequest(proxyType, username, password, machineCode). -// Get(url). -// Set("User-Agent", cf.App.DefaultUserAgent). -// Set("Accept", "*/*") -// resp, body, errs := req.End() -// log.Printf("[DEBUG] 获取图书条目 URL: %s", url) -// -// // 只在有响应内容时才打印,避免日志过长 -// if len(body) > 0 && len(body) < 500 { -// log.Printf("[DEBUG] 获取图书条目响应: %s", body) -// } else if len(body) > 0 { -// log.Printf("[DEBUG] 获取图书条目响应长度: %d 字符", len(body)) -// } else { -// log.Printf("[DEBUG] 获取图书条目响应为空") -// } -// -// // 处理请求错误 -// 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 { -// space := strings.TrimSpace(resp.Status) -// return nil, fmt.Errorf("HTTP错误: %s", space) -// } -// -// // 解析响应 -// var apiResp 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"` -// BookShowInfo []string `json:"bookShowInfo"` -// } `json:"list"` -// } `json:"itemResponse"` -// } `json:"data"` -// } -// -// if err := json.Unmarshal([]byte(body), &apiResp); err != nil { -// return nil, fmt.Errorf("解析JSON失败: %w", err) -// } -// -// // 如果找到条目,返回图片URL -// if apiResp.Data.ItemResponse.Total > 0 && len(apiResp.Data.ItemResponse.List) > 0 { -// list := apiResp.Data.ItemResponse.List[0] -// info := list.BookShowInfo -// -// bookItem := &BookInfo{ -// BookName: list.BookName, -// BookPic: list.ImgUrlEntity.BigImgUrl, -// Mid: list.Mid, -// } -// -// // 根据长度安全填充字段 -// if len(info) > 0 { -// bookItem.Author = info[0] -// } -// if len(info) > 1 { -// bookItem.Publisher = info[1] -// } -// if len(info) > 2 { -// bookItem.PublicationTime = validateDateFormat(info[2]) -// } -// if len(info) > 3 { -// bookItem.BindingLayout = info[3] -// } -// if len(info) > 4 { -// bookItem.FixPrice = info[4] -// } else { -// log.Printf("[WARN] BookShowInfo 长度不足 (仅 %d 项): %v", len(info), info) -// } -// -// fmt.Println("bookItem: ", bookItem) -// return bookItem, nil -// } -// -// return info, nil -//} -// -//// 替换所有空白字符为空格 -//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 extractShippingInfo(shippingFee string) string { -// // 匹配 "快递¥数字" 格式 -// re1 := regexp.MustCompile(`快递¥(\d+\.?\d*)`) -// // 匹配 "包邮" 格式 -// re2 := regexp.MustCompile(`(包邮)`) -// -// if matches := re1.FindStringSubmatch(shippingFee); len(matches) > 1 { -// return matches[1] // 返回数字部分 -// } -// -// if matches := re2.FindStringSubmatch(shippingFee); len(matches) > 1 { -// return "0" // 返回"包邮" -// } -// -// return "" -//} -// -//// 改进字段 -//func extractNormalDetails(s *goquery.Selection, fieldName string) string { -// var info string -// s.Each(func(i int, selection *goquery.Selection) { -// if strings.Contains(selection.Text(), fieldName) { -// all := strings.ReplaceAll(selection.Text(), " ", "") -// all = strings.ReplaceAll(all, "\n", "") -// all = strings.ReplaceAll(all, " ", "") -// split := strings.SplitN(all, ":", 2) -// if len(split) > 1 { -// info = split[1] -// } else { -// return -// } -// } -// }) -// return info -//} -// -//// 改进的字段提取函数 -//func extractNormalText(s *goquery.Selection, fieldName string) string { -// // 在左右两个分区中查找 -// selectors := []string{"div.f_left", "div.f_right"} -// for _, selector := range selectors { -// fieldItem := s.Find(selector + " div.normal-item").FilterFunction(func(i int, sel *goquery.Selection) bool { -// title := sel.Find("span.normal-title").Text() -// return strings.Contains(title, fieldName) -// }) -// -// if fieldItem.Length() > 0 { -// text := fieldItem.Find("span.normal-text").Text() -// return strings.TrimSpace(text) -// } -// } -// return "" -//} -// -//// 重置代理失败计数器(成功时调用) -//func resetProxyFailCount() { -// tailProxyMu.Lock() -// defer tailProxyMu.Unlock() -// proxyFailCount = 0 -//} -// -//// 验证日期格式 -//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 XxProxyRequest(proxyType, username, password, machineCode string) *gorequest.SuperAgent { -// proxyManagerOnce.Do(func() { -// log.Printf("[INFO] 初始化代理管理器,DLL路径: %s", cf.Proxy.ProxyFilePath) -// -// // 检查DLL文件是否存在 -// if _, err := os.Stat(cf.Proxy.ProxyFilePath); os.IsNotExist(err) { -// // 尝试在可执行文件目录查找 -// exePath, _ := os.Executable() -// exeDir := filepath.Dir(exePath) -// dllPath := filepath.Join(exeDir, cf.Proxy.ProxyFilePath) -// -// if _, err := os.Stat(dllPath); err == nil { -// cf.Proxy.ProxyFilePath = dllPath -// } else { -// proxyManagerInitErr = fmt.Errorf("代理DLL文件不存在: %s (也尝试了: %s)", cf.Proxy.ProxyFilePath, dllPath) -// return -// } -// } -// -// globalProxyManager, proxyManagerInitErr = NewProxyConfigManager(cf.Proxy.ProxyFilePath) -// if proxyManagerInitErr != nil { -// log.Printf("[ERROR] 代理管理器初始化失败: %v", proxyManagerInitErr) -// } else { -// log.Printf("[INFO] 代理管理器初始化成功") -// } -// }) -// typeManager, err := globalProxyManager.ProxyTypeManager( -// proxyType, -// username, -// password, -// machineCode, -// ) -// if err != nil { -// fmt.Printf("获取代理服务器信息失败: %v", err) -// } -// return gorequest.New().Proxy(typeManager).Timeout(120*time.Second).Retry(2, 3*time.Second) -//} -// -//// 初始化 -//func initializeConfig(config Config) { -// // 设置全局配置 -// cf = config -//} -// -////export GetKFZShopBookInfo -//func GetKFZShopBookInfo(fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp *C.char) *C.char { -// fetchModeStr := C.GoString(fetchMode) -// proxyTypeStr := C.GoString(proxyType) -// usernameStr := C.GoString(username) -// passwordStr := C.GoString(password) -// machineCodeStr := C.GoString(machineCode) -// shopIdStr := C.GoString(shopId) -// isImageStr := C.GoString(isImage) -// bookNumStr := C.GoString(bookNum) -// pageNumStr := C.GoString(pageNum) -// sortTypeStr := C.GoString(sortType) -// sortStr := C.GoString(sort) -// priceDownStr := C.GoString(priceDown) -// priceUpStr := C.GoString(priceUp) -// -// books, goodsNum, pNum, err := getKFZShopBookInfo(fetchModeStr, proxyTypeStr, usernameStr, passwordStr, machineCodeStr, shopIdStr, isImageStr, bookNumStr, pageNumStr, sortTypeStr, sortStr, priceDownStr, priceUpStr) -// if err != nil { -// result := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// jsonData, _ := json.Marshal(result) -// return C.CString(string(jsonData)) -// } -// -// jsonData, err := json.Marshal(APIResponse{ -// Success: true, -// Message: "查询成功", -// GoodsNum: goodsNum, -// PNum: pNum, -// Data: books, -// }) -// if err != nil { -// errorResult := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// errorJson, _ := json.Marshal(errorResult) -// return C.CString(string(errorJson)) -// } -// -// return C.CString(string(jsonData)) -//} -// -////export GetUrlBookDetails -//func GetUrlBookDetails(fetchMode, proxyType, username, password, machineCode, url *C.char) *C.char { -// fetchModeStr := C.GoString(fetchMode) -// proxyTypeStr := C.GoString(proxyType) -// usernameStr := C.GoString(username) -// passwordStr := C.GoString(password) -// machineCodeStr := C.GoString(machineCode) -// urlStr := C.GoString(url) -// books, err := getUrlBookDetails(fetchModeStr, proxyTypeStr, usernameStr, passwordStr, machineCodeStr, urlStr) -// if err != nil { -// result := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// jsonData, _ := json.Marshal(result) -// return C.CString(string(jsonData)) -// } -// jsonData, err := json.Marshal(APIResponse{ -// Success: true, -// Message: "查询成功", -// Data: books, -// }) -// if err != nil { -// errorResult := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// errorJson, _ := json.Marshal(errorResult) -// return C.CString(string(errorJson)) -// } -// return C.CString(string(jsonData)) -//} -// -////export GetKFZSPTImageURL -//func GetKFZSPTImageURL(proxyType, username, password, machineCode, isbn *C.char) *C.char { -// proxyTypeStr := C.GoString(proxyType) -// usernameStr := C.GoString(username) -// passwordStr := C.GoString(password) -// machineCodeStr := C.GoString(machineCode) -// isbnStr := C.GoString(isbn) -// -// bookInfo, err := getKFZSPTImageURL(proxyTypeStr, usernameStr, passwordStr, machineCodeStr, isbnStr) -// if err != nil { -// result := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// jsonData, _ := json.Marshal(result) -// return C.CString(string(jsonData)) -// } -// -// result := map[string]interface{}{ -// "success": true, -// "bookInfo": bookInfo, -// } -// jsonData, err := json.Marshal(result) -// if err != nil { -// errorResult := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// errorJson, _ := json.Marshal(errorResult) -// return C.CString(string(errorJson)) -// } -// return C.CString(string(jsonData)) -//} -// -////export GetKFZGTImageURL -//func GetKFZGTImageURL(proxyType, username, password, machineCode, isbn *C.char) *C.char { -// proxyTypeStr := C.GoString(proxyType) -// usernameStr := C.GoString(username) -// passwordStr := C.GoString(password) -// machineCodeStr := C.GoString(machineCode) -// isbnStr := C.GoString(isbn) -// -// bookInfo, err := getKFZGTImageURL(proxyTypeStr, usernameStr, passwordStr, machineCodeStr, isbnStr) -// if err != nil { -// result := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// jsonData, _ := json.Marshal(result) -// return C.CString(string(jsonData)) -// } -// -// result := map[string]interface{}{ -// "success": true, -// "bookInfo": bookInfo, -// } -// jsonData, err := json.Marshal(result) -// if err != nil { -// errorResult := map[string]interface{}{ -// "success": false, -// "error": err.Error(), -// } -// errorJson, _ := json.Marshal(errorResult) -// return C.CString(string(errorJson)) -// } -// -// return C.CString(string(jsonData)) -//} -// -////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":"初始化成功"}`) -//} -// -//// 导出函数:释放C字符串内存 -//// -////export FreeCString -//func FreeCString(str *C.char) { -// C.free(unsafe.Pointer(str)) -//} -// -//// 空main函数,编译DLL时需要 //func main() { +// fmt.Println("=== 孔夫子商品模板API服务 ===") // +// http.HandleFunc("/api/goodsTemplate", getGoodsTemplateHandler) +// port := "8989" +// server := &http.Server{ +// Addr: ":" + port, +// Handler: nil, +// } +// // 启动服务器 +// if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { +// fmt.Printf("服务器启动失败: %v\n", err) +// } //} + +// 获取商品模板接口 +func getGoodsTemplateHandler(w http.ResponseWriter, r *http.Request) { + // 设置响应头 + w.Header().Set("Content-Type", "application/json") + + // 只处理GET请求 + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + json.NewEncoder(w).Encode(map[string]interface{}{ + "success": false, + "error": "只支持GET请求", + }) + return + } + + // 获取查询参数 + token := r.URL.Query().Get("token") + itemId := r.URL.Query().Get("itemId") + proxy := r.URL.Query().Get("proxy") + + // 验证必需参数 + if token == "" || itemId == "" { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(map[string]interface{}{ + "success": false, + "error": "缺少必需参数: token 和 itemid", + "usage": map[string]string{ + "token": "登录Token (PHPSESSID)", + "itemid": "商品ID", + "proxy": "代理地址 (可选)", + }, + "example": "GET /api/goods-template?token=abc123&itemid=123456&proxy=http://proxy:8080", + }) + return + } + + log.Printf("收到请求 - 商品ID: %s", itemId) + + // 调用函数获取商品模板 + result, err := outGetGoodsTplMsgLinux(token, itemId, proxy) + if err != nil { + log.Printf("获取商品模板失败: %v", err) + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(map[string]interface{}{ + "success": false, + "error": err.Error(), + }) + return + } + + // 返回成功响应 + json.NewEncoder(w).Encode(map[string]interface{}{ + "success": true, + "data": result, + }) +} diff --git a/main.go b/main.go index 03de10b..aebae34 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,11 @@ package main /* - #include +#include - // proxyConfig.dll 函数声明 - extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); - extern void FreeCString(char* str); +// proxyConfig.dll 函数声明 +extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode); +extern void FreeCString(char* str); */ import "C" import ( @@ -17,6 +17,7 @@ import ( "log" "math/rand" "net/http" + "net/url" "os" "path/filepath" "regexp" @@ -238,6 +239,17 @@ type UserInfo struct { Mobile string `json:"mobile"` } +// 店铺快递费参数 +type ParamsInfo struct { + Params []Param `json:"params"` + Area string `json:"area"` +} + +type Param struct { + UserId string `json:"userId"` + ItemId string `json:"itemId"` +} + // ProxyConfigManager 代理配置DLL管理器 type ProxyConfigManager struct { dll *syscall.DLL @@ -641,6 +653,190 @@ func outAddGoods(token, proxy, formData string) (map[string]interface{}, error) return nil, fmt.Errorf("API返回错误: %+v", data) } +// 获取图片URL(官图和拍图)带有店铺过滤 +func outGetImageFilterShopId(token string, isbn string, shopId int, proxy string, isLiveImage int, isReturnMsg int) (map[string]string, error) { + fmt.Println("[DEBUG] 使用的ISBN: ", isbn) + if isLiveImage == 0 { + gtUrl := fmt.Sprintf("%s?keyword=%s", cf.API.BookSearchURL, 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 + } + if isLiveImage == 1 { + // 实拍图 + sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000", cf.API.ProductSearchURL, isbn, cf.App.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 cf.App.Size >= apiSptResp.Data.ItemResponse.Total { + startIndex = apiSptResp.Data.ItemResponse.Total - 1 + } else { + startIndex = cf.App.Size - 1 + } + 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": item.ImgUrl, + "isbn": item.Isbn, + } + return info, nil + } + } + } + return nil, nil +} + // 获取图片URL(官图和拍图) func outGetImageByIsbn(token string, isbn string, proxy string, isLiveImage int, isReturnMsg int) (*BookInfo, error) { fmt.Println("[DEBUG] 使用的ISBN: ", isbn) @@ -657,7 +853,7 @@ func outGetImageByIsbn(token string, isbn string, proxy string, isLiveImage int, } // 发送请求 respGt, bodyGt, errsGt := requestGt.Get(gtUrl). - Set("Cookie", token). + 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"). @@ -859,121 +1055,8 @@ func outGetImageByIsbn(token string, isbn string, proxy string, isLiveImage int, return nil, fmt.Errorf("查询失败,没有数据!") } -// 登录,获取账号cookie -func loginCookie() (cookie string, err error) { - account := &AccountCredential{ - ID: 0, - Username: "", - Password: "", - Token: "", - } - var change = false - if account.Username == "" || change { - account, err = getRandomAccount() - if err != nil { - log.Printf("获取账号失败: %v", err) - } - change = false - } - fmt.Println("获取的账号: ", account.Username) - // 将拿出的账号token赋值给cookie - cookie = account.Token - // 若cookie为""说明该账号没有token还未登录过,进行登录 - if cookie == "" { - // 登录获取cookie - cookie, err = loginAndGetCookie(account) - if err != nil { - // 若登录失败,则标记为需要更换账号,然后重试 - change = true - log.Printf("登录获取cookie失败: %v ,更换账号重试", err) - } - // 成功拿到cookie,则将新拿到的cookie赋值给account的token - account.Token = cookie - // 将新拿到的cookie同步更新token到数据库 - err = updateAccountToken(account.ID, cookie) - if err != nil { - log.Printf("更新账号token失败: %v", err) - } - } - return cookie, nil -} - -// 登录并获取cookie -func loginAndGetCookie(account *AccountCredential) (string, error) { - formData := map[string]string{ - "loginName": account.Username, - "loginPass": account.Password, - } - // 没有代理 - resp, body, errs := gorequest.New(). - Post(cf.API.LoginURL). - Set("Content-Type", "application/x-www-form-urlencoded"). - Set("User-Agent", cf.App.DefaultUserAgent). - Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"). - Send(formData). - Timeout(30 * time.Second). - End() - // 处理请求错误 - if len(errs) > 0 { - // 检查是否是代理认证失败 - var proxyAuthFailed bool - for _, e := range errs { - if strings.Contains(e.Error(), "Proxy Authentication Required") { - proxyAuthFailed = true - break - } - } - if proxyAuthFailed { - return "", fmt.Errorf("代理认证失败") - } - return "", fmt.Errorf("登录请求失败: %v", errs) - } - // 检查HTTP状态码 - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("登录失败(HTTP状态码: %d)", resp.StatusCode) - } - if !strings.Contains(body, "window.location.href='https://login.kongfz.cn/Pc/Session/rsync") { - // 尝试解析JSON响应获取错误信息 - var response struct { - Status bool `json:"status"` - ErrCode *int `json:"errCode"` - ErrInfo string `json:"errInfo"` - } - if err := json.Unmarshal([]byte(body), &response); err != nil { - // 账号密码错误 - if response.ErrCode != nil && *response.ErrCode == 1001 { - // 标记账号为不可用 - if err := updateAccountInvalid(account.ID); err != nil { - log.Printf("标记账号不可用失败: %v", err) - } - return "", fmt.Errorf("账号或密码错误") - } - if response.ErrInfo != "" { - if err := updateAccountInvalid(account.ID); err != nil { - log.Printf("标记账号不可用失败: %v", err) - } - return "", fmt.Errorf(response.ErrInfo) - } else { - if err := updateAccountNeedVerify(account.ID); err != nil { - log.Printf("标记账号需要验证失败: %v", err) - } - return "", fmt.Errorf("该账号需要手机号登录验证") - } - } else { - return "", fmt.Errorf("登录响应不包含成功跳转信息") - } - } - // 提取Cookie - cookies := resp.Header.Get("Set-Cookie") - if cookies == "" { - return "", fmt.Errorf("登录成功但未获取到Cookie") - } - log.Printf("登录成功,用户: %s", account.Username) - return cookies, nil -} - // 获取商品列表通过店铺ID -func outGetGoodsListMsgByShopId(shopId int, proxy string, isImage int, sortType string, sort string, priceMin float32, priceMax float32, pageNum, returnNum int) (books []BookInfo, goodsNum string, pNum string, err error) { +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("店铺编码为空!") @@ -1063,6 +1146,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, isImage int, sortType 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++ { @@ -1078,13 +1162,102 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, isImage int, sortType // 商品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 } +// 定义对应的结构体 +type FeeInfo struct { + ShippingID string `json:"shippingId"` + ShippingName string `json:"shippingName"` + TotalFee string `json:"totalFee"` + ShippingText string `json:"shippingText"` + FilterTotalFee string `json:"filterTotalFee"` +} + +type DataItem struct { + Fee []FeeInfo `json:"fee"` + IsSeller bool `json:"isSeller"` + ItemID string `json:"itemId"` + UserID string `json:"userId"` +} + +type ResponseStruct struct { + Status bool `json:"status"` + Data []DataItem `json:"data"` + Message string `json:"message"` + ErrType string `json:"errType"` +} + +// 获取店铺里商品的快递费(定位到河南) +func getGoodsListShippingFee(params ParamsInfo, proxy string) ([]DataItem, error) { + if params.Params == nil { + return nil, fmt.Errorf("没有商品信息!") + } + paramsJSON, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("参数序列化失败: %v", err) + } + // URL 编码参数 + encodedParams := url.QueryEscape(string(paramsJSON)) + url := fmt.Sprintf("https://shop.kongfz.com/book/shopsearch/getShippingFee?params=%s", encodedParams) + request := gorequest.New() + if proxy != "" { + request.Proxy(proxy) + } + // 发送请求 + 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) + } + 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 +} + // 获取商品信息通过商品详情链接 func outGetGoodsMsgByDetailUrl(detailUrl, proxy string) (*BookInfo, error) { response, err := fetchResponse(detailUrl, proxy) @@ -2109,7 +2282,7 @@ func FetchBookDetailsShippingFee(url string) (string, error) { // 查找包含快递费用的元素 const elements = document.querySelectorAll('*'); let shippingFee = null; - + elements.forEach(element => { if (element.textContent.includes('快递¥')) { const match = element.textContent.match(/快递¥(\d+\.\d{2})/); @@ -2129,7 +2302,7 @@ func FetchBookDetailsShippingFee(url string) (string, error) { console.log('找到快递费用: 包邮'); } }); - + if (!shippingFee) { // 如果没找到,尝试搜索整个页面 const pageText = document.body.innerText; @@ -2137,7 +2310,7 @@ func FetchBookDetailsShippingFee(url string) (string, error) { if (match) { shippingFee = match[1]; console.log('找到快递费用: ¥' + shippingFee); - } + } // 尝试匹配包邮 else if (pageText.match(/快递[::\s]包邮/)) { shippingFee = '包邮'; @@ -2915,6 +3088,43 @@ func OutAddGoods(token, proxy, formData *C.char) *C.char { return C.CString(string(jsonData)) } +// 获取图片URL(官图和拍图)带有店铺过滤 +// +//export OutGetImageFilterShopId +func OutGetImageFilterShopId(token, isbn *C.char, shopId C.int, proxy *C.char, isLiveImage C.int, isReturnMsg C.int) *C.char { + goToken := C.GoString(token) + goIsbn := C.GoString(isbn) + goShopId := int(shopId) + goProxy := C.GoString(proxy) + goIsLiveImage := int(isLiveImage) + goIsReturnMsg := int(isReturnMsg) + info, err := outGetImageFilterShopId(goToken, goIsbn, goShopId, goProxy, goIsLiveImage, goIsReturnMsg) + 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)) +} + // 获取商品图片(带有Out的都非官方标准接口) // //export OutGetImageByIsbn @@ -2954,9 +3164,10 @@ func OutGetImageByIsbn(token, isbn, proxy *C.char, isLiveImage C.int, isReturnMs // 获取商品列表通过店铺ID(带有Out的都非官方标准接口) // //export OutGetGoodsListMsgByShopId -func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, isImage C.int, sortType *C.char, sort *C.char, priceMin C.float, priceMax C.float, pageNum, returnNum C.int) *C.char { +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) @@ -2964,7 +3175,7 @@ func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, isImage C.int, sort goPriceMax := float32(priceMax) goPageNum := int(pageNum) goReturnNum := int(returnNum) - books, num, pNum, err := outGetGoodsListMsgByShopId(goShopId, goProxy, goIsImage, goSortType, goSort, goPriceMin, goPriceMax, goPageNum, goReturnNum) + books, num, pNum, err := outGetGoodsListMsgByShopId(goShopId, goProxy, goRetPrice, goIsImage, goSortType, goSort, goPriceMin, goPriceMax, goPageNum, goReturnNum) // 构建统一格式的响应 bookInfo := struct { GoodsNum string `json:"goods_num,omitempty"` @@ -3092,5 +3303,4 @@ func FreeCString(str *C.char) { //// 空main函数,编译DLL时需要 //func main() { -// //} diff --git a/main_so.go b/main_so.go new file mode 100644 index 0000000..1a0da3a --- /dev/null +++ b/main_so.go @@ -0,0 +1,3331 @@ +package main + +///* +//#cgo LDFLAGS: -ldl +// +//#include +//#include +//#include +//*/ +//import "C" +//import ( +// "context" +// "database/sql" +// "encoding/json" +// "fmt" +// "io" +// "log" +// "math/rand" +// "net/http" +// "net/url" +// "os" +// "path/filepath" +// "regexp" +// "strconv" +// "strings" +// "sync" +// "time" +// "unsafe" +// +// "github.com/PuerkitoBio/goquery" +// "github.com/chromedp/chromedp" +// _ "github.com/go-sql-driver/mysql" +// "github.com/parnurzeal/gorequest" +//) +// +//const ( +// UseProxy = "proxy" // 使用代理 +// NotProxy = "direct" // 不用代理代理 +//) +// +//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"` +// } `ini:"app" json:"app"` +// +// API struct { +// LoginURL string `ini:"api.login_url" json:"login_url" default:"https://login.kongfz.com/Pc/Login/account"` +// 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"` +// 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"` +// } `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"` +// +// Database struct { +// Username string `ini:"database.username" json:"username" default:"newAdmin"` +// Password string `ini:"database.password" json:"password" default:"bYPp8SbBe5F7nz2i"` +// Host string `ini:"database.host" json:"host" default:"146.56.227.42:3306"` +// Name string `ini:"database.name" json:"name" default:"newadmin"` +// } `ini:"database" json:"database"` +//} +// +//// 条目详情结构体 +//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 +//} +// +//// API响应结构 +//type APIResp struct { +// Success bool `json:"success"` +// Message string `json:"message,omitempty"` +// GoodsNum string `json:"goods_num,omitempty"` +// PNum string `json:"pnum,omitempty"` +// Data interface{} `json:"data,omitempty"` +// Error string `json:"error,omitempty"` +//} +// +//// APIResponse +//type APIResponse struct { +// Success bool `json:"success"` +// Message string `json:"message,omitempty"` +// Data interface{} `json:"data,omitempty"` +//} +// +//// 账号凭证结构 +//type AccountCredential struct { +// ID int64 +// Username string +// Password string +// Token string +//} +// +//// 全局变量 +//var ( +// cf Config // 配置信息 +// tailProxyMu sync.Mutex // 互斥锁 +// proxyFailCount int +// mu sync.RWMutex // 数据库锁 +// +// // 全局代理管理器 +// globalProxyManager *ProxyConfigManager +// proxyManagerOnce sync.Once +// proxyManagerInitErr error +//) +// +//// ProductInfo 商品信息结构 +//type ProductInfo struct { +// ItemID string `json:"itemId"` +// BookName string `json:"bookName"` +// Price string `json:"price"` +// ShippingFee string `json:"shippingFee"` +//} +// +//// ProductResponse 响应结构 +//type ProductResponse struct { +// Success bool `json:"success"` +// Message string `json:"message,omitempty"` +// Data []ProductInfo `json:"data,omitempty"` +//} +// +//// 并行获取详情的结果结构 +//type DetailResult struct { +// URL string +// Doc *goquery.Document +// Error error +// Index int +//} +// +//// 第一阶段:收集基本信息并识别需要详情的项目 +//type BookItem struct { +// Book BookInfo +// Selection *goquery.Selection +// HasDetail bool +// DetailURL string +// Index int +//} +// +//// 分类项结构体 +//type SalesCategory struct { +// Key string `json:"key"` +// Value int `json:"value"` +//} +// +//// 图书详情响应结构体 +//type BookDetailResponse struct { +// Status bool `json:"status"` +// Result BookList `json:"result"` +// ErrMessage string `json:"errMessage"` +// ErrCode int `json:"errCode"` +//} +// +//// 图书列表结构体 +//type BookList struct { +// Current int `json:"current"` +// Data []BookInformation `json:"data"` +// Total int `json:"total"` +//} +// +//// 图书信息结构体 +//type BookInformation struct { +// Author string `json:"author"` +// BookName string `json:"bookName"` +// ContentIntroduction string `json:"contentIntroduction"` +// ImgUrl string `json:"imgUrl"` +// Isbn string `json:"isbn"` +// ItemUrls ItemUrls `json:"itemUrls"` +// Mid int `json:"mid"` +// 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"` +//} +// +//// 作者信息结构体 +//type AuthorInfo struct { +// Name string `json:"name"` +// OriName string `json:"oriName"` +// Nationality string `json:"nationality"` +// Role string `json:"role"` +// Url string `json:"url"` +//} +// +//// 商品链接结构体 +//type ItemUrls struct { +// AppUrl string `json:"appUrl"` +// MUrl string `json:"mUrl"` +// MiniUrl string `json:"miniUrl"` +// PcUrl string `json:"pcUrl"` +//} +// +//type UserInfo struct { +// UserID int64 `json:"userId"` +// Nickname string `json:"nickname"` +// Mobile string `json:"mobile"` +//} +// +//// 店铺快递费参数 +//type ParamsInfo struct { +// Params []Param `json:"params"` +// Area string `json:"area"` +//} +// +//type Param struct { +// UserId string `json:"userId"` +// ItemId string `json:"itemId"` +//} +// +//// ProxyConfigManager 代理配置SO管理器 +//type ProxyConfigManager struct { +// handle unsafe.Pointer +//} +// +//func NewProxyConfigManager(soPath string) (*ProxyConfigManager, error) { +// cSoPath := C.CString(soPath) +// defer C.free(unsafe.Pointer(cSoPath)) +// +// handle := C.dlopen(cSoPath, C.RTLD_LAZY) +// if handle == nil { +// err := C.dlerror() +// if err != nil { +// return nil, fmt.Errorf("加载代理配置SO失败: %s", C.GoString(err)) +// } +// return nil, fmt.Errorf("加载代理配置SO失败: 未知错误") +// } +// return &ProxyConfigManager{handle: handle}, nil +//} +// +//func (m *ProxyConfigManager) Close() { +// if m.handle != nil { +// C.dlclose(m.handle) +// m.handle = nil +// } +//} +// +//// ProxyTypeManager 调用代理类型管理器 +//func (m *ProxyConfigManager) ProxyTypeManager(proxyType, username, password, machineCode string) (string, error) { +// // 查找 ProxyTypeManager 函数 +// cFuncName := C.CString("ProxyTypeManager") +// defer C.free(unsafe.Pointer(cFuncName)) +// +// proc := C.dlsym(m.handle, cFuncName) +// if proc == nil { +// err := C.dlerror() +// if err != nil { +// return "", fmt.Errorf("找不到函数 ProxyTypeManager: %s", C.GoString(err)) +// } +// return "", fmt.Errorf("找不到函数 ProxyTypeManager") +// } +// +// // 准备参数 +// cProxyType := C.CString(proxyType) +// cUsername := C.CString(username) +// cPassword := C.CString(password) +// cMachineCode := C.CString(machineCode) +// defer func() { +// C.free(unsafe.Pointer(cProxyType)) +// C.free(unsafe.Pointer(cUsername)) +// C.free(unsafe.Pointer(cPassword)) +// C.free(unsafe.Pointer(cMachineCode)) +// }() +// +// // 定义函数类型 +// type ProxyTypeManagerFunc func(proxyType, username, password, machineCode *C.char) *C.char +// +// // 转换函数指针 +// proxyFunc := *(*ProxyTypeManagerFunc)(unsafe.Pointer(&proc)) +// +// // 调用函数 +// result := proxyFunc(cProxyType, cUsername, cPassword, cMachineCode) +// if result == nil { +// return "", fmt.Errorf("ProxyTypeManager 返回空结果") +// } +// +// goResult := C.GoString(result) +// +// // 查找并调用 FreeCString 函数 +// cFreeName := C.CString("FreeCString") +// defer C.free(unsafe.Pointer(cFreeName)) +// +// freeProc := C.dlsym(m.handle, cFreeName) +// if freeProc != nil { +// type FreeCStringFunc func(str *C.char) +// freeFunc := *(*FreeCStringFunc)(unsafe.Pointer(&freeProc)) +// freeFunc(result) +// } else { +// // 如果找不到 FreeCString,使用 C.free +// C.free(unsafe.Pointer(result)) +// } +// +// return goResult, nil +//} +// +//// 登录 +//func outLogin(username, password string) (string, error) { +// if username == "" || password == "" { +// return "", fmt.Errorf("请输入用户名和密码!") +// } +// formData := map[string]string{ +// "loginName": username, +// "loginPass": password, +// "returnUrl": "http://user.kongfz.com/", +// } +// resp, body, errs := gorequest.New(). +// Post(cf.API.LoginURL). +// Set("Content-Type", "application/x-www-form-urlencoded"). +// Set("User-Agent", cf.App.DefaultUserAgent). +// 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"` +// } +// 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("登录失败,未知错误!") +//} +// +//// 获取用户信息(带有Out的都非官方标准接口) +//func outGetUserMsg(token string) (*UserInfo, error) { +// 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"` +// } +// } +// 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 +//} +// +//// 获取商品模版 +//func outGetGoodsTplMsg(token, itemId, proxy string) (map[string]interface{}, error) { +// if token == "" { +// return nil, fmt.Errorf("请先登录获取Token") +// } +// url := fmt.Sprintf("https://seller.kongfz.com/pc/itemInfo/getTplFields?itemId=%s&isClone=1&v=%d", +// itemId, time.Now().Unix()) +// // 创建HTTP客户端 +// 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) +// } +// 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"].(float64); ok && val == 1 { +// return data, nil +// } +// return nil, fmt.Errorf("API返回错误: %+v", data) +//} +// +//// 获取商品列表 +//func outGetGoodsListMsgFromSelfShop(token string, proxy string, itemSn string, priceMin string, priceMax string, startCreateTime int, +// endCreateTime int, requestType string, isItemSnEqual int, page int, size int) (map[string]interface{}, error) { +// if token == "" { +// return nil, fmt.Errorf("请先登录获取Token") +// } +// 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 int `json:"startCreateTime"` +// EndCreateTime int `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() +// 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) +// } +// 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) +//} +// +//// 删除商品-已登的店铺(带有Out的都非官方标准接口) +//func outDelGoodsFromSelfShop(token, proxy, itemId string) (map[string]interface{}, error) { +// if token == "" { +// return nil, fmt.Errorf("请先登录获取Token") +// } +// 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) +// } +// 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) +//} +// +//// 新增商品(带有Out的都非官方标准接口) +//func outAddGoods(token, proxy, formData string) (map[string]interface{}, error) { +// if token == "" { +// return nil, fmt.Errorf("请先登录获取Token") +// } +// url := "https://seller.kongfz.com/pc/itemInfo/add" +// 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) +// } +// 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) +//} +// +//// 获取图片URL(官图和拍图)带有店铺过滤 +//func outGetImageFilterShopId(token string, isbn string, shopId int, proxy string, isLiveImage int, isReturnMsg int) (map[string]string, error) { +// fmt.Println("[DEBUG] 使用的ISBN: ", isbn) +// if isLiveImage == 0 { +// gtUrl := fmt.Sprintf("%s?keyword=%s", cf.API.BookSearchURL, 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 +// } +// if isLiveImage == 1 { +// // 实拍图 +// sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000", cf.API.ProductSearchURL, isbn, cf.App.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 cf.App.Size >= apiSptResp.Data.ItemResponse.Total { +// startIndex = apiSptResp.Data.ItemResponse.Total - 1 +// } else { +// startIndex = cf.App.Size - 1 +// } +// 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": item.ImgUrl, +// "isbn": item.Isbn, +// } +// return info, nil +// } +// } +// } +// return nil, nil +//} +// +//// 获取图片URL(官图和拍图) +//func outGetImageByIsbn(token string, isbn string, proxy string, isLiveImage int, isReturnMsg int) (*BookInfo, error) { +// fmt.Println("[DEBUG] 使用的ISBN: ", isbn) +// // isLiveImage 1实拍图 0官图 ,isReturnMsg 0商品信息 +// bookInfo := &BookInfo{} +// if isLiveImage == 0 { +// // 孔网官图请求 +// gtUrl := fmt.Sprintf("%s?keyword=%s", cf.API.BookSearchURL, isbn) +// // 创建HTTP客户端 +// requestGt := gorequest.New() +// // 设置代理(如果有提供代理URL) +// if proxy != "" { +// requestGt.Proxy(proxy) +// } +// // 发送请求 +// 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"` +// } +// 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 +// } +// if isLiveImage == 1 { +// // 实拍图 +// sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000", cf.API.ProductSearchURL, isbn, cf.App.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 cf.App.Size >= apiSptResp.Data.ItemResponse.Total { +// startIndex = apiSptResp.Data.ItemResponse.Total - 1 +// } else { +// startIndex = cf.App.Size - 1 +// } +// 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 +// } +// 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 +//} +// +//// 定义对应的结构体 +//type FeeInfo struct { +// ShippingID string `json:"shippingId"` +// ShippingName string `json:"shippingName"` +// TotalFee string `json:"totalFee"` +// ShippingText string `json:"shippingText"` +// FilterTotalFee string `json:"filterTotalFee"` +//} +// +//type DataItem struct { +// Fee []FeeInfo `json:"fee"` +// IsSeller bool `json:"isSeller"` +// ItemID string `json:"itemId"` +// UserID string `json:"userId"` +//} +// +//type ResponseStruct struct { +// Status bool `json:"status"` +// Data []DataItem `json:"data"` +// Message string `json:"message"` +// ErrType string `json:"errType"` +//} +// +//// 获取店铺里商品的快递费(定位到河南) +//func getGoodsListShippingFee(params ParamsInfo, proxy string) ([]DataItem, error) { +// if params.Params == nil { +// return nil, fmt.Errorf("没有商品信息!") +// } +// paramsJSON, err := json.Marshal(params) +// if err != nil { +// return nil, fmt.Errorf("参数序列化失败: %v", err) +// } +// // URL 编码参数 +// encodedParams := url.QueryEscape(string(paramsJSON)) +// url := fmt.Sprintf("https://shop.kongfz.com/book/shopsearch/getShippingFee?params=%s", encodedParams) +// request := gorequest.New() +// if proxy != "" { +// request.Proxy(proxy) +// } +// // 发送请求 +// 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) +// } +// 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 +//} +// +//// 获取商品信息通过商品详情链接 +//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) { +// compile := regexp.MustCompile(`kongfz\.com/(\d+)/(\d+)`) +// match := compile.FindStringSubmatch(url) +// var shippingFee string +// var shopId int +// var itemId int +// if len(match) == 3 { +// firstNum, err := strconv.Atoi(match[1]) // 店铺ID +// if err != nil { +// return "", fmt.Errorf("无效的店铺编码: %s", match[1]) +// } +// shopId = firstNum +// secondNum, err := strconv.Atoi(match[2]) // 图书ID +// if err != nil { +// return "", fmt.Errorf("无效的图书编码: %s", match[2]) +// } +// itemId = secondNum +// } +// 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"` +// }{} +// +// 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 := cf.App.MaxRetryTimes +// 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) * cf.App.RateLimitDelay // 平方退避 +// 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) +// } +// 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) +// // 发送请求 +// 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) +// } +// +// var isbnList []string +// for _, item := range bookDetailResponse.Result.Data { +// if item.Isbn != "" { +// isbnList = append(isbnList, item.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 +//} +// +//// 获取Document文档(带重试机制) +//func fetchDocument(fetchMode, proxyType, username, password, machineCode, url string) (*goquery.Document, error) { +// log.Printf("调用的URL: %s", url) +// maxRetries := cf.App.MaxRetryTimes +// var detailsResp *http.Response +// var errors []error +// var detailsBody []byte +// for attempt := 0; attempt < maxRetries; attempt++ { +// if attempt > 0 { +// log.Printf("第 %d 次重试请求...", attempt) +// // 重试前等待,使用指数退避策略 +// waitTime := time.Duration(attempt*attempt) * cf.App.RateLimitDelay // 平方退避 +// log.Printf("等待 %v 后重试", waitTime) +// time.Sleep(waitTime) +// } +// if fetchMode == NotProxy { +// 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() +// } else if fetchMode == UseProxy { +// detailsReq := XxProxyRequest(proxyType, username, password, machineCode). +// 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"). +// Set("Connection", "close") // 每次请求关闭连接 +// detailsResp, _, errors = detailsReq.End() +// } else { +// errors = append(errors, fmt.Errorf("请求失败,选择模式错误!")) +// } +// // 检查是否需要重试 +// 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) +// } +// if detailsResp.StatusCode != http.StatusOK { +// return nil, fmt.Errorf("HTTP错误: %s", detailsResp.Status) +// } +// +// detailsBody, err := io.ReadAll(detailsResp.Body) +// if err != nil { +// return nil, err +// } +// +// return goquery.NewDocumentFromReader(strings.NewReader(string(detailsBody))) +//} +// +//// 合并基本信息和详情 +//func mergeBookDetails(bookItems []BookItem, detailResults map[string]*DetailResult) []BookInfo { +// var books []BookInfo +// +// for _, item := range bookItems { +// book := item.Book +// +// // 如果有详情且获取成功,补充详细信息 +// if item.HasDetail && item.DetailURL != "" { +// if result, exists := detailResults[item.DetailURL]; exists && result.Error == nil && result.Doc != nil { +// detailsDiv := result.Doc.Find("div.detail-lists.clear-fix") +// if detailsDiv.Length() > 0 { +// selection := detailsDiv.Find("li") +// book.Author = extractNormalDetails(selection, "作者") +// book.Publisher = extractNormalDetails(selection, "出版社") +// detailsTime := extractNormalDetails(selection, "出版时间") +// if detailsTime != "" { +// formats := []string{"2006-01", "2006-01-02", "2006"} +// for _, format := range formats { +// if t, err := time.Parse(format, detailsTime); err == nil { +// book.PublicationTime = t.Unix() +// break +// } +// } +// } +// book.ISBN = extractNormalDetails(selection, "ISBN") +// book.Edition = extractNormalDetails(selection, "版次") +// book.FixPrice = extractNormalDetails(selection, "定价") +// book.BindingLayout = extractNormalDetails(selection, "装帧") +// book.Format = extractNormalDetails(selection, "开本") +// book.Pages = extractNormalDetails(selection, "页数") +// book.Wordage = extractNormalDetails(selection, "字数") +// } +// } +// } +// +// books = append(books, book) +// } +// return books +//} +// +//// 根据url获取单个图书详情信息 +//func getUrlBookDetails(fetchMode, proxyType, username, password, machineCode, url string) (books []BookInfo, err error) { +// document, err := fetchDocument(fetchMode, proxyType, username, password, machineCode, url) +// if err != nil { +// return nil, err +// } +// +// fee, err := FetchBookDetailsShippingFee(url) +// 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) +// // 快递费 +// if fee != "" { +// if fee == "包邮" { +// book.ExpressDeliveryFee = "0" +// } else { +// book.ExpressDeliveryFee = fee +// } +// } +// books = append(books, book) +// return books, nil +//} +// +//// 获取店铺页面的所有快递费用 +//func FetchProductInfoWithChromedp(url string) (*ProductResponse, error) { +// // 创建上下文 +// opts := append(chromedp.DefaultExecAllocatorOptions[:], +// chromedp.Flag("headless", true), +// chromedp.Flag("disable-gpu", true), +// chromedp.Flag("no-sandbox", true), +// chromedp.Flag("disable-dev-shm-usage", true), +// chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"), +// // 禁用图片加载,加快速度 +// chromedp.Flag("blink-settings", "imagesEnabled=false"), +// ) +// // 启动浏览器 +// allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) +// defer cancel() +// // 创建浏览器上下文 +// ctx, cancel := chromedp.NewContext(allocCtx) +// defer cancel() +// // 设置超时 +// ctx, cancel = context.WithTimeout(ctx, 60*time.Second) +// defer cancel() +// log.Printf("[CHROMEDP] 开始导航到URL: %s", url) +// // 第一步:先导航到页面并等待完全加载 +// err := chromedp.Run(ctx, +// // 导航到目标页面 +// chromedp.Navigate(url), +// +// // 等待页面初步加载 +// chromedp.WaitReady("body", chromedp.ByQuery), +// chromedp.Sleep(3*time.Second), +// +// // 第二步:刷新页面,重新加载动态内容 +// chromedp.Reload(), +// +// // 等待刷新后页面加载 +// chromedp.WaitReady("body", chromedp.ByQuery), +// chromedp.Sleep(5*time.Second), +// +// // 等待关键元素出现 +// chromedp.WaitVisible(".item.clearfix", chromedp.ByQuery), +// +// // 滚动页面以触发动态加载 +// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight / 3)`, nil), +// chromedp.Sleep(2*time.Second), +// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight / 2)`, nil), +// chromedp.Sleep(2*time.Second), +// chromedp.Evaluate(`window.scrollTo(0, document.body.scrollHeight)`, nil), +// chromedp.Sleep(3*time.Second), +// chromedp.Evaluate(`window.scrollTo(0, 0)`, nil), +// chromedp.Sleep(1*time.Second), +// ) +// if err != nil { +// log.Printf("[CHROMEDP] 页面导航失败: %v", err) +// return nil, fmt.Errorf("页面导航失败: %v", err) +// } +// log.Printf("[CHROMEDP] 页面导航完成,开始提取数据") +// var evalResult map[string]interface{} +// // 执行任务 +// err = chromedp.Run(ctx, +// // 导航到目标页面 +// chromedp.Navigate(url), +// +// // 执行 JavaScript 来提取商品信息 +// chromedp.Evaluate(`(function() { +// console.log("开始提取商品信息..."); +// try { +// let productData = []; +// // 查找所有商品项 - 针对孔夫子旧书网的特定选择器 +// const itemSelectors = [ +// '.item.clearfix', +// '.list-item', +// '.product-item', +// '.goods-item', +// '[class*="item"]' +// ]; +// let items = []; +// for (let selector of itemSelectors) { +// const found = document.querySelectorAll(selector); +// if (found.length > 0) { +// items = found; +// console.log("使用选择器:", selector, "找到项目数:", items.length); +// break; +// } +// } +// +// if (items.length === 0) { +// // 如果没有找到特定选择器,尝试查找任何看起来像商品的项目 +// items = document.querySelectorAll('div[class*="item"], li[class*="item"]'); +// console.log("使用通用选择器找到项目数:", items.length); +// } +// console.log("总共找到商品项:", items.length); +// // 遍历每个商品项 +// items.forEach((item, index) => { +// // 提取 itemid +// let itemId = ''; +// +// // 方法1: 从元素属性获取 +// if (item.hasAttribute('itemid')) { +// itemId = item.getAttribute('itemid'); +// } +// +// // 方法2: 从数据属性获取 +// if (!itemId && item.hasAttribute('data-itemid')) { +// itemId = item.getAttribute('data-itemid'); +// } +// +// // 方法3: 从ID属性获取 +// if (!itemId && item.hasAttribute('id')) { +// const id = item.getAttribute('id'); +// if (id.includes('item') || id.includes('product')) { +// itemId = id; +// } +// } +// +// // 方法4: 从链接中提取itemid +// if (!itemId) { +// const links = item.querySelectorAll('a[href*="item"], a[href*="product"]'); +// for (let link of links) { +// const href = link.getAttribute('href'); +// if (href) { +// const itemMatch = href.match(/(?:item|product)[_-]?(\d+)/i); +// if (itemMatch) { +// itemId = itemMatch[1]; +// break; +// } +// } +// } +// } +// +// // 方法5: 从子元素中查找itemid +// if (!itemId) { +// const childWithItemId = item.querySelector('[itemid], [data-itemid]'); +// if (childWithItemId) { +// if (childWithItemId.hasAttribute('itemid')) { +// itemId = childWithItemId.getAttribute('itemid'); +// } else if (childWithItemId.hasAttribute('data-itemid')) { +// itemId = childWithItemId.getAttribute('data-itemid'); +// } +// } +// } +// +// // 提取书名 +// let bookName = ''; +// const titleSelectors = [ +// '.title a', +// '.book-title', +// '.item-title', +// '.name a', +// 'h3 a', +// 'h4 a', +// '.link', +// 'a[title]' +// ]; +// +// for (let selector of titleSelectors) { +// const titleEl = item.querySelector(selector); +// if (titleEl) { +// bookName = titleEl.textContent.trim(); +// if (bookName && bookName.length > 1) { +// break; +// } +// } +// } +// +// // 如果没找到,尝试在item内查找任何文本作为书名 +// if (!bookName) { +// const textContent = item.textContent; +// // 尝试提取看起来像书名的文本(较长的文本块) +// const lines = textContent.split('\n').map(line => line.trim()).filter(line => line.length > 5); +// if (lines.length > 0) { +// bookName = lines[0]; +// } +// } +// +// // 提取价格 +// let price = ''; +// const priceSelectors = [ +// '.price span', +// '.price .bold', +// '.current-price', +// '.sell-price', +// '.cost', +// '.money', +// '[class*="price"]', +// 'strong' +// ]; +// +// for (let selector of priceSelectors) { +// const priceEl = item.querySelector(selector); +// if (priceEl) { +// let priceText = priceEl.textContent.trim(); +// // 清理价格文本,保留数字和小数点 +// priceText = priceText.replace(/[^\d\.]/g, ''); +// if (priceText && !isNaN(parseFloat(priceText))) { +// price = '¥' + priceText; +// break; +// } +// } +// } +// +// // 如果没找到价格,尝试在文本中查找价格模式 +// if (!price) { +// const itemText = item.textContent; +// const priceMatch = itemText.match(/[¥¥]?\s*(\d+\.?\d*)/); +// if (priceMatch) { +// price = '¥' + priceMatch[1]; +// } +// } +// +// // 提取快递费用 +// let shippingFee = ''; +// const shippingSelectors = [ +// '.ship-fee', +// '.shipping-fee', +// '.express-fee', +// '.freight', +// '.postage', +// '[class*="fee"]', +// '[class*="快递"]', +// '[class*="运费"]' +// ]; +// +// for (let selector of shippingSelectors) { +// const shippingEl = item.querySelector(selector); +// if (shippingEl) { +// shippingFee = shippingEl.textContent.trim(); +// if (shippingFee) break; +// } +// } +// +// // 如果没找到运费元素,尝试在文本中查找运费关键词 +// if (!shippingFee) { +// const itemText = item.textContent; +// const feeMatches = itemText.match(/(运费[::]\s*[^\s\n]+)|(快递[::]\s*[^\s\n]+)|(邮费[::]\s*[^\s\n]+)/); +// if (feeMatches) { +// shippingFee = feeMatches[0]; +// } else if (itemText.includes('包邮') || itemText.includes('免运费')) { +// shippingFee = '包邮'; +// } else { +// shippingFee = '运费待确认'; +// } +// } +// +// // 清理书名(移除过长的文本) +// if (bookName && bookName.length > 100) { +// bookName = bookName.substring(0, 100) + '...'; +// } +// +// // 如果还没有itemid,生成一个基于索引的ID +// if (!itemId) { +// itemId = 'item_' + (index + 1); +// } +// +// // 添加到数据中 +// productData.push({ +// itemId: itemId, +// bookName: bookName || '商品${index + 1}', +// price: price || '价格待确认', +// shippingFee: shippingFee +// }); +// }); +// +// // 过滤掉明显无效的数据 +// productData = productData.filter(item => +// (item.bookName !== '商品1' || item.price !== '价格待确认') && +// item.bookName && item.bookName.length > 0 +// ); +// +// console.log("处理后商品数量:", productData.length); +// if (productData.length > 0) { +// console.log("前3个商品示例:", productData.slice(0, 3)); +// } +// +// return { +// success: true, +// message: "成功提取商品信息", +// data: productData +// }; +// +// } catch(error) { +// console.error("提取商品信息时出错:", error); +// return { +// success: false, +// message: "提取商品信息时出错: " + error.message, +// data: [] +// }; +// } +// })()`, &evalResult), +// ) +// +// if err != nil { +// return nil, fmt.Errorf("chromedp执行失败: %v", err) +// } +// +// // 处理 JavaScript 执行结果 +// response := &ProductResponse{} +// +// // 转换结果数据 +// if success, ok := evalResult["success"].(bool); ok { +// response.Success = success +// } +// if message, ok := evalResult["message"].(string); ok { +// response.Message = message +// } +// if data, ok := evalResult["data"].([]interface{}); ok { +// for _, item := range data { +// if productMap, ok := item.(map[string]interface{}); ok { +// product := ProductInfo{} +// if itemId, ok := productMap["itemId"].(string); ok { +// product.ItemID = itemId +// } +// if bookName, ok := productMap["bookName"].(string); ok { +// product.BookName = bookName +// } +// if price, ok := productMap["price"].(string); ok { +// product.Price = price +// } +// if shippingFee, ok := productMap["shippingFee"].(string); ok { +// info := extractShippingInfo(shippingFee) +// product.ShippingFee = info +// } +// response.Data = append(response.Data, product) +// } +// } +// } +// +// return response, nil +//} +// +//// 获取详情页的快递费用 +//func FetchBookDetailsShippingFee(url string) (string, error) { +// // 创建上下文 +// opts := append(chromedp.DefaultExecAllocatorOptions[:], +// chromedp.Flag("headless", true), +// chromedp.Flag("disable-gpu", true), +// chromedp.Flag("no-sandbox", true), +// chromedp.Flag("disable-dev-shm-usage", true), +// chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"), +// // 禁用图片加载,加快速度 +// chromedp.Flag("blink-settings", "imagesEnabled=false"), +// ) +// // 启动浏览器 +// allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) +// defer cancel() +// // 创建浏览器上下文 +// ctx, cancel := chromedp.NewContext(allocCtx) +// defer cancel() +// // 设置超时 +// ctx, cancel = context.WithTimeout(ctx, 60*time.Second) +// defer cancel() +// log.Printf("[CHROMEDP] 开始导航到URL: %s", url) +// var result string +// +// // 增强的 JavaScript 脚本 +// enhancedJSScript := ` +// (function() { +// // 查找包含快递费用的元素 +// const elements = document.querySelectorAll('*'); +// let shippingFee = null; +// +// elements.forEach(element => { +// if (element.textContent.includes('快递¥')) { +// const match = element.textContent.match(/快递¥(\d+\.\d{2})/); +// if (match) { +// shippingFee = match[1]; +// console.log('找到快递费用: ¥' + shippingFee); +// } +// } +// // 匹配格式2: 快递:包邮 +// else if (element.textContent.includes('快递:包邮') || element.textContent.includes('快递:包邮')) { +// shippingFee = '包邮'; +// console.log('找到快递费用: 包邮'); +// } +// // 匹配格式3: 快递 包邮 (包含空格变体) +// else if (element.textContent.match(/快递[::\s]包邮/)) { +// shippingFee = '包邮'; +// console.log('找到快递费用: 包邮'); +// } +// }); +// +// if (!shippingFee) { +// // 如果没找到,尝试搜索整个页面 +// const pageText = document.body.innerText; +// const match = pageText.match(/快递¥(\d+\.\d{2})/); +// if (match) { +// shippingFee = match[1]; +// console.log('找到快递费用: ¥' + shippingFee); +// } +// // 尝试匹配包邮 +// else if (pageText.match(/快递[::\s]包邮/)) { +// shippingFee = '包邮'; +// console.log('找到快递费用: 包邮'); +// } +// // 尝试其他包邮表述 +// else if (pageText.includes('包邮') && pageText.includes('快递')) { +// shippingFee = '包邮'; +// console.log('找到快递费用: 包邮'); +// } +// else { +// console.log('未找到快递费用信息'); +// } +// } +// return shippingFee; +// })();` +// // 先导航到页面并等待完全加载 +// err := chromedp.Run(ctx, +// // 导航到目标页面 +// chromedp.Navigate(url), +// // 刷新页面,重新加载动态内容 +// chromedp.Reload(), +// // 等待刷新后页面加载 +// chromedp.WaitReady("body", chromedp.ByQuery), +// chromedp.Sleep(8*time.Second), +// // 执行 JavaScript 来提取商品信息 +// chromedp.Evaluate(enhancedJSScript, &result), +// ) +// if err != nil { +// log.Printf("[CHROMEDP] 页面导航失败: %v", err) +// return "", fmt.Errorf("页面导航失败: %v", err) +// } +// log.Printf("[CHROMEDP] 页面导航完成,开始提取数据") +// +// if err != nil { +// return "", fmt.Errorf("chromedp执行失败: %v", err) +// } +// return result, nil +//} +// +//// 获取孔网实拍图(查询商品列表) +//func getKFZSPTImageURL(proxyType, username, password, machineCode, isbn string) (*BookInfo, error) { +// log.Printf("[DEBUG] ", isbn, " 获取图书ISBN: %s", isbn) +// url := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000", cf.API.ProductSearchURL, isbn, cf.App.Size) +// +// req := XxProxyRequest(proxyType, username, password, machineCode). +// Get(url). +// Set("User-Agent", cf.App.DefaultUserAgent). +// Set("Accept", "*/*") +// resp, body, errs := req.End() +// log.Printf("[DEBUG] 获取图书列表 URL: %s", url) +// +// // 只在有响应内容时才打印,避免日志过长 +// if len(body) > 0 && len(body) < 500 { +// log.Printf("[DEBUG] 获取图书列表响应: %s", body) +// } else if len(body) > 0 { +// log.Printf("[DEBUG] 获取图书列表响应长度: %d 字符", len(body)) +// } else { +// log.Printf("[DEBUG] 获取图书列表响应为空") +// } +// +// // 处理请求错误 +// if len(errs) > 0 { +// // 检查是否是代理认证失败 +// var proxyAuthFailed bool +// for _, e := range errs { +// if strings.Contains(e.Error(), "Proxy Authentication Required") { +// proxyAuthFailed = true +// break +// } +// } +// if proxyAuthFailed { +// return nil, fmt.Errorf("代理认证失败") +// } +// return nil, fmt.Errorf("查询请求失败: %v", errs) +// } +// +// // 检查HTTP状态码 +// if resp.StatusCode != http.StatusOK { +// space := strings.TrimSpace(resp.Status) +// return nil, fmt.Errorf("HTTP错误: %s", space) +// } +// +// // 解析响应 +// var apiResp 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 { +// Title string `json:"title"` +// ImgUrl string `json:"imgUrl"` +// ImgBigUrl string `json:"imgBigUrl"` +// ItemId int64 `json:"itemId"` +// ShopId int64 `json:"shopId"` +// TplRecords []struct { +// Key string `json:"key"` +// Value string `json:"value"` +// } `json:"tplRecords"` +// } `json:"list"` +// } `json:"itemResponse"` +// } `json:"data"` +// } +// +// if err := json.Unmarshal([]byte(body), &apiResp); err != nil { +// return nil, fmt.Errorf("解析JSON失败: %w", err) +// } +// +// // 如果找到商品,返回图片URL +// if apiResp.Data.ItemResponse.Total > 0 && len(apiResp.Data.ItemResponse.List) > 0 { +// // 确定起始索引 +// var startIndex int +// if cf.App.Size >= apiResp.Data.ItemResponse.Total { +// startIndex = apiResp.Data.ItemResponse.Total - 1 +// } else { +// startIndex = cf.App.Size - 1 +// } +// +// // 从指定索引开始,向前查找有效图片,最多重试3次 +// for attempt := 0; attempt < 3; attempt++ { +// currentIndex := startIndex - attempt +// +// // 检查索引是否有效 +// if currentIndex < 0 || currentIndex >= len(apiResp.Data.ItemResponse.List) { +// log.Printf("[DEBUG] 索引 %d 超出范围,跳过", currentIndex) +// continue +// } +// +// item := apiResp.Data.ItemResponse.List[currentIndex] +// +// // 检查图片URL是否存在 +// if item.ImgBigUrl == "" { +// log.Printf("[DEBUG] 索引 %d 的图片URL为空,跳过", currentIndex) +// continue +// } +// +// info := &BookInfo{} +// info.BookName = item.Title +// info.BookPicS = item.ImgUrl +// info.ItemId = item.ItemId +// info.ShopId = item.ShopId +// +// // 安全地获取TplRecords中的值 +// if len(item.TplRecords) > 0 { +// info.Author = item.TplRecords[0].Value +// } +// if len(item.TplRecords) > 1 { +// info.Publisher = item.TplRecords[1].Value +// } +// if len(item.TplRecords) > 2 { +// info.PublicationTime = validateDateFormat(item.TplRecords[2].Value) +// } +// if len(item.TplRecords) > 3 { +// info.BindingLayout = item.TplRecords[3].Value +// } +// +// // 成功时重置代理失败计数器 +// resetProxyFailCount() +// return info, nil +// } +// // 如果所有尝试都失败了,返回错误 +// log.Printf("[WARN] 经过3次尝试,未找到有效图片") +// return nil, fmt.Errorf("未找到有效图片,已尝试3次") +// } +// return nil, nil +//} +// +//// 获取孔网官图(查询图书条目) +//func getKFZGTImageURL(proxyType, username, password, machineCode, isbn string) (*BookInfo, error) { +// url := fmt.Sprintf("%s?keyword=%s", cf.API.BookSearchURL, isbn) +// +// info := &BookInfo{} +// +// req := XxProxyRequest(proxyType, username, password, machineCode). +// Get(url). +// Set("User-Agent", cf.App.DefaultUserAgent). +// Set("Accept", "*/*") +// resp, body, errs := req.End() +// log.Printf("[DEBUG] 获取图书条目 URL: %s", url) +// // 只在有响应内容时才打印,避免日志过长 +// if len(body) > 0 && len(body) < 500 { +// log.Printf("[DEBUG] 获取图书条目响应: %s", body) +// } else if len(body) > 0 { +// log.Printf("[DEBUG] 获取图书条目响应长度: %d 字符", len(body)) +// } else { +// log.Printf("[DEBUG] 获取图书条目响应为空") +// } +// +// // 处理请求错误 +// 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 { +// space := strings.TrimSpace(resp.Status) +// return nil, fmt.Errorf("HTTP错误: %s", space) +// } +// +// // 解析响应 +// var apiResp 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"` +// BookShowInfo []string `json:"bookShowInfo"` +// } `json:"list"` +// } `json:"itemResponse"` +// } `json:"data"` +// } +// +// if err := json.Unmarshal([]byte(body), &apiResp); err != nil { +// return nil, fmt.Errorf("解析JSON失败: %w", err) +// } +// +// // 如果找到条目,返回图片URL +// if apiResp.Data.ItemResponse.Total > 0 && len(apiResp.Data.ItemResponse.List) > 0 { +// list := apiResp.Data.ItemResponse.List[0] +// info := list.BookShowInfo +// +// bookItem := &BookInfo{ +// BookName: list.BookName, +// BookPic: list.ImgUrlEntity.BigImgUrl, +// Mid: list.Mid, +// } +// +// // 根据长度安全填充字段 +// if len(info) > 0 { +// bookItem.Author = info[0] +// } +// if len(info) > 1 { +// bookItem.Publisher = info[1] +// } +// if len(info) > 2 { +// bookItem.PublicationTime = validateDateFormat(info[2]) +// } +// if len(info) > 3 { +// bookItem.BindingLayout = info[3] +// } +// if len(info) > 4 { +// bookItem.FixPrice = info[4] +// } else { +// log.Printf("[WARN] BookShowInfo 长度不足 (仅 %d 项): %v", len(info), info) +// } +// +// fmt.Println("bookItem: ", bookItem) +// return bookItem, nil +// } +// +// return info, nil +//} +// +//// 替换所有空白字符为空格 +//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 extractShippingInfo(shippingFee string) string { +// // 匹配 "快递¥数字" 格式 +// re1 := regexp.MustCompile(`快递¥(\d+\.?\d*)`) +// // 匹配 "包邮" 格式 +// re2 := regexp.MustCompile(`(包邮)`) +// +// if matches := re1.FindStringSubmatch(shippingFee); len(matches) > 1 { +// return matches[1] // 返回数字部分 +// } +// +// if matches := re2.FindStringSubmatch(shippingFee); len(matches) > 1 { +// return "0" // 返回"包邮" +// } +// +// return "" +//} +// +//// 改进字段 +//func extractNormalDetails(s *goquery.Selection, fieldName string) string { +// var info string +// s.Each(func(i int, selection *goquery.Selection) { +// if strings.Contains(selection.Text(), fieldName) { +// all := strings.ReplaceAll(selection.Text(), " ", "") +// all = strings.ReplaceAll(all, "\n", "") +// all = strings.ReplaceAll(all, " ", "") +// split := strings.SplitN(all, ":", 2) +// if len(split) > 1 { +// info = split[1] +// } else { +// return +// } +// } +// }) +// return info +//} +// +//// 改进的字段提取函数 +//func extractNormalText(s *goquery.Selection, fieldName string) string { +// // 在左右两个分区中查找 +// selectors := []string{"div.f_left", "div.f_right"} +// for _, selector := range selectors { +// fieldItem := s.Find(selector + " div.normal-item").FilterFunction(func(i int, sel *goquery.Selection) bool { +// title := sel.Find("span.normal-title").Text() +// return strings.Contains(title, fieldName) +// }) +// +// if fieldItem.Length() > 0 { +// text := fieldItem.Find("span.normal-text").Text() +// return strings.TrimSpace(text) +// } +// } +// return "" +//} +// +//// 重置代理失败计数器(成功时调用) +//func resetProxyFailCount() { +// tailProxyMu.Lock() +// defer tailProxyMu.Unlock() +// proxyFailCount = 0 +//} +// +//// 验证日期格式 +//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 XxProxyRequest(proxyType, username, password, machineCode string) *gorequest.SuperAgent { +// proxyManagerOnce.Do(func() { +// log.Printf("[INFO] 初始化代理管理器,DLL路径: %s", cf.Proxy.ProxyFilePath) +// +// // 检查DLL文件是否存在 +// if _, err := os.Stat(cf.Proxy.ProxyFilePath); os.IsNotExist(err) { +// // 尝试在可执行文件目录查找 +// exePath, _ := os.Executable() +// exeDir := filepath.Dir(exePath) +// dllPath := filepath.Join(exeDir, cf.Proxy.ProxyFilePath) +// +// if _, err := os.Stat(dllPath); err == nil { +// cf.Proxy.ProxyFilePath = dllPath +// } else { +// proxyManagerInitErr = fmt.Errorf("代理DLL文件不存在: %s (也尝试了: %s)", cf.Proxy.ProxyFilePath, dllPath) +// return +// } +// } +// +// globalProxyManager, proxyManagerInitErr = NewProxyConfigManager(cf.Proxy.ProxyFilePath) +// if proxyManagerInitErr != nil { +// log.Printf("[ERROR] 代理管理器初始化失败: %v", proxyManagerInitErr) +// } else { +// log.Printf("[INFO] 代理管理器初始化成功") +// } +// }) +// typeManager, err := globalProxyManager.ProxyTypeManager( +// proxyType, +// username, +// password, +// machineCode, +// ) +// if err != nil { +// fmt.Printf("获取代理服务器信息失败: %v", err) +// } +// return gorequest.New().Proxy(typeManager).Timeout(120*time.Second).Retry(2, 3*time.Second) +//} +// +//// 初始化 +//func initializeConfig(config Config) { +// // 设置全局配置 +// cf = config +//} +// +//// 连接数据库(选品) +//func connectDBXP() (*sql.DB, error) { +// db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", +// cf.Database.Username, cf.Database.Password, cf.Database.Host, cf.Database.Name)) +// if err != nil { +// return nil, fmt.Errorf("打开数据库连接失败: %v", err) +// } +// // 设置连接池参数 +// db.SetMaxOpenConns(20) // 最大打开连接数 +// db.SetMaxIdleConns(10) // 最大空闲连接数 +// err = db.Ping() +// if err != nil { +// return nil, fmt.Errorf("数据库连接测试失败: %v", err) +// } +// return db, nil +//} +// +//// 从数据库随机获取一个可用账号 +//func getRandomAccount() (*AccountCredential, error) { +// mu.RLock() +// defer mu.RUnlock() +// // sql语句 +// query := ` +// SELECT id, credential_account, credential_password, token +// FROM t_credential +// WHERE credential_type = 'KWCredential' +// AND status = 0 +// AND is_del = 0 +// ORDER BY RAND() +// LIMIT 1` +// +// db, err := connectDBXP() +// if err != nil { +// return nil, err +// } +// var ac AccountCredential +// err = db.QueryRow(query).Scan(&ac.ID, &ac.Username, &ac.Password, &ac.Token) +// if err != nil { +// if err == sql.ErrNoRows { +// return nil, fmt.Errorf("没有可用的账号") +// } +// return nil, fmt.Errorf("查询账号失败: %v", err) +// } +// return &ac, nil +//} +// +//// 修改账号为不可用 +//func updateAccountInvalid(id int64) error { +// mu.RLock() +// defer mu.RUnlock() +// db, err := connectDBXP() +// if err != nil { +// return err +// } +// query := `UPDATE t_credential SET is_del = 1 WHERE id = ?` +// _, err = db.Exec(query, id) +// return fmt.Errorf("更新失败: %v", err) +//} +// +//// 修改账号为需要验证 +//func updateAccountNeedVerify(id int64) error { +// mu.RLock() +// defer mu.RUnlock() +// db, err := connectDBXP() +// if err != nil { +// return err +// } +// query := `UPDATE t_credential SET status = 1 WHERE id = ?` +// _, err = db.Exec(query, id) +// return err +//} +// +//// 更新账号token +//func updateAccountToken(id int64, token string) error { +// mu.RLock() +// defer mu.RUnlock() +// db, err := connectDBXP() +// if err != nil { +// return err +// } +// query := `UPDATE t_credential SET token = ? WHERE id = ?` +// _, err = db.Exec(query, token, id) +// return err +//} +// +//// 登录(带有Out的都非官方标准接口) +//// +////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, +// } +// 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)) +//} +// +//// 获取用户信息(带有Out的都非官方标准接口) +//// +////export OutGetUserMsg +//func OutGetUserMsg(token *C.char) *C.char { +// goToken := C.GoString(token) +// userInfo, err := outGetUserMsg(goToken) +// 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)) +//} +// +//// 获取商品模版(带有Out的都非官方标准接口) +//// +////export OutGetGoodsTplMsg +//func OutGetGoodsTplMsg(token, itemId, proxy *C.char) *C.char { +// goToken := C.GoString(token) +// goItemId := C.GoString(itemId) +// goProxy := C.GoString(proxy) +// info, err := outGetGoodsTplMsg(goToken, goItemId, goProxy) +// 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)) +//} +// +//// 获取商品列表-已登的店铺(带有Out的都非官方标准接口) +//// +////export OutGetGoodsListMsgFromSelfShop +//func OutGetGoodsListMsgFromSelfShop(token, proxy, itemSn, priceMin, priceMax *C.char, startCreateTime C.int, +// endCreateTime C.int, 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 := int(startCreateTime) +// goEndCreateTime := int(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) +// 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)) +//} +// +//// 删除商品-已登的店铺(带有Out的都非官方标准接口) +//// +////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) +// 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)) +//} +// +//// 新增商品(带有Out的都非官方标准接口) +//// +////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) +// 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)) +//} +// +//// 获取图片URL(官图和拍图)带有店铺过滤 +//// +////export OutGetImageFilterShopId +//func OutGetImageFilterShopId(token, isbn *C.char, shopId C.int, proxy *C.char, isLiveImage C.int, isReturnMsg C.int) *C.char { +// goToken := C.GoString(token) +// goIsbn := C.GoString(isbn) +// goShopId := int(shopId) +// goProxy := C.GoString(proxy) +// goIsLiveImage := int(isLiveImage) +// goIsReturnMsg := int(isReturnMsg) +// info, err := outGetImageFilterShopId(goToken, goIsbn, goShopId, goProxy, goIsLiveImage, goIsReturnMsg) +// 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)) +//} +// +//// 获取商品图片(带有Out的都非官方标准接口) +//// +////export OutGetImageByIsbn +//func OutGetImageByIsbn(token, isbn, proxy *C.char, isLiveImage C.int, isReturnMsg C.int) *C.char { +// goToken := C.GoString(token) +// goIsbn := C.GoString(isbn) +// goProxy := C.GoString(proxy) +// goIsLiveImage := int(isLiveImage) +// goIsReturnMsg := int(isReturnMsg) +// bookInfo, err := outGetImageByIsbn(goToken, goIsbn, goProxy, goIsLiveImage, goIsReturnMsg) +// 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)) +//} +// +//// 获取商品列表通过店铺ID(带有Out的都非官方标准接口) +//// +////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"` +// Data interface{} `json:"data,omitempty"` +// }{ +// GoodsNum: num, +// PNum: pNum, +// Data: books, +// } +// 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)) +//} +// +//// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口) +//// +////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)) +//} +// +//// 获取销量榜商品列表(带有Out的都非官放标准接口) +//// +////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)) +//} +// +////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":"初始化成功"}`) +//} +// +//// 导出函数:释放C字符串内存 +//// +////export FreeCString +//func FreeCString(str *C.char) { +// C.free(unsafe.Pointer(str)) +//} +// +//// 空main函数,编译DLL时需要 +//func main() { +//} diff --git a/proxyConfig.go b/proxy.go similarity index 100% rename from proxyConfig.go rename to proxy.go diff --git a/dll/xkongfz.h b/so/kongfz.h similarity index 71% rename from dll/xkongfz.h rename to so/kongfz.h index 5c450e9..ce93dd7 100644 --- a/dll/xkongfz.h +++ b/so/kongfz.h @@ -96,13 +96,37 @@ extern "C" { // extern __declspec(dllexport) char* OutLogin(char* username, char* password); +// 获取用户信息(带有Out的都非官方标准接口) +// +extern __declspec(dllexport) char* OutGetUserMsg(char* token); + +// 获取商品模版(带有Out的都非官方标准接口) +// +extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* itemId, char* proxy); + +// 获取商品列表-已登的店铺(带有Out的都非官方标准接口) +// +extern __declspec(dllexport) char* OutGetGoodsListMsgFromSelfShop(char* token, char* proxy, char* itemSn, char* priceMin, char* priceMax, int startCreateTime, int endCreateTime, char* requestType, int isItemSnEqual, int page, int size); + +// 删除商品-已登的店铺(带有Out的都非官方标准接口) +// +extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId); + +// 新增商品(带有Out的都非官方标准接口) +// +extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData); + +// 获取图片URL(官图和拍图)带有店铺过滤 +// +extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* isbn, int shopId, char* proxy, int isLiveImage, int isReturnMsg); + // 获取商品图片(带有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); +extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum); // 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口) // diff --git a/zjdydll.go b/zjdydll.go index ae576ae..e6f987f 100644 --- a/zjdydll.go +++ b/zjdydll.go @@ -1,1153 +1,1153 @@ package main -import "C" -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "path/filepath" - "syscall" - "unsafe" -) - -// 配置结构 -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"` -} - -// API响应结构 -type APIResponses struct { - Success bool `json:"success"` - Message string `json:"message,omitempty"` - GoodsNum string `json:"goods_num,omitempty"` - PNum string `json:"pnum,omitempty"` - Data interface{} `json:"data,omitempty"` - Error string `json:"error,omitempty"` -} - -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) callFunction(funcName string, arg string) (string, error) { - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - argPtr, _ := syscall.BytePtrFromString(arg) - r1, _, err := proc.Call(uintptr(unsafe.Pointer(argPtr))) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -// 二参数函数调用 -func (m *DLLManager) callFunctionTwoArgs(funcName string, args ...string) (string, error) { - if len(args) != 2 { - return "", fmt.Errorf("函数 %s 需要2个参数,但提供了 %d 个", funcName, len(args)) - } - - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - // 准备参数指针 - argPtrs := make([]uintptr, 2) - for i, arg := range args { - argPtr, _ := syscall.BytePtrFromString(arg) - argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) - } - - r1, _, err := proc.Call( - argPtrs[0], // proxyType - argPtrs[1], // username - ) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -// 五参数函数调用 - 用于 GetKFZGTImageURL 和 GetKFZSPTImageURL -func (m *DLLManager) callFunctionFiveArgs(funcName string, args ...string) (string, error) { - if len(args) != 5 { - return "", fmt.Errorf("函数 %s 需要5个参数,但提供了 %d 个", funcName, len(args)) - } - - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - // 准备参数指针 - argPtrs := make([]uintptr, 5) - for i, arg := range args { - argPtr, _ := syscall.BytePtrFromString(arg) - argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) - } - - r1, _, err := proc.Call( - argPtrs[0], // proxyType - argPtrs[1], // username - argPtrs[2], // password - argPtrs[3], // machineCode - argPtrs[4], // isbn - ) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -// 六参数函数调用 -func (m *DLLManager) callFunctionSixArgs(funcName string, args ...string) (string, error) { - if len(args) != 6 { - return "", fmt.Errorf("函数 %s 需要6个参数,但提供了 %d 个", funcName, len(args)) - } - - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - // 准备参数指针 - argPtrs := make([]uintptr, 6) - for i, arg := range args { - argPtr, _ := syscall.BytePtrFromString(arg) - argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) - } - - r1, _, err := proc.Call( - argPtrs[0], // fetchMode - argPtrs[1], // proxyType - argPtrs[2], // username - argPtrs[3], // password - argPtrs[4], // machineCode - argPtrs[5], // isbn - ) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -// 十二参数函数调用 - 专门用于 GetKFZShopBookInfo -func (m *DLLManager) callFunctionTwelveArgs(funcName string, args ...string) (string, error) { - if len(args) != 12 { - return "", fmt.Errorf("函数 %s 需要12个参数,但提供了 %d 个", funcName, len(args)) - } - - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - // 准备参数指针 - argPtrs := make([]uintptr, 12) - for i, arg := range args { - argPtr, _ := syscall.BytePtrFromString(arg) - argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) - } - - r1, _, err := proc.Call( - argPtrs[0], // proxyType - argPtrs[1], // username - argPtrs[2], // password - argPtrs[3], // machineCode - argPtrs[4], // shopId - argPtrs[5], // isImage - argPtrs[6], // bookNum - argPtrs[7], // pageNum - argPtrs[8], // sortType - argPtrs[9], // sort - argPtrs[10], // priceDown - argPtrs[11], // priceUp - ) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -// 十三参数函数调用 - 专门用于 GetKFZShopBookInfo -func (m *DLLManager) callFunctionThirteenArgs(funcName string, args ...string) (string, error) { - if len(args) != 13 { - return "", fmt.Errorf("函数 %s 需要13个参数,但提供了 %d 个", funcName, len(args)) - } - - proc, err := m.dll.FindProc(funcName) - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) - } - - // 准备参数指针 - argPtrs := make([]uintptr, 13) - for i, arg := range args { - argPtr, _ := syscall.BytePtrFromString(arg) - argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) - } - - r1, _, err := proc.Call( - argPtrs[0], // fetchMode - argPtrs[1], // proxyType - argPtrs[2], // username - argPtrs[3], // password - argPtrs[4], // machineCode - argPtrs[5], // shopId - argPtrs[6], // isImage - argPtrs[7], // bookNum - argPtrs[8], // pageNum - argPtrs[9], // sortType - argPtrs[10], // sort - argPtrs[11], // priceDown - argPtrs[12], // priceUp - ) - if err != nil && err.Error() != "The operation completed successfully." { - return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) - } - - result := (*byte)(unsafe.Pointer(r1)) - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) - } - - // 释放内存 - freeProc, _ := m.dll.FindProc("FreeString") - if freeProc != nil { - freeProc.Call(r1) - } - - return string(resultBytes), nil -} - -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(fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp string) (string, error) { - return m.callFunctionThirteenArgs("GetKFZShopBookInfo", - fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp) -} - -// 获取 GetUrlBookDetails - 需要6个参数 -func (m *DLLManager) GetUrlBookDetails(fetchMode, proxyType, username, password, machineCode, url string) (string, error) { - return m.callFunctionSixArgs("GetUrlBookDetails", fetchMode, proxyType, username, password, machineCode, url) -} - -// 获取 OutLogin -func (m *DLLManager) OutLogin(username, password string) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutLogin") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutLogin", err) - } - // 转换参数 - usernamePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(username))) - passwordPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(password))) - // 调用函数 - ret, _, err := proc.Call(usernamePtr, passwordPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetUserMsg -func (m *DLLManager) OutGetUserMsg(token string) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetUserMsg") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetUserMsg", err) - } - // 转换参数 - tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) - // 调用函数 - ret, _, err := proc.Call(tokenPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetGoodsTplMsg -func (m *DLLManager) OutGetGoodsTplMsg(token, itemId, proxy string) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetGoodsTplMsg") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsTplMsg", err) - } - // 转换参数 - tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) - itemIdPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(itemId))) - proxyPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - // 调用函数 - ret, _, err := proc.Call(tokenPtr, itemIdPtr, proxyPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetGoodsListMsgFromSelfShop -func (m *DLLManager) OutGetGoodsListMsgFromSelfShop(token string, proxy string, itemSn string, priceMin string, priceMax string, startCreateTime int, - endCreateTime int, requestType string, isItemSnEqual int, page int, size int) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetGoodsListMsgFromSelfShop") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsListMsgFromSelfShop", err) - } - // 转换参数 - tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) - proxyPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - itemSnPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(itemSn))) - priceMinPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(priceMin))) - priceMaxPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(priceMax))) - startCreateTimePtr := uintptr(startCreateTime) - endCreateTimePtr := uintptr(endCreateTime) - requestTypePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(requestType))) - isItemSnEqualPtr := uintptr(isItemSnEqual) - pagePtr := uintptr(page) - sizePtr := uintptr(size) - // 调用函数 - ret, _, err := proc.Call(tokenPtr, proxyPtr, itemSnPtr, priceMinPtr, priceMaxPtr, startCreateTimePtr, endCreateTimePtr, requestTypePtr, isItemSnEqualPtr, pagePtr, sizePtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetImageByIsbn -func (m *DLLManager) OutGetImageByIsbn(token string, isbn string, proxy string, isLiveImage bool, isReturnMsg bool) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetImageByIsbn") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetImageByIsbn", err) - } - // 转换参数 - tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) - isbnPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(isbn))) - var proxyPtr uintptr - if proxy != "" { - proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - } else { - proxyPtr = 0 - } - var isLiveImageInt int - if isLiveImage { - isLiveImageInt = 0 - } else { - isLiveImageInt = 1 - } - isLiveImagePtr := uintptr(isLiveImageInt) - var isReturnMsgInt int - if isReturnMsg { - isLiveImageInt = 0 - } else { - isLiveImageInt = 1 - } - isReturnMsgPtr := uintptr(isReturnMsgInt) - // 调用函数 - ret, _, err := proc.Call(tokenPtr, isbnPtr, proxyPtr, isLiveImagePtr, isReturnMsgPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetGoodsMsgByDetailUrl -func (m *DLLManager) OutGetGoodsMsgByDetailUrl(url, proxy string) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetGoodsMsgByDetailUrl") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsMsgByDetailUrl", err) - } - // 转换参数 - urlPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(url))) - var proxyPtr uintptr - if proxy != "" { - proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - } else { - proxyPtr = 0 - } - // 调用函数 - ret, _, err := proc.Call(urlPtr, proxyPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetGoodsListMsgByShopId -func (m *DLLManager) OutGetGoodsListMsgByShopId(shopId int, proxy string, isImage bool, sortType string, sort string, priceMin float32, priceMax float32, pageNum, returnNum int) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetGoodsListMsgByShopId") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsListMsgByShopId", err) - } - // 转换参数 - shopIdPtr := uintptr(shopId) - var isImageInt int - if isImage { - isImageInt = 0 - } else { - isImageInt = 1 - } - isImagePtr := uintptr(isImageInt) - sortTypePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(sortType))) - sortPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(sort))) - priceMinPtr := uintptr(*(*uint32)(unsafe.Pointer(&priceMin))) - priceMaxPtr := uintptr(*(*uint32)(unsafe.Pointer(&priceMax))) - pageNumPtr := uintptr(pageNum) - returnNumPtr := uintptr(returnNum) - var proxyPtr uintptr - if proxy != "" { - proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - } else { - proxyPtr = 0 - } - // 调用函数 - ret, _, err := proc.Call(shopIdPtr, proxyPtr, isImagePtr, sortTypePtr, sortPtr, priceMinPtr, priceMaxPtr, pageNumPtr, returnNumPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 获取 OutGetTopGoodsListMsg -func (m *DLLManager) OutGetTopGoodsListMsg(catId int, proxy string) (string, error) { - // 获取函数 - proc, err := m.dll.FindProc("OutGetTopGoodsListMsg") - if err != nil { - return "", fmt.Errorf("找不到函数 %s: %v", "OutGetTopGoodsListMsg", err) - } - // 转换参数 - catIdPtr := uintptr(catId) - var proxyPtr uintptr - if proxy != "" { - proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) - } else { - proxyPtr = 0 - } - // 调用函数 - ret, _, err := proc.Call(catIdPtr, proxyPtr) - if ret == 0 { - return "", fmt.Errorf("DLL调用失败: %v", err) - } - - // 将C字符串转换为Go字符串 - var resultBytes []byte - for i := 0; ; i++ { - if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { - break - } - resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) - } - - // 释放内存(假设DLL提供了FreeMemory函数) - findProc, err := m.dll.FindProc("FreeString") - if findProc != nil { - findProc.Call(ret) - } - return string(resultBytes), nil -} - -// 创建默认配置 -func createDefaultConfig() Config { - var configs Config - - // 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" - - configs.Database.Username = "newAdmin" - configs.Database.Password = "bYPp8SbBe5F7nz2i" - configs.Database.Host = "146.56.227.42:3306" - configs.Database.Name = "newadmin" - - return configs -} - -// 获取当前可执行文件所在目录 -func getExecutableDir() string { - exePath, err := os.Executable() - if err != nil { - return "." - } - return filepath.Dir(exePath) -} - -// 格式化输出JSON响应 -func printFormattedResponse(operation string, result string, err error) int { - var apiResp APIResponses - fmt.Printf("\n%s:\n", operation) - fmt.Println("=" + string(make([]byte, 50)) + "=") - - if err != nil { - fmt.Printf("❌ 错误: %v\n", err) - } - var count int - if err := json.Unmarshal([]byte(result), &apiResp); err == nil { - if apiResp.Success { - fmt.Printf("✅ 成功: %s\n", apiResp.Message) - if apiResp.Data != nil { - fmt.Println("📊 数据:") - jsonData, _ := json.MarshalIndent(apiResp.Data, "", " ") - fmt.Println(string(jsonData)) - // 方法1: 判断 Data 的类型并统计条数 - switch data := apiResp.Data.(type) { - case []interface{}: - // 如果是切片/数组 - count = len(data) - fmt.Printf("📈 数据条数: %d\n", count) - case []BookInfo: - // 如果是 BookInfo 切片 - count = len(data) - fmt.Printf("📚 图书数量: %d\n", count) - case map[string]interface{}: - // 如果是映射 - count = len(data) - fmt.Printf("🗂️ 数据字段数: %d\n", count) - case []ProductInfo: - // 如果是 ProductInfo 切片 - count = len(data) - fmt.Printf("🛒 商品数量: %d\n", count) - default: - fmt.Printf("ℹ️ 数据类型: %T\n", apiResp.Data) - } - } - if apiResp.GoodsNum != "" { - fmt.Printf("📦 商品数量: %s\n", apiResp.GoodsNum) - } - if apiResp.PNum != "" { - fmt.Printf("📄 页码: %s\n", apiResp.PNum) - } - } else { - fmt.Printf("❌ 失败: %s\n", apiResp.Error) - } - } else { - fmt.Printf("原始响应: %s\n", result) - // 如果是代理地址,可以进一步处理 - if len(result) > 0 && (result[0:4] == "http" || result[0:3] == "socks") { - fmt.Printf("✅ 代理服务器地址获取成功\n") - fmt.Printf("🔗 代理地址: %s\n", result) - } - } - return count -} - -// 专门处理代理配置响应的函数 -func handleProxyResponse(result string, err error) { - fmt.Printf("\n代理配置测试:\n") - fmt.Println("=" + string(make([]byte, 50)) + "=") - - if err != nil { - fmt.Printf("❌ 错误: %v\n", err) - return - } - - // 代理配置通常返回代理服务器地址,不是JSON格式 - fmt.Printf("📡 代理服务器地址: %s\n", result) - fmt.Printf("✅ 代理配置获取成功\n") - - // 如果需要,可以在这里进一步处理代理地址 - // 例如:验证代理格式、提取IP和端口等 - if result != "" { - fmt.Printf("🔗 可用代理: %s\n", result) - } -} - -// // ProxyTypeManager 调用代理类型管理器 +//import "C" +//import ( +// "encoding/json" +// "fmt" +// "log" +// "net/http" +// "os" +// "path/filepath" +// "syscall" +// "unsafe" +//) // -// func (m *DLLManager) ProxyTypeManager(proxyType, username, password, machineCode string) (string, error) { -// proc, err := m.dll.FindProc("ProxyTypeManager") -// if err != nil { -// return "", fmt.Errorf("找不到函数 ProxyTypeManager: %v", err) -// } +//// 配置结构 +//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"` // -// // 准备参数 -// proxyTypePtr, _ := syscall.BytePtrFromString(proxyType) -// usernamePtr, _ := syscall.BytePtrFromString(username) -// passwordPtr, _ := syscall.BytePtrFromString(password) -// machineCodePtr, _ := syscall.BytePtrFromString(machineCode) +// API struct { +// LoginURL string `json:"login_url"` +// BookSearchURL string `json:"book_search_url"` +// ProductSearchURL string `json:"product_search_url"` +// } `json:"api"` // -// // 调用函数 -// r1, _, err := proc.Call( -// uintptr(unsafe.Pointer(proxyTypePtr)), -// uintptr(unsafe.Pointer(usernamePtr)), -// uintptr(unsafe.Pointer(passwordPtr)), -// uintptr(unsafe.Pointer(machineCodePtr)), -// ) +// 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"` +//} // -// if err != nil && err.Error() != "The operation completed successfully." { -// return "", fmt.Errorf("调用 ProxyTypeManager 失败: %v", err) -// } +//// API响应结构 +//type APIResponses struct { +// Success bool `json:"success"` +// Message string `json:"message,omitempty"` +// GoodsNum string `json:"goods_num,omitempty"` +// PNum string `json:"pnum,omitempty"` +// Data interface{} `json:"data,omitempty"` +// Error string `json:"error,omitempty"` +//} // -// // 转换结果 -// 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) -// } +//type DLLManager struct { +// dll *syscall.DLL +//} // -// // 释放内存 -// freeProc, _ := m.dll.FindProc("FreeString") -// if freeProc != nil { -// freeProc.Call(r1) -// } -// -// return string(resultBytes), nil +//func NewDLLManager(dllPath string) (*DLLManager, error) { +// dll, err := syscall.LoadDLL(dllPath) +// if err != nil { +// return nil, fmt.Errorf("加载DLL失败: %v", err) // } // -// FreeCString 释放C字符串内存 -func (m *DLLManager) FreeCString(strPtr uintptr) error { - proc, err := m.dll.FindProc("FreeCString") - if err != nil { - return fmt.Errorf("找不到函数 FreeCString: %v", err) - } - - _, _, err = proc.Call(strPtr) - if err != nil && err.Error() != "The operation completed successfully." { - return fmt.Errorf("调用 FreeCString 失败: %v", err) - } - - return nil -} - -func main() { - http.HandleFunc("/api/kfzShopBookInfo", handleGetKFZShopBookInfo) - port := "8080" - server := &http.Server{ - Addr: ":" + port, - Handler: nil, - } - // 启动服务器 - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - fmt.Printf("服务器启动失败: %v\n", err) - } -} - -func handleGetKFZShopBookInfo(w http.ResponseWriter, r *http.Request) { - // 加载DLL - manager, err := NewDLLManager("dll/kongfz.dll") - if err != nil { - fmt.Printf("初始化DLL管理器失败: %v", err) - } - //defer manager.Close() - - log.Println("✅ DLL加载成功") - - // 使用默认配置初始化 - config := createDefaultConfig() - initializeConfig(config) - configJSON, err := json.Marshal(config) - if err != nil { - fmt.Printf("序列化配置失败: %v", err) - } - - result, err := manager.Initialize(string(configJSON)) - if err != nil { - fmt.Printf("初始化失败: %v", err) - } - - var initResp APIResponses - if err := json.Unmarshal([]byte(result), &initResp); err != nil { - fmt.Printf("解析初始化响应失败: %v", err) - } - - if !initResp.Success { - fmt.Printf("初始化失败: %s", initResp.Message) - } - - log.Println("✅ DLL初始化成功") - // 设置响应头 - w.Header().Set("Content-Type", "application/json; charset=utf-8") - - // 只支持GET请求 - if r.Method != http.MethodGet { - sendErrorResponse(w, http.StatusMethodNotAllowed, "只支持GET方法") - return - } - - // 获取代理信息 - typeManager, err := proxyTypeManager("CALF_ELEPHANT_PROXY", "1297757178467602432", "QgQBvP7f", "") - if err != nil { - fmt.Printf(err.Error()) - } - fmt.Println(typeManager) - - user, err := manager.OutLogin("18904056801", "Long6166@@") - if err != nil { - fmt.Printf(err.Error()) - } - fmt.Println(user) - - var data APIResponse - if err := json.Unmarshal([]byte(user), &data); err != nil { - log.Printf("[ERROR] 解析第 %d 页失败: %v", data, err) - } - var token string - // 从 Data 中提取 token - if dataMap, ok := data.Data.(map[string]interface{}); ok { - if tk, exists := dataMap["token"]; exists { - fmt.Println("Token:", tk) - token = tk.(string) - // Token: d322c80960ab6722922912dd2ce219d4b4099d54 - } else { - fmt.Println("Token 不存在") - } - } else { - fmt.Println("Data 格式不正确") - } - fmt.Println(token) - - msg, err := manager.OutGetUserMsg(token) - if err != nil { - fmt.Printf(err.Error()) - } - fmt.Println(msg) - - tplMsg, err := manager.OutGetGoodsTplMsg(token, "9142583516", "") - if err != nil { - fmt.Printf(err.Error()) - } - fmt.Println(tplMsg) - - shop, err := manager.OutGetGoodsListMsgFromSelfShop(token, "", "", "", "", 0, 0, "allUnSold", 0, 1, 10) - if err != nil { - fmt.Printf(err.Error()) - } - fmt.Println(shop) - - //// 获取cookie - //cookie, err := loginCookie() - //if err != nil { - // fmt.Printf(err.Error()) - //} - //fmt.Println(cookie) - - //account, err := getRandomAccount() - //if err != nil { - // fmt.Printf(err.Error()) - //} - //fmt.Println(account) - - //var isbns = []string{ - // "9787500601593", "9787506331746", "9787020106752", "9787807089353", "9787536692930", "9787530221532", "9787544270878", "9787208061644", "9787506365437", "9787513708371", "9787513922135", "9787536693968", "9787541151736", "9787544213561", "9787544754781", "9787549204304", "9787108006639", "9787531764700", "9787544253994", "9787540456023", "9787540456429", "9787544267618", "9787544277723", "9787550008496", "9787806070680", "9787806801529", "9787807530244", "9787020008728", "9787020139590", "9787204055401", "9787500680239", "9787505724778", "9787506365680", "9787530221525", "9787536097261", - //} - // - //for _, isbn := range isbns { - // // 获取孔网图片 - // isbnss, err := outGetImageByIsbn("", isbn, "", 0, 0) - // if err != nil { - // fmt.Printf(err.Error()) - // } - // fmt.Println(isbnss) - //} - - //books, num, pNum, err := outGetGoodsListMsgByShopId(3092, - // typeManager, true, "", - // "", 0, 14, 1, 5) - - //msg, err := manager.OutGetTopGoodsListMsg(0, "") - //if err != nil { - // - //} - //fmt.Println(msg) - //fetchMode := r.URL.Query().Get("fetchMode") - //proxyType := r.URL.Query().Get("proxyType") - //username := r.URL.Query().Get("username") - //password := r.URL.Query().Get("password") - //machineCode := r.URL.Query().Get("machineCode") - //url := r.URL.Query().Get("url") - //shopId := r.URL.Query().Get("shopId") //https://shop.kongfz.com/793900/all/0_100_0_0_1_sort_desc_0_0/ - //isImage := r.URL.Query().Get("isImage") - //bookNum := r.URL.Query().Get("bookNum") - //pageNum := r.URL.Query().Get("pageNum") - //sortType := r.URL.Query().Get("sortType") - //sort := r.URL.Query().Get("sort") - //priceDown := r.URL.Query().Get("priceDown") - //priceUp := r.URL.Query().Get("priceUp") - - //details, err := manager.OutGetGoodsMsgByDetailUrl("https://book.kongfz.com/151391/6531924071/", "") - //if err != nil { - // - //} - //fmt.Println(details) - - //id, err := manager.OutGetGoodsListMsgByShopId(1181761, - // "", true, "", - // "", 0, 0, 1, 5) - //if err != nil { - // fmt.Println(err) - //} - //fmt.Println(id) - //var pa ShopBookResult - //if err := json.Unmarshal([]byte(id), &pa); err != nil { - // log.Printf("[ERROR] 解析第 %d 页失败: %v", pa, err) - //} - //for _, urls := range pa.Data.Data { - // ss, err := proxyTypeManager("CALF_ELEPHANT_PROXY", "1297757178467602432", "QgQBvP7f", "") - // if err != nil { - // fmt.Printf(err.Error()) - // } - // fmt.Println(ss) - // url, err := manager.OutGetGoodsMsgByDetailUrl(urls.DetailURL, ss) - // if err != nil { - // fmt.Println(err) - // } - // fmt.Println(url) - //} - - //books, num, pNum, err := outGetGoodsListMsgByShopId(1181761, - // typeManager, true, "", - // "", 0, 0, 1, 5) - // - //fmt.Println(books) - //fmt.Println(num) - //fmt.Println(pNum) - //fmt.Println(err) - //for _, book := range books { - // ss, err := proxyTypeManager("TAIL_PROXY", "", "", "07f4d0fbcff99966c2b37b0c1fb7f01c") - // if err != nil { - // fmt.Printf(err.Error()) - // } - // fmt.Println(ss) - // url, err := outGetGoodsMsgByDetailUrl(book.DetailUrl, ss) - // if err != nil { - // fmt.Println(err) - // } - // fmt.Println(url) - //} - -} - -func manegiyoutGetGoodsListMsgFromSelfShop() { - -} - -// 发送错误响应 -func sendErrorResponse(w http.ResponseWriter, statusCode int, message string) { - response := APIResp{ - Success: false, - Message: message, - } - w.WriteHeader(statusCode) - json.NewEncoder(w).Encode(response) -} - -type ShopBookResult struct { - Success bool `json:"success"` - Message string `json:"message"` - Data struct { - GoodsNum string `json:"goods_num"` - Pnum string `json:"pnum"` - Data []struct { - BookName string `json:"book_name"` - Author string `json:"author"` - Publisher string `json:"publisher"` - ISBN string `json:"isbn"` - PublishTime 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 int `json:"mid"` - ItemID int64 `json:"item_id"` - ShopID int `json:"shop_id"` - DetailURL string `json:"detail_url"` - } `json:"data"` - } `json:"data"` -} +// return &DLLManager{dll: dll}, nil +//} +// +//func (m *DLLManager) Close() { +// if m.dll != nil { +// m.dll.Release() +// } +//} +// +//// 单参数函数调用 +//func (m *DLLManager) callFunction(funcName string, arg string) (string, error) { +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// argPtr, _ := syscall.BytePtrFromString(arg) +// r1, _, err := proc.Call(uintptr(unsafe.Pointer(argPtr))) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//// 二参数函数调用 +//func (m *DLLManager) callFunctionTwoArgs(funcName string, args ...string) (string, error) { +// if len(args) != 2 { +// return "", fmt.Errorf("函数 %s 需要2个参数,但提供了 %d 个", funcName, len(args)) +// } +// +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// // 准备参数指针 +// argPtrs := make([]uintptr, 2) +// for i, arg := range args { +// argPtr, _ := syscall.BytePtrFromString(arg) +// argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) +// } +// +// r1, _, err := proc.Call( +// argPtrs[0], // proxyType +// argPtrs[1], // username +// ) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//// 五参数函数调用 - 用于 GetKFZGTImageURL 和 GetKFZSPTImageURL +//func (m *DLLManager) callFunctionFiveArgs(funcName string, args ...string) (string, error) { +// if len(args) != 5 { +// return "", fmt.Errorf("函数 %s 需要5个参数,但提供了 %d 个", funcName, len(args)) +// } +// +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// // 准备参数指针 +// argPtrs := make([]uintptr, 5) +// for i, arg := range args { +// argPtr, _ := syscall.BytePtrFromString(arg) +// argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) +// } +// +// r1, _, err := proc.Call( +// argPtrs[0], // proxyType +// argPtrs[1], // username +// argPtrs[2], // password +// argPtrs[3], // machineCode +// argPtrs[4], // isbn +// ) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//// 六参数函数调用 +//func (m *DLLManager) callFunctionSixArgs(funcName string, args ...string) (string, error) { +// if len(args) != 6 { +// return "", fmt.Errorf("函数 %s 需要6个参数,但提供了 %d 个", funcName, len(args)) +// } +// +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// // 准备参数指针 +// argPtrs := make([]uintptr, 6) +// for i, arg := range args { +// argPtr, _ := syscall.BytePtrFromString(arg) +// argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) +// } +// +// r1, _, err := proc.Call( +// argPtrs[0], // fetchMode +// argPtrs[1], // proxyType +// argPtrs[2], // username +// argPtrs[3], // password +// argPtrs[4], // machineCode +// argPtrs[5], // isbn +// ) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//// 十二参数函数调用 - 专门用于 GetKFZShopBookInfo +//func (m *DLLManager) callFunctionTwelveArgs(funcName string, args ...string) (string, error) { +// if len(args) != 12 { +// return "", fmt.Errorf("函数 %s 需要12个参数,但提供了 %d 个", funcName, len(args)) +// } +// +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// // 准备参数指针 +// argPtrs := make([]uintptr, 12) +// for i, arg := range args { +// argPtr, _ := syscall.BytePtrFromString(arg) +// argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) +// } +// +// r1, _, err := proc.Call( +// argPtrs[0], // proxyType +// argPtrs[1], // username +// argPtrs[2], // password +// argPtrs[3], // machineCode +// argPtrs[4], // shopId +// argPtrs[5], // isImage +// argPtrs[6], // bookNum +// argPtrs[7], // pageNum +// argPtrs[8], // sortType +// argPtrs[9], // sort +// argPtrs[10], // priceDown +// argPtrs[11], // priceUp +// ) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//// 十三参数函数调用 - 专门用于 GetKFZShopBookInfo +//func (m *DLLManager) callFunctionThirteenArgs(funcName string, args ...string) (string, error) { +// if len(args) != 13 { +// return "", fmt.Errorf("函数 %s 需要13个参数,但提供了 %d 个", funcName, len(args)) +// } +// +// proc, err := m.dll.FindProc(funcName) +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", funcName, err) +// } +// +// // 准备参数指针 +// argPtrs := make([]uintptr, 13) +// for i, arg := range args { +// argPtr, _ := syscall.BytePtrFromString(arg) +// argPtrs[i] = uintptr(unsafe.Pointer(argPtr)) +// } +// +// r1, _, err := proc.Call( +// argPtrs[0], // fetchMode +// argPtrs[1], // proxyType +// argPtrs[2], // username +// argPtrs[3], // password +// argPtrs[4], // machineCode +// argPtrs[5], // shopId +// argPtrs[6], // isImage +// argPtrs[7], // bookNum +// argPtrs[8], // pageNum +// argPtrs[9], // sortType +// argPtrs[10], // sort +// argPtrs[11], // priceDown +// argPtrs[12], // priceUp +// ) +// if err != nil && err.Error() != "The operation completed successfully." { +// return "", fmt.Errorf("调用函数 %s 失败: %v", funcName, err) +// } +// +// result := (*byte)(unsafe.Pointer(r1)) +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))) +// } +// +// // 释放内存 +// freeProc, _ := m.dll.FindProc("FreeString") +// if freeProc != nil { +// freeProc.Call(r1) +// } +// +// return string(resultBytes), nil +//} +// +//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(fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp string) (string, error) { +// return m.callFunctionThirteenArgs("GetKFZShopBookInfo", +// fetchMode, proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp) +//} +// +//// 获取 GetUrlBookDetails - 需要6个参数 +//func (m *DLLManager) GetUrlBookDetails(fetchMode, proxyType, username, password, machineCode, url string) (string, error) { +// return m.callFunctionSixArgs("GetUrlBookDetails", fetchMode, proxyType, username, password, machineCode, url) +//} +// +//// 获取 OutLogin +//func (m *DLLManager) OutLogin(username, password string) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutLogin") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutLogin", err) +// } +// // 转换参数 +// usernamePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(username))) +// passwordPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(password))) +// // 调用函数 +// ret, _, err := proc.Call(usernamePtr, passwordPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetUserMsg +//func (m *DLLManager) OutGetUserMsg(token string) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetUserMsg") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetUserMsg", err) +// } +// // 转换参数 +// tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) +// // 调用函数 +// ret, _, err := proc.Call(tokenPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetGoodsTplMsg +//func (m *DLLManager) OutGetGoodsTplMsg(token, itemId, proxy string) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetGoodsTplMsg") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsTplMsg", err) +// } +// // 转换参数 +// tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) +// itemIdPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(itemId))) +// proxyPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// // 调用函数 +// ret, _, err := proc.Call(tokenPtr, itemIdPtr, proxyPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetGoodsListMsgFromSelfShop +//func (m *DLLManager) OutGetGoodsListMsgFromSelfShop(token string, proxy string, itemSn string, priceMin string, priceMax string, startCreateTime int, +// endCreateTime int, requestType string, isItemSnEqual int, page int, size int) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetGoodsListMsgFromSelfShop") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsListMsgFromSelfShop", err) +// } +// // 转换参数 +// tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) +// proxyPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// itemSnPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(itemSn))) +// priceMinPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(priceMin))) +// priceMaxPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(priceMax))) +// startCreateTimePtr := uintptr(startCreateTime) +// endCreateTimePtr := uintptr(endCreateTime) +// requestTypePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(requestType))) +// isItemSnEqualPtr := uintptr(isItemSnEqual) +// pagePtr := uintptr(page) +// sizePtr := uintptr(size) +// // 调用函数 +// ret, _, err := proc.Call(tokenPtr, proxyPtr, itemSnPtr, priceMinPtr, priceMaxPtr, startCreateTimePtr, endCreateTimePtr, requestTypePtr, isItemSnEqualPtr, pagePtr, sizePtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetImageByIsbn +//func (m *DLLManager) OutGetImageByIsbn(token string, isbn string, proxy string, isLiveImage bool, isReturnMsg bool) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetImageByIsbn") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetImageByIsbn", err) +// } +// // 转换参数 +// tokenPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(token))) +// isbnPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(isbn))) +// var proxyPtr uintptr +// if proxy != "" { +// proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// } else { +// proxyPtr = 0 +// } +// var isLiveImageInt int +// if isLiveImage { +// isLiveImageInt = 0 +// } else { +// isLiveImageInt = 1 +// } +// isLiveImagePtr := uintptr(isLiveImageInt) +// var isReturnMsgInt int +// if isReturnMsg { +// isLiveImageInt = 0 +// } else { +// isLiveImageInt = 1 +// } +// isReturnMsgPtr := uintptr(isReturnMsgInt) +// // 调用函数 +// ret, _, err := proc.Call(tokenPtr, isbnPtr, proxyPtr, isLiveImagePtr, isReturnMsgPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetGoodsMsgByDetailUrl +//func (m *DLLManager) OutGetGoodsMsgByDetailUrl(url, proxy string) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetGoodsMsgByDetailUrl") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsMsgByDetailUrl", err) +// } +// // 转换参数 +// urlPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(url))) +// var proxyPtr uintptr +// if proxy != "" { +// proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// } else { +// proxyPtr = 0 +// } +// // 调用函数 +// ret, _, err := proc.Call(urlPtr, proxyPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetGoodsListMsgByShopId +//func (m *DLLManager) OutGetGoodsListMsgByShopId(shopId int, proxy string, isImage bool, sortType string, sort string, priceMin float32, priceMax float32, pageNum, returnNum int) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetGoodsListMsgByShopId") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetGoodsListMsgByShopId", err) +// } +// // 转换参数 +// shopIdPtr := uintptr(shopId) +// var isImageInt int +// if isImage { +// isImageInt = 0 +// } else { +// isImageInt = 1 +// } +// isImagePtr := uintptr(isImageInt) +// sortTypePtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(sortType))) +// sortPtr := uintptr(unsafe.Pointer(syscall.StringBytePtr(sort))) +// priceMinPtr := uintptr(*(*uint32)(unsafe.Pointer(&priceMin))) +// priceMaxPtr := uintptr(*(*uint32)(unsafe.Pointer(&priceMax))) +// pageNumPtr := uintptr(pageNum) +// returnNumPtr := uintptr(returnNum) +// var proxyPtr uintptr +// if proxy != "" { +// proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// } else { +// proxyPtr = 0 +// } +// // 调用函数 +// ret, _, err := proc.Call(shopIdPtr, proxyPtr, isImagePtr, sortTypePtr, sortPtr, priceMinPtr, priceMaxPtr, pageNumPtr, returnNumPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 获取 OutGetTopGoodsListMsg +//func (m *DLLManager) OutGetTopGoodsListMsg(catId int, proxy string) (string, error) { +// // 获取函数 +// proc, err := m.dll.FindProc("OutGetTopGoodsListMsg") +// if err != nil { +// return "", fmt.Errorf("找不到函数 %s: %v", "OutGetTopGoodsListMsg", err) +// } +// // 转换参数 +// catIdPtr := uintptr(catId) +// var proxyPtr uintptr +// if proxy != "" { +// proxyPtr = uintptr(unsafe.Pointer(syscall.StringBytePtr(proxy))) +// } else { +// proxyPtr = 0 +// } +// // 调用函数 +// ret, _, err := proc.Call(catIdPtr, proxyPtr) +// if ret == 0 { +// return "", fmt.Errorf("DLL调用失败: %v", err) +// } +// +// // 将C字符串转换为Go字符串 +// var resultBytes []byte +// for i := 0; ; i++ { +// if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i))) == 0 { +// break +// } +// resultBytes = append(resultBytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ret)) + uintptr(i)))) +// } +// +// // 释放内存(假设DLL提供了FreeMemory函数) +// findProc, err := m.dll.FindProc("FreeString") +// if findProc != nil { +// findProc.Call(ret) +// } +// return string(resultBytes), nil +//} +// +//// 创建默认配置 +//func createDefaultConfig() Config { +// var configs Config +// +// // 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" +// +// configs.Database.Username = "newAdmin" +// configs.Database.Password = "bYPp8SbBe5F7nz2i" +// configs.Database.Host = "146.56.227.42:3306" +// configs.Database.Name = "newadmin" +// +// return configs +//} +// +//// 获取当前可执行文件所在目录 +//func getExecutableDir() string { +// exePath, err := os.Executable() +// if err != nil { +// return "." +// } +// return filepath.Dir(exePath) +//} +// +//// 格式化输出JSON响应 +//func printFormattedResponse(operation string, result string, err error) int { +// var apiResp APIResponses +// fmt.Printf("\n%s:\n", operation) +// fmt.Println("=" + string(make([]byte, 50)) + "=") +// +// if err != nil { +// fmt.Printf("❌ 错误: %v\n", err) +// } +// var count int +// if err := json.Unmarshal([]byte(result), &apiResp); err == nil { +// if apiResp.Success { +// fmt.Printf("✅ 成功: %s\n", apiResp.Message) +// if apiResp.Data != nil { +// fmt.Println("📊 数据:") +// jsonData, _ := json.MarshalIndent(apiResp.Data, "", " ") +// fmt.Println(string(jsonData)) +// // 方法1: 判断 Data 的类型并统计条数 +// switch data := apiResp.Data.(type) { +// case []interface{}: +// // 如果是切片/数组 +// count = len(data) +// fmt.Printf("📈 数据条数: %d\n", count) +// case []BookInfo: +// // 如果是 BookInfo 切片 +// count = len(data) +// fmt.Printf("📚 图书数量: %d\n", count) +// case map[string]interface{}: +// // 如果是映射 +// count = len(data) +// fmt.Printf("🗂️ 数据字段数: %d\n", count) +// case []ProductInfo: +// // 如果是 ProductInfo 切片 +// count = len(data) +// fmt.Printf("🛒 商品数量: %d\n", count) +// default: +// fmt.Printf("ℹ️ 数据类型: %T\n", apiResp.Data) +// } +// } +// if apiResp.GoodsNum != "" { +// fmt.Printf("📦 商品数量: %s\n", apiResp.GoodsNum) +// } +// if apiResp.PNum != "" { +// fmt.Printf("📄 页码: %s\n", apiResp.PNum) +// } +// } else { +// fmt.Printf("❌ 失败: %s\n", apiResp.Error) +// } +// } else { +// fmt.Printf("原始响应: %s\n", result) +// // 如果是代理地址,可以进一步处理 +// if len(result) > 0 && (result[0:4] == "http" || result[0:3] == "socks") { +// fmt.Printf("✅ 代理服务器地址获取成功\n") +// fmt.Printf("🔗 代理地址: %s\n", result) +// } +// } +// return count +//} +// +//// 专门处理代理配置响应的函数 +//func handleProxyResponse(result string, err error) { +// fmt.Printf("\n代理配置测试:\n") +// fmt.Println("=" + string(make([]byte, 50)) + "=") +// +// if err != nil { +// fmt.Printf("❌ 错误: %v\n", err) +// return +// } +// +// // 代理配置通常返回代理服务器地址,不是JSON格式 +// fmt.Printf("📡 代理服务器地址: %s\n", result) +// fmt.Printf("✅ 代理配置获取成功\n") +// +// // 如果需要,可以在这里进一步处理代理地址 +// // 例如:验证代理格式、提取IP和端口等 +// if result != "" { +// fmt.Printf("🔗 可用代理: %s\n", result) +// } +//} +// +//// // ProxyTypeManager 调用代理类型管理器 +//// +//// func (m *DLLManager) 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("FreeString") +//// if freeProc != nil { +//// freeProc.Call(r1) +//// } +//// +//// return string(resultBytes), nil +//// } +//// +//// FreeCString 释放C字符串内存 +//func (m *DLLManager) FreeCString(strPtr uintptr) error { +// proc, err := m.dll.FindProc("FreeCString") +// if err != nil { +// return fmt.Errorf("找不到函数 FreeCString: %v", err) +// } +// +// _, _, err = proc.Call(strPtr) +// if err != nil && err.Error() != "The operation completed successfully." { +// return fmt.Errorf("调用 FreeCString 失败: %v", err) +// } +// +// return nil +//} +// +//func main() { +// http.HandleFunc("/api/kfzShopBookInfo", handleGetKFZShopBookInfo) +// port := "8080" +// server := &http.Server{ +// Addr: ":" + port, +// Handler: nil, +// } +// // 启动服务器 +// if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { +// fmt.Printf("服务器启动失败: %v\n", err) +// } +//} +// +//func handleGetKFZShopBookInfo(w http.ResponseWriter, r *http.Request) { +// // 加载DLL +// manager, err := NewDLLManager("dll/kongfz.dll") +// if err != nil { +// fmt.Printf("初始化DLL管理器失败: %v", err) +// } +// //defer manager.Close() +// +// log.Println("✅ DLL加载成功") +// +// // 使用默认配置初始化 +// config := createDefaultConfig() +// initializeConfig(config) +// configJSON, err := json.Marshal(config) +// if err != nil { +// fmt.Printf("序列化配置失败: %v", err) +// } +// +// result, err := manager.Initialize(string(configJSON)) +// if err != nil { +// fmt.Printf("初始化失败: %v", err) +// } +// +// var initResp APIResponses +// if err := json.Unmarshal([]byte(result), &initResp); err != nil { +// fmt.Printf("解析初始化响应失败: %v", err) +// } +// +// if !initResp.Success { +// fmt.Printf("初始化失败: %s", initResp.Message) +// } +// +// log.Println("✅ DLL初始化成功") +// // 设置响应头 +// w.Header().Set("Content-Type", "application/json; charset=utf-8") +// +// // 只支持GET请求 +// if r.Method != http.MethodGet { +// sendErrorResponse(w, http.StatusMethodNotAllowed, "只支持GET方法") +// return +// } +// +// // 获取代理信息 +// typeManager, err := proxyTypeManager("CALF_ELEPHANT_PROXY", "1297757178467602432", "QgQBvP7f", "") +// if err != nil { +// fmt.Printf(err.Error()) +// } +// fmt.Println(typeManager) +// +// user, err := manager.OutLogin("18904056801", "Long6166@@") +// if err != nil { +// fmt.Printf(err.Error()) +// } +// fmt.Println(user) +// +// var data APIResponse +// if err := json.Unmarshal([]byte(user), &data); err != nil { +// log.Printf("[ERROR] 解析第 %d 页失败: %v", data, err) +// } +// var token string +// // 从 Data 中提取 token +// if dataMap, ok := data.Data.(map[string]interface{}); ok { +// if tk, exists := dataMap["token"]; exists { +// fmt.Println("Token:", tk) +// token = tk.(string) +// // Token: d322c80960ab6722922912dd2ce219d4b4099d54 +// } else { +// fmt.Println("Token 不存在") +// } +// } else { +// fmt.Println("Data 格式不正确") +// } +// fmt.Println(token) +// +// //msg, err := manager.OutGetUserMsg(token) +// //if err != nil { +// // fmt.Printf(err.Error()) +// //} +// //fmt.Println(msg) +// // +// //tplMsg, err := manager.OutGetGoodsTplMsg(token, "9142583516", "") +// //if err != nil { +// // fmt.Printf(err.Error()) +// //} +// //fmt.Println("tplMsg: ", tplMsg) +// // +// //shop, err := manager.OutGetGoodsListMsgFromSelfShop(token, "", "", "", "", 0, 0, "allUnSold", 0, 1, 10) +// //if err != nil { +// // fmt.Printf(err.Error()) +// //} +// //fmt.Println(shop) +// +// //// 获取cookie +// //cookie, err := loginCookie() +// //if err != nil { +// // fmt.Printf(err.Error()) +// //} +// //fmt.Println(cookie) +// +// //account, err := getRandomAccount() +// //if err != nil { +// // fmt.Printf(err.Error()) +// //} +// //fmt.Println(account) +// +// //var isbns = []string{ +// // "9787500601593", "9787506331746", "9787020106752", "9787807089353", "9787536692930", "9787530221532", "9787544270878", "9787208061644", "9787506365437", "9787513708371", "9787513922135", "9787536693968", "9787541151736", "9787544213561", "9787544754781", "9787549204304", "9787108006639", "9787531764700", "9787544253994", "9787540456023", "9787540456429", "9787544267618", "9787544277723", "9787550008496", "9787806070680", "9787806801529", "9787807530244", "9787020008728", "9787020139590", "9787204055401", "9787500680239", "9787505724778", "9787506365680", "9787530221525", "9787536097261", +// //} +// // +// //for _, isbn := range isbns { +// // // 获取孔网图片 +// // isbnss, err := outGetImageByIsbn("", isbn, "", 0, 0) +// // if err != nil { +// // fmt.Printf(err.Error()) +// // } +// // fmt.Println(isbnss) +// //} +// +// //books, num, pNum, err := outGetGoodsListMsgByShopId(3092, +// // typeManager, true, "", +// // "", 0, 14, 1, 5) +// +// //msg, err := manager.OutGetTopGoodsListMsg(0, "") +// //if err != nil { +// // +// //} +// //fmt.Println(msg) +// //fetchMode := r.URL.Query().Get("fetchMode") +// //proxyType := r.URL.Query().Get("proxyType") +// //username := r.URL.Query().Get("username") +// //password := r.URL.Query().Get("password") +// //machineCode := r.URL.Query().Get("machineCode") +// //url := r.URL.Query().Get("url") +// //shopId := r.URL.Query().Get("shopId") //https://shop.kongfz.com/793900/all/0_100_0_0_1_sort_desc_0_0/ +// //isImage := r.URL.Query().Get("isImage") +// //bookNum := r.URL.Query().Get("bookNum") +// //pageNum := r.URL.Query().Get("pageNum") +// //sortType := r.URL.Query().Get("sortType") +// //sort := r.URL.Query().Get("sort") +// //priceDown := r.URL.Query().Get("priceDown") +// //priceUp := r.URL.Query().Get("priceUp") +// +// //details, err := manager.OutGetGoodsMsgByDetailUrl("https://book.kongfz.com/151391/6531924071/", "") +// //if err != nil { +// // +// //} +// //fmt.Println(details) +// +// //id, err := manager.OutGetGoodsListMsgByShopId(1181761, +// // "", true, "", +// // "", 0, 0, 1, 5) +// //if err != nil { +// // fmt.Println(err) +// //} +// //fmt.Println(id) +// //var pa ShopBookResult +// //if err := json.Unmarshal([]byte(id), &pa); err != nil { +// // log.Printf("[ERROR] 解析第 %d 页失败: %v", pa, err) +// //} +// //for _, urls := range pa.Data.Data { +// // ss, err := proxyTypeManager("CALF_ELEPHANT_PROXY", "1297757178467602432", "QgQBvP7f", "") +// // if err != nil { +// // fmt.Printf(err.Error()) +// // } +// // fmt.Println(ss) +// // url, err := manager.OutGetGoodsMsgByDetailUrl(urls.DetailURL, ss) +// // if err != nil { +// // fmt.Println(err) +// // } +// // fmt.Println(url) +// //} +// +// books, num, pNum, err := outGetGoodsListMsgByShopId(788247, +// typeManager, 1, 0, "", +// "", 0, 0, 1, 5) +// +// fmt.Println(books) +// fmt.Println(num) +// fmt.Println(pNum) +// fmt.Println(err) +// //for _, book := range books { +// // ss, err := proxyTypeManager("TAIL_PROXY", "", "", "07f4d0fbcff99966c2b37b0c1fb7f01c") +// // if err != nil { +// // fmt.Printf(err.Error()) +// // } +// // fmt.Println(ss) +// // url, err := outGetGoodsMsgByDetailUrl(book.DetailUrl, ss) +// // if err != nil { +// // fmt.Println(err) +// // } +// // fmt.Println(url) +// //} +// +//} +// +//func manegiyoutGetGoodsListMsgFromSelfShop() { +// +//} +// +//// 发送错误响应 +//func sendErrorResponse(w http.ResponseWriter, statusCode int, message string) { +// response := APIResp{ +// Success: false, +// Message: message, +// } +// w.WriteHeader(statusCode) +// json.NewEncoder(w).Encode(response) +//} +// +//type ShopBookResult struct { +// Success bool `json:"success"` +// Message string `json:"message"` +// Data struct { +// GoodsNum string `json:"goods_num"` +// Pnum string `json:"pnum"` +// Data []struct { +// BookName string `json:"book_name"` +// Author string `json:"author"` +// Publisher string `json:"publisher"` +// ISBN string `json:"isbn"` +// PublishTime 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 int `json:"mid"` +// ItemID int64 `json:"item_id"` +// ShopID int `json:"shop_id"` +// DetailURL string `json:"detail_url"` +// } `json:"data"` +// } `json:"data"` +//}