497 lines
15 KiB
Go
497 lines
15 KiB
Go
package main
|
|
|
|
/*
|
|
#cgo linux LDFLAGS: -ldl
|
|
#cgo windows LDFLAGS: -lkernel32
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
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 <dlfcn.h>
|
|
#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)
|
|
}
|