initial commit
This commit is contained in:
parent
e32d25abd4
commit
3c0c91bf3b
6
.gitignore
vendored
6
.gitignore
vendored
@ -27,6 +27,6 @@ go.work.sum
|
|||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Editor/IDE
|
Editor/IDE
|
||||||
# .idea/
|
.idea/
|
||||||
# .vscode/
|
.vscode/
|
||||||
|
|||||||
202
bookImage.go
Normal file
202
bookImage.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全局变量
|
||||||
|
var (
|
||||||
|
// 数据库连接配置
|
||||||
|
DBUsername = "root" // 用户名
|
||||||
|
DBPassword = "Long6166@@" // 密码
|
||||||
|
DBHost = "nj-cynosdbmysql-grp-1v6vxn5f.sql.tencentcdb.com" // 主机
|
||||||
|
DBPort = 26247 // 端口
|
||||||
|
DBDataBase = "book_center" // 数据库
|
||||||
|
DBCharset = "utf8mb4" // 字符集
|
||||||
|
|
||||||
|
//连接池配置
|
||||||
|
MaxOpenConns = 20
|
||||||
|
MaxIdleConns = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// 连接数据库
|
||||||
|
func connectDB() (*sql.DB, error) {
|
||||||
|
// 数据源名称格式
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||||
|
DBUsername,
|
||||||
|
DBPassword,
|
||||||
|
DBHost,
|
||||||
|
DBPort,
|
||||||
|
DBDataBase,
|
||||||
|
DBCharset)
|
||||||
|
// 连接数据库
|
||||||
|
db, err := sql.Open("mysql", dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("打开数据库连接失败: %v", err)
|
||||||
|
}
|
||||||
|
// 设置连接池参数
|
||||||
|
db.SetMaxOpenConns(MaxOpenConns) // 最大打开连接数
|
||||||
|
db.SetMaxIdleConns(MaxIdleConns) // 最大空闲连接数
|
||||||
|
// 测试链接
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("数据库连接测试失败: %v", err)
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectBookIsbn(db *sql.DB, bookId int) error {
|
||||||
|
//// 执行语句
|
||||||
|
//query := `SELECT isbn, MAX(create_time) as max_create_time
|
||||||
|
//FROM shop_goods_rejection
|
||||||
|
//WHERE isbn IS NOT NULL
|
||||||
|
//GROUP BY isbn`
|
||||||
|
//
|
||||||
|
//exec, err := db.Exec(query)
|
||||||
|
//fmt.Println(exec, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func main() {
|
||||||
|
//db, err := connectDB()
|
||||||
|
//if err != nil {
|
||||||
|
// log.Fatal("数据库连接失败:", err)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//books, err := GetAllCategoryBooks()
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("", err)
|
||||||
|
//}
|
||||||
|
//if len(books) == 0 {
|
||||||
|
// fmt.Printf("数组是空!")
|
||||||
|
//}
|
||||||
|
//// 查询数据库中不存在的ISBN
|
||||||
|
//missingISBNs, err := findMissingISBNs(db, books)
|
||||||
|
//if err != nil {
|
||||||
|
// log.Fatal("查询失败:", err)
|
||||||
|
//}
|
||||||
|
//log.Printf("数据库中不存在的ISBN数量: %d", len(missingISBNs))
|
||||||
|
//fmt.Println("数据库中不存在的ISBN数组:")
|
||||||
|
//for i, isbn := range missingISBNs {
|
||||||
|
// fmt.Printf("%d. %s\n", i+1, isbn)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//missingISBNsString := strings.Join(missingISBNs, "\n")
|
||||||
|
//
|
||||||
|
//// 这里您可以将 missingISBNs 数组用于后续处理
|
||||||
|
//// 比如保存到文件、插入到另一个表等
|
||||||
|
//fmt.Printf("\n新的missingISBNs字符串: %v\n", missingISBNsString)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 查询数据库中不存在的ISBN
|
||||||
|
func findMissingISBNs(db *sql.DB, books []string) ([]string, error) {
|
||||||
|
if len(books) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据库中存在的ISBN
|
||||||
|
existingISBNs, err := queryExistingISBNs(db, books)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建现有ISBN的映射,用于快速查找
|
||||||
|
existingMap := make(map[string]bool)
|
||||||
|
for _, isbn := range existingISBNs {
|
||||||
|
existingMap[isbn] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新数组,存储数据库中不存在的ISBN
|
||||||
|
missingISBNs := make([]string, 0)
|
||||||
|
for _, isbn := range books {
|
||||||
|
if !existingMap[isbn] {
|
||||||
|
missingISBNs = append(missingISBNs, isbn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingISBNs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据库中存在的ISBN
|
||||||
|
func queryExistingISBNs(db *sql.DB, isbns []string) ([]string, error) {
|
||||||
|
if len(isbns) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分批处理,避免SQL语句过长
|
||||||
|
batchSize := 1000
|
||||||
|
allExistingISBNs := make([]string, 0)
|
||||||
|
|
||||||
|
for i := 0; i < len(isbns); i += batchSize {
|
||||||
|
end := i + batchSize
|
||||||
|
if end > len(isbns) {
|
||||||
|
end = len(isbns)
|
||||||
|
}
|
||||||
|
|
||||||
|
batchISBNs := isbns[i:end]
|
||||||
|
batchExisting, err := queryBatchISBNs(db, batchISBNs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allExistingISBNs = append(allExistingISBNs, batchExisting...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allExistingISBNs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询ISBN
|
||||||
|
func queryBatchISBNs(db *sql.DB, isbns []string) ([]string, error) {
|
||||||
|
if len(isbns) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
// 构建IN查询的占位符
|
||||||
|
placeholders := make([]string, len(isbns))
|
||||||
|
args := make([]interface{}, len(isbns))
|
||||||
|
for i, isbn := range isbns {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
args[i] = isbn
|
||||||
|
}
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
SELECT isbn
|
||||||
|
FROM book_center
|
||||||
|
WHERE isbn IN (%s)
|
||||||
|
AND del_flag = 0
|
||||||
|
`, joinPlaceholders(placeholders, ","))
|
||||||
|
|
||||||
|
rows, err := db.Query(query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("查询数据库失败: %v", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var existingISBNs []string
|
||||||
|
for rows.Next() {
|
||||||
|
var isbn string
|
||||||
|
if err := rows.Scan(&isbn); err != nil {
|
||||||
|
return nil, fmt.Errorf("扫描结果失败: %v", err)
|
||||||
|
}
|
||||||
|
existingISBNs = append(existingISBNs, isbn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("遍历结果失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingISBNs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// joinPlaceholders 连接占位符
|
||||||
|
func joinPlaceholders(placeholders []string, sep string) string {
|
||||||
|
if len(placeholders) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := placeholders[0]
|
||||||
|
for i := 1; i < len(placeholders); i++ {
|
||||||
|
result += sep + placeholders[i]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
299
bookNameError.go
Normal file
299
bookNameError.go
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// func main() {
|
||||||
|
// // 构建数据库连接字符串
|
||||||
|
// dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
// "root", "Long6166@@", "nj-cynosdbmysql-grp-1v6vxn5f.sql.tencentcdb.com", "26247", "book_center")
|
||||||
|
// // 连接数据库
|
||||||
|
// db, err := sql.Open("mysql", dsn)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("数据库连接失败:", err)
|
||||||
|
// }
|
||||||
|
// defer db.Close()
|
||||||
|
//
|
||||||
|
// // 测试数据库连接
|
||||||
|
// err = db.Ping()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("数据库连接测试失败:", err)
|
||||||
|
// }
|
||||||
|
// fmt.Println("数据库连接成功")
|
||||||
|
//
|
||||||
|
// // 查询包含"影印版"或"(影印版)"的书名
|
||||||
|
// books, err := queryBooksWithCopyVersion(db)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("查询失败:", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("找到 %d 本包含'影印版'或'(影印版)'的书\n", len(books))
|
||||||
|
//
|
||||||
|
// if len(books) == 0 {
|
||||||
|
// fmt.Println("没有找到需要更新的图书")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 更新书名,删除"影印版"和"(影印版)"
|
||||||
|
// updatedCount, err := updateBookNames(db, books)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("更新失败:", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("成功更新 %d 本书的书名\n", updatedCount)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Book 结构体对应 book_center 表
|
||||||
|
type Book struct {
|
||||||
|
ID int64
|
||||||
|
BookName string
|
||||||
|
ISBN string
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryBooksWithCopyVersion 查询包含"影印版"或"(影印版)"的书名
|
||||||
|
func queryBooksWithCopyVersion(db *sql.DB) ([]Book, error) {
|
||||||
|
query := `
|
||||||
|
SELECT id, book_name, isbn
|
||||||
|
FROM book_center
|
||||||
|
WHERE book_name LIKE '%影印版%'
|
||||||
|
AND del_flag = 0
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var books []Book
|
||||||
|
for rows.Next() {
|
||||||
|
var book Book
|
||||||
|
err := rows.Scan(&book.ID, &book.BookName, &book.ISBN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
books = append(books, book)
|
||||||
|
}
|
||||||
|
|
||||||
|
return books, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanBookName 清理书名,删除所有影印版相关文字
|
||||||
|
func cleanBookName(bookName string) string {
|
||||||
|
// 定义需要删除的模式
|
||||||
|
patterns := []string{
|
||||||
|
"(影印版)", // 全角括号
|
||||||
|
"(影印版)", // 半角括号
|
||||||
|
"影印版", // 无括号
|
||||||
|
"【影印版】", // 方头括号
|
||||||
|
"[影印版]", // 方括号
|
||||||
|
}
|
||||||
|
newName := bookName
|
||||||
|
|
||||||
|
// 逐个删除模式
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
newName = strings.ReplaceAll(newName, pattern, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除多余的空格
|
||||||
|
newName = strings.TrimSpace(newName)
|
||||||
|
newName = regexp.MustCompile(`\s+`).ReplaceAllString(newName, " ")
|
||||||
|
|
||||||
|
return newName
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateBookNames 更新书名,删除所有影印版相关文字
|
||||||
|
func updateBookNames(db *sql.DB, books []Book) (int, error) {
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
updateStmt, err := tx.Prepare(`
|
||||||
|
UPDATE book_center
|
||||||
|
SET book_name = ?, update_time = UNIX_TIMESTAMP()
|
||||||
|
WHERE id = ? AND isbn = ?
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer updateStmt.Close()
|
||||||
|
|
||||||
|
updatedCount := 0
|
||||||
|
for _, book := range books {
|
||||||
|
// 清理书名
|
||||||
|
newBookName := cleanBookName(book.BookName)
|
||||||
|
|
||||||
|
// 如果书名没有变化,跳过更新
|
||||||
|
if newBookName == book.BookName {
|
||||||
|
fmt.Printf("跳过: 书名无变化 (ID: %d, ISBN: %s)\n", book.ID, book.ISBN)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果书名超过字段长度限制,截断
|
||||||
|
if len(newBookName) > 400 {
|
||||||
|
newBookName = newBookName[:400]
|
||||||
|
fmt.Printf("警告: 书名超长,已截断 (ID: %d)\n", book.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := updateStmt.Exec(newBookName, book.ID, book.ISBN)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("更新失败 (ID: %d): %v\n", book.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsAffected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("获取影响行数失败 (ID: %d): %v\n", book.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rowsAffected > 0 {
|
||||||
|
updatedCount++
|
||||||
|
fmt.Printf("更新成功: ID=%d, ISBN=%s\n", book.ID, book.ISBN)
|
||||||
|
fmt.Printf(" 原书名: %s\n", book.BookName)
|
||||||
|
fmt.Printf(" 新书名: %s\n", newBookName)
|
||||||
|
fmt.Println(" ---")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没用了
|
||||||
|
//// 获取销量榜所有分类中图书的isbn
|
||||||
|
//func GetAllCategoryBooks() ([]string, error) {
|
||||||
|
// // 1. 获取分类列表
|
||||||
|
// categories, err := GetSalesBookInfo()
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("获取分类列表失败: %v", err)
|
||||||
|
// }
|
||||||
|
// // 初始化返回的ISBN数组
|
||||||
|
// var allISBNs []string
|
||||||
|
//
|
||||||
|
// for i, category := range categories {
|
||||||
|
// fmt.Printf("\n=== 正在处理分类 %d/%d: %s (ID: %d) ===\n", i+1, len(categories), category.Key, category.Value)
|
||||||
|
// // 根据分类ID获取图书详情
|
||||||
|
// bookDetails, err := GetBookDetailsByCategory(category.Value)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("获取分类 %s 的图书信息失败: %v\n", category.Key, err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // 将当前分类的图书数据添加到结果map中
|
||||||
|
// if bookDetails != nil && bookDetails.Result.Data != nil {
|
||||||
|
// for _, book := range bookDetails.Result.Data {
|
||||||
|
// if book.Isbn != "" {
|
||||||
|
// allISBNs = append(allISBNs, book.Isbn)
|
||||||
|
// fmt.Printf("添加到ISBN列表: %s\n", book.Isbn)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fmt.Printf("分类 %s 获取到 %d 本图书\n", category.Key, len(bookDetails.Result.Data))
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("分类 %s 没有获取到图书数据\n", category.Key)
|
||||||
|
// }
|
||||||
|
// time.Sleep(500 * time.Microsecond)
|
||||||
|
// }
|
||||||
|
// // 去重处理(可选)
|
||||||
|
// allISBNs = removeDuplicateISBNs(allISBNs)
|
||||||
|
// // 打印统计信息
|
||||||
|
// fmt.Printf("\n=== 总计: %d 个分类, %d 个唯一ISBN ===\n", len(categories), len(allISBNs))
|
||||||
|
// return allISBNs, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 获取销量榜的图书信息
|
||||||
|
//func GetSalesBookInfo() ([]SalesCategory, error) {
|
||||||
|
// // 构建请求URL
|
||||||
|
// url := "https://item.kongfz.com/api/Pc/getSellWellCatList"
|
||||||
|
// // 创建HTTP客户端
|
||||||
|
// client := &http.Client{
|
||||||
|
// Timeout: 30 * time.Second,
|
||||||
|
// }
|
||||||
|
// // 创建请求
|
||||||
|
// req, err := http.NewRequest("GET", url, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||||
|
// }
|
||||||
|
// // 设置请求头,模拟浏览器请求
|
||||||
|
// req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
||||||
|
// req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||||
|
// req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
|
||||||
|
// req.Header.Set("Referer", "https://item.kongfz.com/")
|
||||||
|
// // 发送请求
|
||||||
|
// resp, err := client.Do(req)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("请求失败: %v", err)
|
||||||
|
// }
|
||||||
|
// defer resp.Body.Close()
|
||||||
|
// // 检查HTTP状态码
|
||||||
|
// if resp.StatusCode != http.StatusOK {
|
||||||
|
// return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||||||
|
// }
|
||||||
|
// // 读取响应体
|
||||||
|
// body, err := io.ReadAll(resp.Body)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
// }
|
||||||
|
// // 解析JSON响应
|
||||||
|
// var salesCategoryResponse SalesCategoryResponse
|
||||||
|
// err = json.Unmarshal(body, &salesCategoryResponse)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("解析JSON失败: %v", err)
|
||||||
|
// }
|
||||||
|
// return salesCategoryResponse.Result, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 根据分类ID获取图书详情
|
||||||
|
//func GetBookDetailsByCategory(catId int) (*BookDetailResponse, error) {
|
||||||
|
// // 构建请求URL
|
||||||
|
// url := fmt.Sprintf("https://item.kongfz.com/api/pc/getSellWellListDetail?page=1&pageSize=100&timeRank=2&catId=%d", catId)
|
||||||
|
// // 创建HTTP客户端
|
||||||
|
// client := &http.Client{
|
||||||
|
// Timeout: 30 * time.Second,
|
||||||
|
// }
|
||||||
|
// // 创建请求
|
||||||
|
// req, err := http.NewRequest("GET", url, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||||
|
// }
|
||||||
|
// // 设置请求头,模拟浏览器请求
|
||||||
|
// req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
||||||
|
// req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||||
|
// req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
|
||||||
|
// req.Header.Set("Referer", "https://item.kongfz.com/")
|
||||||
|
// // 发送请求
|
||||||
|
// resp, err := client.Do(req)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("请求失败: %v", err)
|
||||||
|
// }
|
||||||
|
// defer resp.Body.Close()
|
||||||
|
// // 检查HTTP状态码
|
||||||
|
// if resp.StatusCode != http.StatusOK {
|
||||||
|
// return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
|
||||||
|
// }
|
||||||
|
// // 读取响应体
|
||||||
|
// body, err := io.ReadAll(resp.Body)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
// }
|
||||||
|
// var bookDetailResponse BookDetailResponse
|
||||||
|
// err = json.Unmarshal(body, &bookDetailResponse)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("解析JSON失败: %v", err)
|
||||||
|
// }
|
||||||
|
// if !bookDetailResponse.Status {
|
||||||
|
// return nil, fmt.Errorf("API返回错误: %s (代码: %d)", bookDetailResponse.ErrMessage, bookDetailResponse.ErrCode)
|
||||||
|
// }
|
||||||
|
// return &bookDetailResponse, nil
|
||||||
|
//}
|
||||||
59
config.ini
Normal file
59
config.ini
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# 应用配置
|
||||||
|
[app]
|
||||||
|
# 最大重试次数
|
||||||
|
max_retry_times = 3
|
||||||
|
# 间隔休眠时间
|
||||||
|
rate_limit_delay = 500ms
|
||||||
|
# 线程数
|
||||||
|
size = 5
|
||||||
|
# 默认用户代理
|
||||||
|
default_user_agent = Mozilla/5.0
|
||||||
|
|
||||||
|
# 代理配置
|
||||||
|
[proxy]
|
||||||
|
# 小象代理服务器列表(逗号分隔)
|
||||||
|
servers = http-dynamic.xiaoxiangdaili.com,http-dynamic-S02.xiaoxiangdaili.com,http-dynamic-S03.xiaoxiangdaili.com,http-dynamic-S04.xiaoxiangdaili.com
|
||||||
|
# 代理账号
|
||||||
|
username = 1297757178467602432
|
||||||
|
# 代理密码
|
||||||
|
password = QgQBvP7f
|
||||||
|
# 尾巴代理机器码
|
||||||
|
tail_machine_code = b7bf22a237ec692f13fcc2c43ee63252
|
||||||
|
# 尾巴代理卡密
|
||||||
|
tail_card_key = DL_20_YK_1920acb2129844c2aabade3896560a9b
|
||||||
|
# config配置dll
|
||||||
|
proxy_file_path = dll/proxyConfig.dll
|
||||||
|
|
||||||
|
# API接口配置
|
||||||
|
[api]
|
||||||
|
# 登录接口地址
|
||||||
|
login_url = https://login.kongfz.com/Pc/Login/account
|
||||||
|
# 图书搜索接口地址
|
||||||
|
book_search_url = https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list
|
||||||
|
# 产品搜索接口地址
|
||||||
|
product_search_url = https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list
|
||||||
|
|
||||||
|
# 服务器配置
|
||||||
|
[server]
|
||||||
|
# 服务监听端口
|
||||||
|
port = 9999
|
||||||
|
|
||||||
|
# 图片配置
|
||||||
|
[image]
|
||||||
|
# 图片保存目录
|
||||||
|
target_dir = /file/goods_img/
|
||||||
|
# 图片保存目录(本地测试路径)
|
||||||
|
; target_dir = ./images/
|
||||||
|
# 图片访问域名
|
||||||
|
domain = book.center.image.buzhiyushu.cn
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
[database]
|
||||||
|
# 数据库用户名
|
||||||
|
username = newAdmin
|
||||||
|
# 数据库密码
|
||||||
|
password = bYPp8SbBe5F7nz2i
|
||||||
|
# 数据库主机
|
||||||
|
host = 146.56.227.42:3306
|
||||||
|
# 数据库名称
|
||||||
|
name = newadmin
|
||||||
118
dll/kongfz.h
Normal file
118
dll/kongfz.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||||
|
|
||||||
|
/* package command-line-arguments */
|
||||||
|
|
||||||
|
|
||||||
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
extern size_t _GoStringLen(_GoString_ s);
|
||||||
|
extern const char *_GoStringPtr(_GoString_ s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
#line 3 "main.go"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// proxyConfig.dll 函数声明
|
||||||
|
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
|
||||||
|
extern void FreeCString(char* str);
|
||||||
|
|
||||||
|
#line 1 "cgo-generated-wrapper"
|
||||||
|
|
||||||
|
|
||||||
|
/* End of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Start of boilerplate cgo prologue. */
|
||||||
|
#line 1 "cgo-gcc-export-header-prolog"
|
||||||
|
|
||||||
|
#ifndef GO_CGO_PROLOGUE_H
|
||||||
|
#define GO_CGO_PROLOGUE_H
|
||||||
|
|
||||||
|
typedef signed char GoInt8;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
typedef short GoInt16;
|
||||||
|
typedef unsigned short GoUint16;
|
||||||
|
typedef int GoInt32;
|
||||||
|
typedef unsigned int GoUint32;
|
||||||
|
typedef long long GoInt64;
|
||||||
|
typedef unsigned long long GoUint64;
|
||||||
|
typedef GoInt64 GoInt;
|
||||||
|
typedef GoUint64 GoUint;
|
||||||
|
typedef size_t GoUintptr;
|
||||||
|
typedef float GoFloat32;
|
||||||
|
typedef double GoFloat64;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
|
||||||
|
#include <complex.h>
|
||||||
|
typedef _Fcomplex GoComplex64;
|
||||||
|
typedef _Dcomplex GoComplex128;
|
||||||
|
#else
|
||||||
|
#include <complex>
|
||||||
|
typedef std::complex<float> GoComplex64;
|
||||||
|
typedef std::complex<double> GoComplex128;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef float _Complex GoComplex64;
|
||||||
|
typedef double _Complex GoComplex128;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
static assertion to make sure the file is being used on architecture
|
||||||
|
at least with matching size of GoInt.
|
||||||
|
*/
|
||||||
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
#endif
|
||||||
|
typedef void *GoMap;
|
||||||
|
typedef void *GoChan;
|
||||||
|
typedef struct { void *t; void *v; } GoInterface;
|
||||||
|
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of boilerplate cgo prologue. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 获取商品图片(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
|
||||||
|
|
||||||
|
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
|
||||||
|
|
||||||
|
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
|
||||||
|
|
||||||
|
// 获取销量榜商品列表(带有Out的都非官放标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
|
||||||
|
extern __declspec(dllexport) char* Initialize(char* configJSON);
|
||||||
|
|
||||||
|
// 导出函数:释放C字符串内存
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
104
dll/proxyConfig.h
Normal file
104
dll/proxyConfig.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||||
|
|
||||||
|
/* package command-line-arguments */
|
||||||
|
|
||||||
|
|
||||||
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
extern size_t _GoStringLen(_GoString_ s);
|
||||||
|
extern const char *_GoStringPtr(_GoString_ s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
#line 3 "proxyConfig.go"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#line 1 "cgo-generated-wrapper"
|
||||||
|
|
||||||
|
|
||||||
|
/* End of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Start of boilerplate cgo prologue. */
|
||||||
|
#line 1 "cgo-gcc-export-header-prolog"
|
||||||
|
|
||||||
|
#ifndef GO_CGO_PROLOGUE_H
|
||||||
|
#define GO_CGO_PROLOGUE_H
|
||||||
|
|
||||||
|
typedef signed char GoInt8;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
typedef short GoInt16;
|
||||||
|
typedef unsigned short GoUint16;
|
||||||
|
typedef int GoInt32;
|
||||||
|
typedef unsigned int GoUint32;
|
||||||
|
typedef long long GoInt64;
|
||||||
|
typedef unsigned long long GoUint64;
|
||||||
|
typedef GoInt64 GoInt;
|
||||||
|
typedef GoUint64 GoUint;
|
||||||
|
typedef size_t GoUintptr;
|
||||||
|
typedef float GoFloat32;
|
||||||
|
typedef double GoFloat64;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
|
||||||
|
#include <complex.h>
|
||||||
|
typedef _Fcomplex GoComplex64;
|
||||||
|
typedef _Dcomplex GoComplex128;
|
||||||
|
#else
|
||||||
|
#include <complex>
|
||||||
|
typedef std::complex<float> GoComplex64;
|
||||||
|
typedef std::complex<double> GoComplex128;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef float _Complex GoComplex64;
|
||||||
|
typedef double _Complex GoComplex128;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
static assertion to make sure the file is being used on architecture
|
||||||
|
at least with matching size of GoInt.
|
||||||
|
*/
|
||||||
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
#endif
|
||||||
|
typedef void *GoMap;
|
||||||
|
typedef void *GoChan;
|
||||||
|
typedef struct { void *t; void *v; } GoInterface;
|
||||||
|
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of boilerplate cgo prologue. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 导出函数:获取代理健康状态(用于调试)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* GetProxyHealth(void);
|
||||||
|
|
||||||
|
// 导出函数:代理类型管理器
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
|
||||||
|
|
||||||
|
// 导出函数:释放C字符串内存
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
120
dll/test.h
Normal file
120
dll/test.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||||
|
|
||||||
|
/* package command-line-arguments */
|
||||||
|
|
||||||
|
|
||||||
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
extern size_t _GoStringLen(_GoString_ s);
|
||||||
|
extern const char *_GoStringPtr(_GoString_ s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
#line 3 "kfztp.go"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// proxyConfig.dll 函数声明
|
||||||
|
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
|
||||||
|
extern void FreeCString(char* str);
|
||||||
|
|
||||||
|
#line 1 "cgo-generated-wrapper"
|
||||||
|
|
||||||
|
|
||||||
|
/* End of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Start of boilerplate cgo prologue. */
|
||||||
|
#line 1 "cgo-gcc-export-header-prolog"
|
||||||
|
|
||||||
|
#ifndef GO_CGO_PROLOGUE_H
|
||||||
|
#define GO_CGO_PROLOGUE_H
|
||||||
|
|
||||||
|
typedef signed char GoInt8;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
typedef short GoInt16;
|
||||||
|
typedef unsigned short GoUint16;
|
||||||
|
typedef int GoInt32;
|
||||||
|
typedef unsigned int GoUint32;
|
||||||
|
typedef long long GoInt64;
|
||||||
|
typedef unsigned long long GoUint64;
|
||||||
|
typedef GoInt64 GoInt;
|
||||||
|
typedef GoUint64 GoUint;
|
||||||
|
typedef size_t GoUintptr;
|
||||||
|
typedef float GoFloat32;
|
||||||
|
typedef double GoFloat64;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
|
||||||
|
#include <complex.h>
|
||||||
|
typedef _Fcomplex GoComplex64;
|
||||||
|
typedef _Dcomplex GoComplex128;
|
||||||
|
#else
|
||||||
|
#include <complex>
|
||||||
|
typedef std::complex<float> GoComplex64;
|
||||||
|
typedef std::complex<double> GoComplex128;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef float _Complex GoComplex64;
|
||||||
|
typedef double _Complex GoComplex128;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
static assertion to make sure the file is being used on architecture
|
||||||
|
at least with matching size of GoInt.
|
||||||
|
*/
|
||||||
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
#endif
|
||||||
|
typedef void *GoMap;
|
||||||
|
typedef void *GoChan;
|
||||||
|
typedef struct { void *t; void *v; } GoInterface;
|
||||||
|
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of boilerplate cgo prologue. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 获取商品图片(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetImageByIsbn(char* isbn, char* proxy, _Bool isLiveImage, _Bool isReturnMsg);
|
||||||
|
|
||||||
|
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, _Bool isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
|
||||||
|
|
||||||
|
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
|
||||||
|
|
||||||
|
// 获取销量榜商品列表(带有Out的都非官放标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
|
||||||
|
extern __declspec(dllexport) char* GetKFZSPTImageURL(char* proxyType, char* username, char* password, char* machineCode, char* isbn);
|
||||||
|
extern __declspec(dllexport) char* GetKFZGTImageURL(char* proxyType, char* username, char* password, char* machineCode, char* isbn);
|
||||||
|
extern __declspec(dllexport) char* Initialize(char* configJSON);
|
||||||
|
|
||||||
|
// 导出函数:释放C字符串内存
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
BIN
dll/xkongfz.dll~
Normal file
BIN
dll/xkongfz.dll~
Normal file
Binary file not shown.
122
dll/xkongfz.h
Normal file
122
dll/xkongfz.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||||
|
|
||||||
|
/* package command-line-arguments */
|
||||||
|
|
||||||
|
|
||||||
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
extern size_t _GoStringLen(_GoString_ s);
|
||||||
|
extern const char *_GoStringPtr(_GoString_ s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
#line 3 "main.go"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// proxyConfig.dll 函数声明
|
||||||
|
extern char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
|
||||||
|
extern void FreeCString(char* str);
|
||||||
|
|
||||||
|
#line 1 "cgo-generated-wrapper"
|
||||||
|
|
||||||
|
|
||||||
|
/* End of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Start of boilerplate cgo prologue. */
|
||||||
|
#line 1 "cgo-gcc-export-header-prolog"
|
||||||
|
|
||||||
|
#ifndef GO_CGO_PROLOGUE_H
|
||||||
|
#define GO_CGO_PROLOGUE_H
|
||||||
|
|
||||||
|
typedef signed char GoInt8;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
typedef short GoInt16;
|
||||||
|
typedef unsigned short GoUint16;
|
||||||
|
typedef int GoInt32;
|
||||||
|
typedef unsigned int GoUint32;
|
||||||
|
typedef long long GoInt64;
|
||||||
|
typedef unsigned long long GoUint64;
|
||||||
|
typedef GoInt64 GoInt;
|
||||||
|
typedef GoUint64 GoUint;
|
||||||
|
typedef size_t GoUintptr;
|
||||||
|
typedef float GoFloat32;
|
||||||
|
typedef double GoFloat64;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
|
||||||
|
#include <complex.h>
|
||||||
|
typedef _Fcomplex GoComplex64;
|
||||||
|
typedef _Dcomplex GoComplex128;
|
||||||
|
#else
|
||||||
|
#include <complex>
|
||||||
|
typedef std::complex<float> GoComplex64;
|
||||||
|
typedef std::complex<double> GoComplex128;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef float _Complex GoComplex64;
|
||||||
|
typedef double _Complex GoComplex128;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
static assertion to make sure the file is being used on architecture
|
||||||
|
at least with matching size of GoInt.
|
||||||
|
*/
|
||||||
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
#endif
|
||||||
|
typedef void *GoMap;
|
||||||
|
typedef void *GoChan;
|
||||||
|
typedef struct { void *t; void *v; } GoInterface;
|
||||||
|
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of boilerplate cgo prologue. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 登录(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutLogin(char* username, char* password);
|
||||||
|
|
||||||
|
// 获取商品图片(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
|
||||||
|
|
||||||
|
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
|
||||||
|
|
||||||
|
// 获取商品信息通过商品详情链接(带有0ut的都非官方标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
|
||||||
|
|
||||||
|
// 获取销量榜商品列表(带有Out的都非官放标准接口)
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
|
||||||
|
extern __declspec(dllexport) char* Initialize(char* configJSON);
|
||||||
|
|
||||||
|
// 导出函数:释放C字符串内存
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
29
go.mod
Normal file
29
go.mod
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
module kfzgw-info
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/PuerkitoBio/goquery v1.10.3
|
||||||
|
github.com/chromedp/chromedp v0.14.2
|
||||||
|
github.com/go-ini/ini v1.67.0
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
|
github.com/parnurzeal/gorequest v0.3.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
|
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 // indirect
|
||||||
|
github.com/chromedp/sysutil v1.1.0 // indirect
|
||||||
|
github.com/elazarl/goproxy v1.7.2 // indirect
|
||||||
|
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
|
||||||
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
|
github.com/gobwas/ws v1.4.0 // indirect
|
||||||
|
github.com/moul/http2curl v1.0.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/smartystreets/goconvey v1.8.1 // indirect
|
||||||
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
|
golang.org/x/net v0.39.0 // indirect
|
||||||
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
|
)
|
||||||
124
go.sum
Normal file
124
go.sum
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||||
|
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||||
|
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||||
|
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||||
|
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E=
|
||||||
|
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
|
||||||
|
github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM=
|
||||||
|
github.com/chromedp/chromedp v0.14.2/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo=
|
||||||
|
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
|
||||||
|
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
|
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||||
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs=
|
||||||
|
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
|
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||||
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
|
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
|
||||||
|
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||||
|
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||||
|
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||||
|
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||||
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||||
|
github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
|
||||||
|
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
|
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||||
|
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||||
|
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
|
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
378
iniConfigUtil.go
Normal file
378
iniConfigUtil.go
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
// Package utils 提供配置文件加载工具,支持从INI文件加载配置到结构体
|
||||||
|
// 支持类型: int, string, bool, float, time.Duration 及其切片类型
|
||||||
|
// 特性:
|
||||||
|
// - 支持默认值标签 `default`
|
||||||
|
// - 支持INI路径标签 `ini:"section.key"`
|
||||||
|
// - 支持时间单位转换标签 `multiplier`
|
||||||
|
// - 自动处理切片类型(逗号分隔)
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ini/ini"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadConfig 从INI文件加载配置到结构体
|
||||||
|
// 参数:
|
||||||
|
//
|
||||||
|
// configPtr: 指向配置结构体的指针(必须是结构体指针)
|
||||||
|
// filename: INI配置文件的路径
|
||||||
|
//
|
||||||
|
// 返回:
|
||||||
|
//
|
||||||
|
// error: 加载成功返回nil,失败返回ConfigError详细错误信息
|
||||||
|
//
|
||||||
|
// 用法说明:
|
||||||
|
// 1. 定义配置结构体,使用标签声明INI映射关系和默认值
|
||||||
|
// 2. 调用LoadConfig(&config, "config.ini")
|
||||||
|
// 3. 检查错误并应用配置
|
||||||
|
//
|
||||||
|
// 示例结构体:
|
||||||
|
//
|
||||||
|
// type AppConfig struct {
|
||||||
|
// Port int `ini:"server.port" default:"8080"`
|
||||||
|
// Timeout time.Duration `ini:"server.timeout" multiplier:"1s"`
|
||||||
|
// Features []string `ini:"server.features"`
|
||||||
|
// }
|
||||||
|
func LoadConfig(configPtr interface{}, filename string) error {
|
||||||
|
// 验证输入必须是指针
|
||||||
|
if reflect.TypeOf(configPtr).Kind() != reflect.Ptr {
|
||||||
|
return &ConfigError{Message: "configPtr必须是指向结构体的指针"} // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证输入必须指向结构体
|
||||||
|
configValue := reflect.ValueOf(configPtr).Elem()
|
||||||
|
// 确保输入是结构体
|
||||||
|
if configValue.Kind() != reflect.Struct {
|
||||||
|
return &ConfigError{Message: "configPtr必须指向结构体"} // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置默认值(如果结构体有默认值)
|
||||||
|
setDefaultValues(configValue)
|
||||||
|
|
||||||
|
// 检查配置文件是否存在
|
||||||
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
|
log.Printf("配置文件不存在: %s, 使用默认值", filename) // 打印信息
|
||||||
|
return nil // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载INI文件
|
||||||
|
iniCfg, err := ini.Load(filename)
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return &ConfigError{Message: "加载配置文件失败", Cause: err} // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射配置到结构体
|
||||||
|
if err := mapConfig(iniCfg, configValue); err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理特殊类型(如time.Duration)
|
||||||
|
processSpecialTypes(configValue)
|
||||||
|
|
||||||
|
// 返回成功
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaultValues 设置结构体字段的默认值
|
||||||
|
// 遍历结构体字段,检测`default`标签并设置初始值
|
||||||
|
// setDefaultValues 设置结构体字段的默认值
|
||||||
|
func setDefaultValues(configValue reflect.Value) {
|
||||||
|
// 获取结构体类型
|
||||||
|
configType := configValue.Type()
|
||||||
|
|
||||||
|
// 遍历字段
|
||||||
|
for i := 0; i < configType.NumField(); i++ {
|
||||||
|
// 获取字段信息
|
||||||
|
field := configType.Field(i)
|
||||||
|
// 获取字段值
|
||||||
|
fieldValue := configValue.Field(i)
|
||||||
|
|
||||||
|
// 如果字段是结构体,递归处理
|
||||||
|
if fieldValue.Kind() == reflect.Struct {
|
||||||
|
setDefaultValues(fieldValue)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字段已经设置了值,跳过
|
||||||
|
if !fieldValue.IsZero() {
|
||||||
|
continue // 跳过
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有默认值标签
|
||||||
|
if defaultValue, ok := field.Tag.Lookup("default"); ok {
|
||||||
|
setValueFromString(fieldValue, defaultValue) // 设置字段值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapConfig 将INI配置映射到结构体字段
|
||||||
|
// 解析`ini`标签获取section和key,读取对应配置值
|
||||||
|
func mapConfig(iniCfg *ini.File, configValue reflect.Value) error {
|
||||||
|
// 获取结构体类型
|
||||||
|
configType := configValue.Type()
|
||||||
|
|
||||||
|
// 遍历字段
|
||||||
|
for i := 0; i < configType.NumField(); i++ {
|
||||||
|
field := configType.Field(i) // 获取字段信息
|
||||||
|
fieldValue := configValue.Field(i) // 获取字段值
|
||||||
|
|
||||||
|
// 如果字段是结构体,递归处理
|
||||||
|
if fieldValue.Kind() == reflect.Struct {
|
||||||
|
if err := mapConfig(iniCfg, fieldValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取INI标签
|
||||||
|
iniTag, ok := field.Tag.Lookup("ini")
|
||||||
|
// 如果没有INI标签,跳过这个字段
|
||||||
|
if !ok || iniTag == "" {
|
||||||
|
continue // 跳过
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析INI键名(支持section.key格式)
|
||||||
|
sectionName, keyName := parseIniTag(iniTag)
|
||||||
|
|
||||||
|
// 获取INI值
|
||||||
|
section, err := iniCfg.GetSection(sectionName)
|
||||||
|
// 如果section不存在
|
||||||
|
if err != nil {
|
||||||
|
continue // 跳过这个字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取INI键值
|
||||||
|
key, err := section.GetKey(keyName)
|
||||||
|
// 如果key不存在
|
||||||
|
if err != nil {
|
||||||
|
continue //跳过这个字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置结构体字段值
|
||||||
|
if err := setFieldValue(fieldValue, key); err != nil {
|
||||||
|
// 返回错误
|
||||||
|
return &ConfigError{
|
||||||
|
Message: "设置字段值失败", // 返回错误信息
|
||||||
|
Cause: err, // 返回错误原因
|
||||||
|
Field: field.Name, // 返回字段名称
|
||||||
|
Tag: iniTag, // 返回标签
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIniTag 解析INI标签格式
|
||||||
|
// 输入: "section.key" 格式的字符串
|
||||||
|
// 返回: (section名称, key名称)
|
||||||
|
func parseIniTag(tag string) (section, key string) {
|
||||||
|
// 默认section
|
||||||
|
section = "DEFAULT"
|
||||||
|
// 默认key
|
||||||
|
key = tag
|
||||||
|
|
||||||
|
// 检查是否有section前缀
|
||||||
|
if parts := regexp.MustCompile(`^(\w+)\.(\w+)$`).FindStringSubmatch(tag); len(parts) == 3 {
|
||||||
|
section = parts[1] // 设置section
|
||||||
|
key = parts[2] // 设置key
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回section和key
|
||||||
|
return section, key
|
||||||
|
}
|
||||||
|
|
||||||
|
// setFieldValue 根据INI键值设置结构体字段值
|
||||||
|
// 自动处理基础类型和time.Duration类型转换
|
||||||
|
func setFieldValue(fieldValue reflect.Value, key *ini.Key) error {
|
||||||
|
// 检查字段类型
|
||||||
|
switch fieldValue.Kind() {
|
||||||
|
case reflect.String: // 字符串类型
|
||||||
|
fieldValue.SetString(key.String()) // 设置字段值
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
|
||||||
|
// 特殊处理time.Duration类型
|
||||||
|
if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
|
||||||
|
duration, err := time.ParseDuration(key.String()) // 解析字符串为time.Duration
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
fieldValue.SetInt(int64(duration)) // 设置字段值
|
||||||
|
} else {
|
||||||
|
intValue, err := key.Int() // 获取整数值
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
fieldValue.SetInt(int64(intValue)) // 设置字段值
|
||||||
|
}
|
||||||
|
case reflect.Bool: // 布尔类型
|
||||||
|
boolValue, err := key.Bool() // 获取布尔值
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
fieldValue.SetBool(boolValue) // 设置字段值
|
||||||
|
case reflect.Float32, reflect.Float64: // 浮点类型
|
||||||
|
floatValue, err := key.Float64() // 获取浮点值
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
fieldValue.SetFloat(floatValue) // 设置字段值
|
||||||
|
case reflect.Slice: // 切片类型
|
||||||
|
return setSliceValue(fieldValue, key) // 处理切片类型字段
|
||||||
|
default: // 其他类型
|
||||||
|
return &ConfigError{Message: "不支持的字段类型", FieldType: fieldValue.Type().String()} // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回成功
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSliceValue 设置切片类型字段值
|
||||||
|
// 将逗号分隔的字符串解析为指定类型的切片
|
||||||
|
func setSliceValue(fieldValue reflect.Value, key *ini.Key) error {
|
||||||
|
// 获取切片元素类型
|
||||||
|
sliceType := fieldValue.Type().Elem()
|
||||||
|
// 获取逗号分隔的值
|
||||||
|
values := key.Strings(",")
|
||||||
|
|
||||||
|
// 创建新切片
|
||||||
|
slice := reflect.MakeSlice(fieldValue.Type(), len(values), len(values))
|
||||||
|
|
||||||
|
// 遍历值
|
||||||
|
for i, val := range values {
|
||||||
|
elemValue := reflect.New(sliceType).Elem() // 创建新元素
|
||||||
|
|
||||||
|
// 根据切片元素类型设置值
|
||||||
|
switch sliceType.Kind() {
|
||||||
|
case reflect.String: // 字符串类型
|
||||||
|
elemValue.SetString(val) // 设置字段值
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
|
||||||
|
intVal, err := strconv.ParseInt(val, 10, 64) // 解析字符串为整数
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
elemValue.SetInt(intVal) // 设置字段值
|
||||||
|
case reflect.Float32, reflect.Float64: // 浮点类型
|
||||||
|
floatVal, err := strconv.ParseFloat(val, 64) // 解析字符串为浮点数
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
elemValue.SetFloat(floatVal) // 设置字段值
|
||||||
|
case reflect.Bool: // 布尔类型
|
||||||
|
boolVal, err := strconv.ParseBool(val) // 解析字符串为布尔值
|
||||||
|
// 处理错误
|
||||||
|
if err != nil {
|
||||||
|
return err // 返回错误
|
||||||
|
}
|
||||||
|
elemValue.SetBool(boolVal) // 设置字段值
|
||||||
|
default:
|
||||||
|
return &ConfigError{Message: "不支持的切片元素类型", FieldType: sliceType.String()} // 返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.Index(i).Set(elemValue) // 设置切片元素
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置切片字段值
|
||||||
|
fieldValue.Set(slice)
|
||||||
|
// 返回成功
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setValueFromString 从字符串解析值到结构体字段(用于默认值)
|
||||||
|
// 支持基础类型转换,不处理复杂类型
|
||||||
|
func setValueFromString(fieldValue reflect.Value, valueStr string) {
|
||||||
|
switch fieldValue.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
fieldValue.SetString(valueStr)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if intValue, err := strconv.ParseInt(valueStr, 10, 64); err == nil {
|
||||||
|
fieldValue.SetInt(intValue)
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
if boolValue, err := strconv.ParseBool(valueStr); err == nil {
|
||||||
|
fieldValue.SetBool(boolValue)
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
if floatValue, err := strconv.ParseFloat(valueStr, 64); err == nil {
|
||||||
|
fieldValue.SetFloat(floatValue)
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// 切片类型需要特殊处理,这里简化处理
|
||||||
|
default:
|
||||||
|
// 保留原panic调用,但修改提示信息
|
||||||
|
panic("未处理的默认值类型")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processSpecialTypes 处理特殊类型转换
|
||||||
|
// 当前支持time.Duration的倍数转换(使用multiplier标签)
|
||||||
|
// processSpecialTypes 处理特殊类型转换
|
||||||
|
func processSpecialTypes(configValue reflect.Value) {
|
||||||
|
// 获取结构体类型
|
||||||
|
configType := configValue.Type()
|
||||||
|
|
||||||
|
// 遍历结构体字段
|
||||||
|
for i := 0; i < configType.NumField(); i++ {
|
||||||
|
field := configType.Field(i) // 获取字段
|
||||||
|
fieldValue := configValue.Field(i) // 获取字段值
|
||||||
|
|
||||||
|
// 如果字段是结构体,递归处理
|
||||||
|
if fieldValue.Kind() == reflect.Struct {
|
||||||
|
processSpecialTypes(fieldValue)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理time.Duration类型的倍数转换
|
||||||
|
if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
|
||||||
|
// 检查是否存在multiplier标签
|
||||||
|
if multiplier, ok := field.Tag.Lookup("multiplier"); ok {
|
||||||
|
// 解析multiplier标签
|
||||||
|
if mult, err := time.ParseDuration(multiplier); err == nil {
|
||||||
|
duration := time.Duration(fieldValue.Int()) // 将字段值转换为time.Duration
|
||||||
|
fieldValue.SetInt(int64(duration * mult)) // 将字段值设置为转换后的时间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigError 自定义配置错误类型
|
||||||
|
// 包含错误原因、字段信息和原始错误
|
||||||
|
type ConfigError struct {
|
||||||
|
Message string
|
||||||
|
Cause error
|
||||||
|
Field string
|
||||||
|
FieldType string
|
||||||
|
Tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 实现error接口,提供详细错误信息
|
||||||
|
func (e *ConfigError) Error() string {
|
||||||
|
msg := "配置错误: " + e.Message
|
||||||
|
if e.Field != "" {
|
||||||
|
msg += " [字段: " + e.Field + "]"
|
||||||
|
}
|
||||||
|
if e.FieldType != "" {
|
||||||
|
msg += " [类型: " + e.FieldType + "]"
|
||||||
|
}
|
||||||
|
if e.Tag != "" {
|
||||||
|
msg += " [标签: " + e.Tag + "]"
|
||||||
|
}
|
||||||
|
if e.Cause != nil {
|
||||||
|
msg += " - " + e.Cause.Error()
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
736
md/main.md
Normal file
736
md/main.md
Normal file
@ -0,0 +1,736 @@
|
|||||||
|
# 针对kongfz.dll文件使用教程
|
||||||
|
## 1.需要先创建dll工具实例
|
||||||
|
|
||||||
|
### 1. 加载DLL,用于调用dll文件,代码示例:
|
||||||
|
```go
|
||||||
|
func main(){
|
||||||
|
// 加载DLL
|
||||||
|
manager, err := NewDLLManager("main.dll")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("初始化DLL管理器失败: %v", err)
|
||||||
|
}
|
||||||
|
// 确保字段被释放,但是需要注意,如果加这个,可能把你的dll文件释放掉了,导致程序崩溃
|
||||||
|
defer manager.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type DLLManager struct {
|
||||||
|
dll *syscall.DLL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDLLManager(dllPath string) (*DLLManager, error) {
|
||||||
|
dll, err := syscall.LoadDLL(dllPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("加载DLL失败: %v", err)
|
||||||
|
}
|
||||||
|
return &DLLManager{dll: dll}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DLLManager) Close() {
|
||||||
|
if m.dll != nil {
|
||||||
|
m.dll.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *DLLManager) Initialize(configJSON string) (string, error) {
|
||||||
|
return m.callFunction("Initialize", configJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后的 GetKFZGTImageURL - 需要5个参数
|
||||||
|
func (m *DLLManager) GetKFZGTImageURL(proxyType, username, password, machineCode, isbn string) (string, error) {
|
||||||
|
return m.callFunctionFiveArgs("GetKFZGTImageURL",
|
||||||
|
proxyType, username, password, machineCode, isbn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后的 GetKFZSPTImageURL - 需要5个参数
|
||||||
|
func (m *DLLManager) GetKFZSPTImageURL(proxyType, username, password, machineCode, isbn string) (string, error) {
|
||||||
|
return m.callFunctionFiveArgs("GetKFZSPTImageURL",
|
||||||
|
proxyType, username, password, machineCode, isbn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后的 GetKFZShopBookInfo - 需要12个参数
|
||||||
|
func (m *DLLManager) GetKFZShopBookInfo(proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp string) (string, error) {
|
||||||
|
return m.callFunctionTwelveArgs("GetKFZShopBookInfo",
|
||||||
|
proxyType, username, password, machineCode, shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.初始化配置信息,代码示例:
|
||||||
|
````go
|
||||||
|
func main(){
|
||||||
|
// 使用默认配置初始化
|
||||||
|
config := createDefaultConfig()
|
||||||
|
configJSON, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("序列化配置失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := manager.Initialize(string(configJSON))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var initResp APIResponses
|
||||||
|
if err := json.Unmarshal([]byte(result), &initResp); err != nil {
|
||||||
|
log.Fatalf("解析初始化响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !initResp.Success {
|
||||||
|
log.Fatalf("初始化失败: %s", initResp.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建默认配置示例
|
||||||
|
func createDefaultConfig() Configs {
|
||||||
|
var configs Configs
|
||||||
|
|
||||||
|
// App配置
|
||||||
|
configs.App.MaxRetryTimes = 3
|
||||||
|
configs.App.RateLimitDelay = 1000
|
||||||
|
configs.App.Size = 10
|
||||||
|
configs.App.DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
|
|
||||||
|
// API配置
|
||||||
|
configs.API.LoginURL = "https://login.kongfz.com/Pc/Login/account"
|
||||||
|
configs.API.BookSearchURL = "https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list"
|
||||||
|
configs.API.ProductSearchURL = "https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list"
|
||||||
|
|
||||||
|
// 代理配置(需要根据实际情况修改)
|
||||||
|
configs.Proxy.Servers = "http-dynamic.xiaoxiangdaili.com,http-dynamic-S02.xiaoxiangdaili.com,http-dynamic-S03.xiaoxiangdaili.com,http-dynamic-S04.xiaoxiangdaili.com"
|
||||||
|
configs.Proxy.Username = "1297757178467602432"
|
||||||
|
configs.Proxy.Password = "QgQBvP7f"
|
||||||
|
configs.Proxy.TailMachineCode = "b7bf22a237ec692f13fcc2c43ee63252"
|
||||||
|
configs.Proxy.TailCardKey = "DL_20_YK_1920acb2129844c2aabade3896560a9b"
|
||||||
|
configs.Proxy.ProxyFilePath = "dll/proxyConfig.dll"
|
||||||
|
|
||||||
|
return configs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置结构
|
||||||
|
type Configs struct {
|
||||||
|
App struct {
|
||||||
|
MaxRetryTimes int `json:"max_retry_times"`
|
||||||
|
RateLimitDelay int `json:"rate_limit_delay"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
DefaultUserAgent string `json:"default_user_agent"`
|
||||||
|
} `json:"app"`
|
||||||
|
|
||||||
|
API struct {
|
||||||
|
LoginURL string `json:"login_url"`
|
||||||
|
BookSearchURL string `json:"book_search_url"`
|
||||||
|
ProductSearchURL string `json:"product_search_url"`
|
||||||
|
} `json:"api"`
|
||||||
|
|
||||||
|
Proxy struct {
|
||||||
|
Servers string `json:"servers"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
TailMachineCode string `json:"tail_machine_code"`
|
||||||
|
TailCardKey string `json:"tail_card_key"`
|
||||||
|
ProxyFilePath string `json:"proxy_file_path"`
|
||||||
|
} `json:"proxy"`
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
|
### 直接调用接口,代码示例:入参、返回参数都在下面
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
// 调用方法示例
|
||||||
|
shopResult, err := manager.GetKFZShopBookInfo(
|
||||||
|
proxyType, username, password, machineCode,
|
||||||
|
shopId, isImage, bookNum, pageNum, sortType, sort, priceDown, priceUp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## 接口详情
|
||||||
|
|
||||||
|
### 1. 获取孔网官图
|
||||||
|
|
||||||
|
**请求信息**
|
||||||
|
|
||||||
|
gtResult, err := manager.GetKFZGTImageURL(testCase.isbn)
|
||||||
|
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|------|--------|----|-----------|
|
||||||
|
| proxyType | string | 是 | 代理类型(小象代理:CALF_ELEPHANT_PROXY,内置代理:TAIL_PROXY) |
|
||||||
|
| username | string | 是 | 小象代理账号 |
|
||||||
|
| password | string | 是 | 小象代理密码 |
|
||||||
|
| machineCode | string | 是 | 内置代理机械码 |
|
||||||
|
| isbn | string | 是 | 图书的 ISBN 号 |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 | 示例值 |
|
||||||
|
|--------|------|------|---------|
|
||||||
|
| `success` | boolean | 请求是否成功 | `true` |
|
||||||
|
| `message` | string | 响应消息 | `"查询成功"` |
|
||||||
|
| `data` | array | 图书信息列表 | - |
|
||||||
|
|
||||||
|
**图书信息字段说明(data数据)**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `author` | string | 作者 |
|
||||||
|
| `binding_layout` | string | 装帧布局 |
|
||||||
|
| `book_name` | string | 图书名称 |
|
||||||
|
| `book_pic` | string | 图书图片(大图) |
|
||||||
|
| `book_pic_s` | string | 图书图片(小图) |
|
||||||
|
| `buyCount` | string | 购买数量 |
|
||||||
|
| `category` | string | 分类 |
|
||||||
|
| `condition` | string | 图书状况 |
|
||||||
|
| `content` | string | 内容简介 |
|
||||||
|
| `edition` | string | 版次 |
|
||||||
|
| `editor` | string | 编者 |
|
||||||
|
| `express_delivery_fee` | string | 快递费用 |
|
||||||
|
| `fix_price` | string | 定价 |
|
||||||
|
| `format` | string | 开本 |
|
||||||
|
| `isbn` | string | ISBN号 |
|
||||||
|
| `item_id` | number | 商品ID |
|
||||||
|
| `languages` | string | 语言 |
|
||||||
|
| `mid` | number | 商家ID |
|
||||||
|
| `pages` | string | 页数 |
|
||||||
|
| `paper` | string | 纸张 |
|
||||||
|
| `print_time` | string | 印刷时间 |
|
||||||
|
| `publication_time` | number | 出版时间(时间戳) |
|
||||||
|
| `publisher` | string | 出版社 |
|
||||||
|
| `sellCount` | string | 销售数量 |
|
||||||
|
| `shop_id` | number | 店铺ID |
|
||||||
|
| `wordage` | string | 字数 |
|
||||||
|
|
||||||
|
**响应示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": {
|
||||||
|
"author": "",
|
||||||
|
"binding_layout": "",
|
||||||
|
"book_name": "",
|
||||||
|
"book_pic": "",
|
||||||
|
"book_pic_s": "",
|
||||||
|
"buyCount": "",
|
||||||
|
"category": "",
|
||||||
|
"condition": "",
|
||||||
|
"content": "",
|
||||||
|
"edition": "",
|
||||||
|
"editor": "",
|
||||||
|
"express_delivery_fee": "",
|
||||||
|
"fix_price": "",
|
||||||
|
"format": "",
|
||||||
|
"isbn": "",
|
||||||
|
"item_id": 0,
|
||||||
|
"languages": "",
|
||||||
|
"mid": 0,
|
||||||
|
"pages": "",
|
||||||
|
"paper": "",
|
||||||
|
"print_time": "",
|
||||||
|
"publication_time": 0,
|
||||||
|
"publisher": "",
|
||||||
|
"sellCount": "",
|
||||||
|
"shop_id": 0,
|
||||||
|
"wordage": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 获取孔网实拍图
|
||||||
|
|
||||||
|
**请求信息**
|
||||||
|
|
||||||
|
sptResult, err := manager.GetKFZSPTImageURL(testCase.isbn)
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|------|--------|----|------------|
|
||||||
|
| proxyType | string | 是 | 代理类型(小象代理:CALF_ELEPHANT_PROXY,内置代理:TAIL_PROXY) |
|
||||||
|
| username | string | 是 | 小象代理账号 |
|
||||||
|
| password | string | 是 | 小象代理密码 |
|
||||||
|
| machineCode | string | 是 | 内置代理机械码 |
|
||||||
|
| isbn | string | 是 | 图书的 ISBN 号 |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 | 示例值 |
|
||||||
|
|--------|------|------|---------|
|
||||||
|
| `success` | boolean | 请求是否成功 | `true` |
|
||||||
|
| `message` | string | 响应消息 | `"查询成功"` |
|
||||||
|
| `data` | array | 图书信息列表 | - |
|
||||||
|
|
||||||
|
**图书信息字段说明(data数据)**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `author` | string | 作者 |
|
||||||
|
| `binding_layout` | string | 装帧布局 |
|
||||||
|
| `book_name` | string | 图书名称 |
|
||||||
|
| `book_pic` | string | 图书图片(大图) |
|
||||||
|
| `book_pic_s` | string | 图书图片(小图) |
|
||||||
|
| `buyCount` | string | 购买数量 |
|
||||||
|
| `category` | string | 分类 |
|
||||||
|
| `condition` | string | 图书状况 |
|
||||||
|
| `content` | string | 内容简介 |
|
||||||
|
| `edition` | string | 版次 |
|
||||||
|
| `editor` | string | 编者 |
|
||||||
|
| `express_delivery_fee` | string | 快递费用 |
|
||||||
|
| `fix_price` | string | 定价 |
|
||||||
|
| `format` | string | 开本 |
|
||||||
|
| `isbn` | string | ISBN号 |
|
||||||
|
| `item_id` | number | 商品ID |
|
||||||
|
| `languages` | string | 语言 |
|
||||||
|
| `mid` | number | 商家ID |
|
||||||
|
| `pages` | string | 页数 |
|
||||||
|
| `paper` | string | 纸张 |
|
||||||
|
| `print_time` | string | 印刷时间 |
|
||||||
|
| `publication_time` | number | 出版时间(时间戳) |
|
||||||
|
| `publisher` | string | 出版社 |
|
||||||
|
| `sellCount` | string | 销售数量 |
|
||||||
|
| `shop_id` | number | 店铺ID |
|
||||||
|
| `wordage` | string | 字数 |
|
||||||
|
|
||||||
|
**响应示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": {
|
||||||
|
"author": "",
|
||||||
|
"binding_layout": "",
|
||||||
|
"book_name": "",
|
||||||
|
"book_pic": "",
|
||||||
|
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/adaaecbf/a1e5df0a21cfdbba_s.jpg",
|
||||||
|
"buyCount": "",
|
||||||
|
"category": "",
|
||||||
|
"condition": "",
|
||||||
|
"content": "",
|
||||||
|
"edition": "",
|
||||||
|
"editor": "",
|
||||||
|
"express_delivery_fee": "",
|
||||||
|
"fix_price": "",
|
||||||
|
"format": "",
|
||||||
|
"isbn": "",
|
||||||
|
"item_id": 0,
|
||||||
|
"languages": "",
|
||||||
|
"mid": 0,
|
||||||
|
"pages": "",
|
||||||
|
"paper": "",
|
||||||
|
"print_time": "",
|
||||||
|
"publication_time": 0,
|
||||||
|
"publisher": "",
|
||||||
|
"sellCount": "",
|
||||||
|
"shop_id": 0,
|
||||||
|
"wordage": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 获取店铺图书信息
|
||||||
|
|
||||||
|
**请求信息**
|
||||||
|
|
||||||
|
shopResult, err := manager.GetKFZShopBookInfo(
|
||||||
|
proxyType, //代理类型
|
||||||
|
username, //小象代理账号
|
||||||
|
password, //小象代理密码
|
||||||
|
machineCode, //内置代理机械码
|
||||||
|
shopId,
|
||||||
|
isImage,
|
||||||
|
bookNum,
|
||||||
|
pageNum,
|
||||||
|
sortType,
|
||||||
|
sort,
|
||||||
|
priceDown,
|
||||||
|
priceUp,
|
||||||
|
)
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|--------|----|------------------------------------------------|
|
||||||
|
| fetchMode | string | 是 | 是否使用代理 (使用代理:proxy 不使用代理:direct) |
|
||||||
|
| proxyType | string | 是 | 代理类型(小象代理:CALF_ELEPHANT_PROXY,内置代理:TAIL_PROXY) |
|
||||||
|
| username | string | 是 | 小象代理账号 |
|
||||||
|
| password | string | 是 | 小象代理密码 |
|
||||||
|
| machineCode | string | 是 | 内置代理机械码 |
|
||||||
|
| shopId | string | 是 | 店铺编号 |
|
||||||
|
| isImage | string | 否 | 是否有图片 |
|
||||||
|
| bookNum | string | 否 | 图书数量 |
|
||||||
|
| pageNum | string | 否 | 页数 |
|
||||||
|
| sortType | string | 否 | 排序类型(sort,putDate,newItem,price只有这四种) |
|
||||||
|
| sort | string | 否 | 排序方式(asc,desc) |
|
||||||
|
| priceUp | string | 否 | 最低价格 |
|
||||||
|
| priceDown | string | 否 | 最高价格 |
|
||||||
|
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 | 示例值 |
|
||||||
|
|--------|------|--------|---------|
|
||||||
|
| `success` | boolean | 请求是否成功 | `true` |
|
||||||
|
| `message` | string | 响应消息 | `"查询成功"` |
|
||||||
|
| `goods_num` | string | 商品数量 | `"查询成功"` |
|
||||||
|
| `pnum` | string | 店铺商品页数 | `"查询成功"` |
|
||||||
|
| `data` | array | 图书信息列表 | - |
|
||||||
|
|
||||||
|
**图书信息字段说明(data数据)**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| `book_name` | string | 书名 |
|
||||||
|
| `author` | string | 作者 |
|
||||||
|
| `publisher` | string | 出版社 |
|
||||||
|
| `isbn` | string | ISBN号 |
|
||||||
|
| `publication_time` | number | 出版时间(时间戳) |
|
||||||
|
| `edition` | string | 版次 |
|
||||||
|
| `print_time` | string | 印刷时间 |
|
||||||
|
| `fix_price` | string | 定价 |
|
||||||
|
| `binding_layout` | string | 装帧 |
|
||||||
|
| `format` | string | 开本 |
|
||||||
|
| `paper` | string | 纸张 |
|
||||||
|
| `pages` | string | 页数 |
|
||||||
|
| `wordage` | string | 字数 |
|
||||||
|
| `languages` | string | 语种 |
|
||||||
|
| `era` | string | 年代 |
|
||||||
|
| `engraving_method` | string | 刻印方式 |
|
||||||
|
| `dimensions` | string | 尺寸 |
|
||||||
|
| `volume_number` | string | 册数 |
|
||||||
|
| `book_pic` | string | 图书图片(官图) |
|
||||||
|
| `book_pic_s` | string | 图书图片(实拍图) |
|
||||||
|
| `selling_price` | string | 售价 |
|
||||||
|
| `condition` | string | 品相 |
|
||||||
|
| `express_delivery_fee` | string | 快递费 |
|
||||||
|
| `editor` | string | 编辑 |
|
||||||
|
| `category` | string | 分类 |
|
||||||
|
| `buy_count` | string | 买过 |
|
||||||
|
| `sell_count` | string | 买过 |
|
||||||
|
| `content` | string | 内容 |
|
||||||
|
| `mid` | number | 商家ID |
|
||||||
|
| `item_id` | number | 商品ID |
|
||||||
|
| `shop_id` | number | 店铺ID |
|
||||||
|
|
||||||
|
**响应示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "查询成功",
|
||||||
|
"goods_num": "",
|
||||||
|
"pnum": "",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"author": "",
|
||||||
|
"binding_layout": "",
|
||||||
|
"book_name": "",
|
||||||
|
"book_pic": "",
|
||||||
|
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/cddbcbda/6833c1d27437d121_s.jpg",
|
||||||
|
"buyCount": "",
|
||||||
|
"category": "",
|
||||||
|
"condition": "",
|
||||||
|
"content": "",
|
||||||
|
"edition": "",
|
||||||
|
"editor": "",
|
||||||
|
"express_delivery_fee": "",
|
||||||
|
"fix_price": "",
|
||||||
|
"format": "",
|
||||||
|
"isbn": "",
|
||||||
|
"item_id": 0,
|
||||||
|
"languages": "",
|
||||||
|
"mid": 0,
|
||||||
|
"pages": "",
|
||||||
|
"paper": "",
|
||||||
|
"print_time": "",
|
||||||
|
"publication_time": 0,
|
||||||
|
"publisher": "",
|
||||||
|
"sellCount": "",
|
||||||
|
"shop_id": 0,
|
||||||
|
"wordage": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 根据url获取单个图书详情信息
|
||||||
|
|
||||||
|
**请求信息**
|
||||||
|
|
||||||
|
shopResult, err := manager.GetUrlBookDetails(
|
||||||
|
fetchMode,
|
||||||
|
proxyType,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
machineCode,
|
||||||
|
url
|
||||||
|
)
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|--------|----|------------------------------------------------|
|
||||||
|
| fetchMode | string | 是 | 是否使用代理 (使用代理:proxy 不使用代理:direct) |
|
||||||
|
| proxyType | string | 是 | 代理类型(小象代理:CALF_ELEPHANT_PROXY,内置代理:TAIL_PROXY) |
|
||||||
|
| username | string | 是 | 小象代理账号 |
|
||||||
|
| password | string | 是 | 小象代理密码 |
|
||||||
|
| machineCode | string | 是 | 内置代理机械码 |
|
||||||
|
| url | string | 是 | 传入详情页的url |
|
||||||
|
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 | 示例值 |
|
||||||
|
|--------|------|--------|---------|
|
||||||
|
| `success` | boolean | 请求是否成功 | `true` |
|
||||||
|
| `message` | string | 响应消息 | `"查询成功"` |
|
||||||
|
| `goods_num` | string | 商品数量 | `"查询成功"` |
|
||||||
|
| `pnum` | string | 店铺商品页数 | `"查询成功"` |
|
||||||
|
| `data` | array | 图书信息列表 | - |
|
||||||
|
|
||||||
|
**图书信息字段说明(data数据)**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| `book_name` | string | 书名 |
|
||||||
|
| `author` | string | 作者 |
|
||||||
|
| `publisher` | string | 出版社 |
|
||||||
|
| `isbn` | string | ISBN号 |
|
||||||
|
| `publication_time` | number | 出版时间(时间戳) |
|
||||||
|
| `edition` | string | 版次 |
|
||||||
|
| `print_time` | string | 印刷时间 |
|
||||||
|
| `fix_price` | string | 定价 |
|
||||||
|
| `binding_layout` | string | 装帧 |
|
||||||
|
| `format` | string | 开本 |
|
||||||
|
| `paper` | string | 纸张 |
|
||||||
|
| `pages` | string | 页数 |
|
||||||
|
| `wordage` | string | 字数 |
|
||||||
|
| `languages` | string | 语种 |
|
||||||
|
| `era` | string | 年代 |
|
||||||
|
| `engraving_method` | string | 刻印方式 |
|
||||||
|
| `dimensions` | string | 尺寸 |
|
||||||
|
| `volume_number` | string | 册数 |
|
||||||
|
| `book_pic` | string | 图书图片(官图) |
|
||||||
|
| `book_pic_s` | string | 图书图片(实拍图) |
|
||||||
|
| `selling_price` | string | 售价 |
|
||||||
|
| `condition` | string | 品相 |
|
||||||
|
| `express_delivery_fee` | string | 快递费 |
|
||||||
|
| `editor` | string | 编辑 |
|
||||||
|
| `category` | string | 分类 |
|
||||||
|
| `buy_count` | string | 买过 |
|
||||||
|
| `sell_count` | string | 买过 |
|
||||||
|
| `content` | string | 内容 |
|
||||||
|
| `mid` | number | 商家ID |
|
||||||
|
| `item_id` | number | 商品ID |
|
||||||
|
| `shop_id` | number | 店铺ID |
|
||||||
|
|
||||||
|
**响应示例**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "查询成功",
|
||||||
|
"goods_num": "",
|
||||||
|
"pnum": "",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"author": "",
|
||||||
|
"binding_layout": "",
|
||||||
|
"book_name": "",
|
||||||
|
"book_pic": "",
|
||||||
|
"book_pic_s": "https://www0.kfzimg.com/sw/kfz-cos/kfzimg/cddbcbda/6833c1d27437d121_s.jpg",
|
||||||
|
"buyCount": "",
|
||||||
|
"category": "",
|
||||||
|
"condition": "",
|
||||||
|
"content": "",
|
||||||
|
"edition": "",
|
||||||
|
"editor": "",
|
||||||
|
"express_delivery_fee": "",
|
||||||
|
"fix_price": "",
|
||||||
|
"format": "",
|
||||||
|
"isbn": "",
|
||||||
|
"item_id": 0,
|
||||||
|
"languages": "",
|
||||||
|
"mid": 0,
|
||||||
|
"pages": "",
|
||||||
|
"paper": "",
|
||||||
|
"print_time": "",
|
||||||
|
"publication_time": 0,
|
||||||
|
"publisher": "",
|
||||||
|
"sellCount": "",
|
||||||
|
"shop_id": 0,
|
||||||
|
"wordage": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 初始化
|
||||||
|
|
||||||
|
**请求信息**
|
||||||
|
|
||||||
|
manager.Initialize(
|
||||||
|
configJSON,
|
||||||
|
)
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|--------|----|------|
|
||||||
|
| configJSON | string | 是 | 配置信息 |
|
||||||
|
|
||||||
|
**响应数据**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 | 示例值 |
|
||||||
|
|--------|------|--------|---------|
|
||||||
|
| `success` | boolean | 请求是否成功 | `true` |
|
||||||
|
| `message` | string | 响应消息 | `"查询成功"` |
|
||||||
|
|
||||||
|
**图书信息字段说明(data数据)**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| `book_name` | string | 书名 |
|
||||||
|
| `author` | string | 作者 |
|
||||||
|
| `publisher` | string | 出版社 |
|
||||||
|
| `isbn` | string | ISBN号 |
|
||||||
|
| `publication_time` | number | 出版时间(时间戳) |
|
||||||
|
| `edition` | string | 版次 |
|
||||||
|
| `print_time` | string | 印刷时间 |
|
||||||
|
| `fix_price` | string | 定价 |
|
||||||
|
| `binding_layout` | string | 装帧 |
|
||||||
|
| `format` | string | 开本 |
|
||||||
|
| `paper` | string | 纸张 |
|
||||||
|
| `pages` | string | 页数 |
|
||||||
|
| `wordage` | string | 字数 |
|
||||||
|
| `languages` | string | 语种 |
|
||||||
|
| `era` | string | 年代 |
|
||||||
|
| `engraving_method` | string | 刻印方式 |
|
||||||
|
| `dimensions` | string | 尺寸 |
|
||||||
|
| `volume_number` | string | 册数 |
|
||||||
|
| `book_pic` | string | 图书图片(官图) |
|
||||||
|
| `book_pic_s` | string | 图书图片(实拍图) |
|
||||||
|
| `selling_price` | string | 售价 |
|
||||||
|
| `condition` | string | 品相 |
|
||||||
|
| `express_delivery_fee` | string | 快递费 |
|
||||||
|
| `editor` | string | 编辑 |
|
||||||
|
| `category` | string | 分类 |
|
||||||
|
| `buy_count` | string | 买过 |
|
||||||
|
| `sell_count` | string | 买过 |
|
||||||
|
| `content` | string | 内容 |
|
||||||
|
| `mid` | number | 商家ID |
|
||||||
|
| `item_id` | number | 商品ID |
|
||||||
|
| `shop_id` | number | 店铺ID |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# proxyConfig.dll文件使用讲解
|
||||||
|
由于是kongfz.dll文件去调用proxyConfig.dll文件里面的函数,无需再去调用。
|
||||||
|
除非直接使用proxyConfig.dll文件。
|
||||||
|
## 1.直接使用proxyConfig.dll文件,应先加载DLL
|
||||||
|
```go
|
||||||
|
func main(){
|
||||||
|
// 加载DLL
|
||||||
|
manager, err := NewDLLManager("文件路径")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("初始化DLL管理器失败: %v", err)
|
||||||
|
}
|
||||||
|
// 确保字段被释放,但是需要注意,如果加这个,可能把你的dll文件释放掉了,导致程序崩溃
|
||||||
|
defer manager.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type DLLManager struct {
|
||||||
|
dll *syscall.DLL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDLLManager(dllPath string) (*DLLManager, error) {
|
||||||
|
dll, err := syscall.LoadDLL(dllPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("加载DLL失败: %v", err)
|
||||||
|
}
|
||||||
|
return &DLLManager{dll: dll}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DLLManager) Close() {
|
||||||
|
if m.dll != nil {
|
||||||
|
m.dll.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyTypeManager 调用代理类型管理器
|
||||||
|
func (m *ProxyConfigManager) ProxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
|
||||||
|
proc, err := m.dll.FindProc("ProxyTypeManager")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("找不到函数 ProxyTypeManager: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备参数
|
||||||
|
proxyTypePtr, _ := syscall.BytePtrFromString(proxyType)
|
||||||
|
usernamePtr, _ := syscall.BytePtrFromString(username)
|
||||||
|
passwordPtr, _ := syscall.BytePtrFromString(password)
|
||||||
|
machineCodePtr, _ := syscall.BytePtrFromString(machineCode)
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
r1, _, err := proc.Call(
|
||||||
|
uintptr(unsafe.Pointer(proxyTypePtr)),
|
||||||
|
uintptr(unsafe.Pointer(usernamePtr)),
|
||||||
|
uintptr(unsafe.Pointer(passwordPtr)),
|
||||||
|
uintptr(unsafe.Pointer(machineCodePtr)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil && err.Error() != "The operation completed successfully." {
|
||||||
|
return "", fmt.Errorf("调用 ProxyTypeManager 失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换结果
|
||||||
|
result := (*byte)(unsafe.Pointer(r1))
|
||||||
|
var resultBytes []byte
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
bytePtr := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(result)) + uintptr(i)))
|
||||||
|
if *bytePtr == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
resultBytes = append(resultBytes, *bytePtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放内存
|
||||||
|
freeProc, _ := m.dll.FindProc("FreeCString")
|
||||||
|
if freeProc != nil {
|
||||||
|
freeProc.Call(r1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(resultBytes), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 直接调用ProxyTypeManager函数方法
|
||||||
|
```go
|
||||||
|
typeManager, err := globalProxyManager.ProxyTypeManager(
|
||||||
|
proxyType,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
machineCode,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
**请求信息**
|
||||||
|
typeManager, err := globalProxyManager.ProxyTypeManager(
|
||||||
|
proxyType,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
machineCode,
|
||||||
|
)
|
||||||
|
|
||||||
|
**请求参数**
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|------|--------|----|------------|
|
||||||
|
| proxyType | string | 是 | 代理类型(小象代理:CALF_ELEPHANT_PROXY,内置代理:TAIL_PROXY) |
|
||||||
|
| username | string | 是 | 小象代理账号 |
|
||||||
|
| password | string | 是 | 小象代理密码 |
|
||||||
|
| machineCode | string | 是 | 内置代理机械码 |
|
||||||
|
|
||||||
|
**响应信息**
|
||||||
|
string字符串: http://18434270290:JWt15lWW@111.179.77.244:44751
|
||||||
510
proxyConfig.go
Normal file
510
proxyConfig.go
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// #include <stdlib.h>
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/parnurzeal/gorequest"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 代理类型常量
|
||||||
|
const (
|
||||||
|
CalfElephantProxyType = "CALF_ELEPHANT_PROXY"
|
||||||
|
TailProxyType = "TAIL_PROXY"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 代理服务器列表
|
||||||
|
var (
|
||||||
|
servers = []string{
|
||||||
|
"http-dynamic.xiaoxiangdaili.com",
|
||||||
|
"http-dynamic-S02.xiaoxiangdaili.com",
|
||||||
|
"http-dynamic-S03.xiaoxiangdaili.com",
|
||||||
|
"http-dynamic-S04.xiaoxiangdaili.com",
|
||||||
|
}
|
||||||
|
randMutex sync.Mutex
|
||||||
|
globalRand *rand.Rand
|
||||||
|
|
||||||
|
// 代理健康状态管理
|
||||||
|
proxyHealthMaps = make(map[string]*ProxyHealth)
|
||||||
|
proxyHealthMutex sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProxyManager 代理管理器结构体
|
||||||
|
type ProxyManager struct {
|
||||||
|
servers []string `json:"servers"` // 代理服务器列表
|
||||||
|
username string `json:"username"` // 代理账号
|
||||||
|
password string `json:"password"` // 代理密码
|
||||||
|
tailCardSecret string `json:"tail_card_secret"` // 尾巴代理卡密
|
||||||
|
proxyType string `json:"proxy_type"` // 代理类型 CALF_ELEPHANT_PROXY/TAIL_PROXY
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理健康状态
|
||||||
|
type ProxyHealth struct {
|
||||||
|
SuccessCount int // 成功次数
|
||||||
|
FailCount int // 失败次数
|
||||||
|
LastCheck time.Time // 最后检查时间
|
||||||
|
ResponseTime time.Duration // 响应时间
|
||||||
|
IsHealthy bool // 是否健康
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 创建全局的随机数生成器
|
||||||
|
globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取代理URL
|
||||||
|
func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
|
||||||
|
switch proxyType {
|
||||||
|
case CalfElephantProxyType:
|
||||||
|
return buildCalfElephantProxyURL(username, password)
|
||||||
|
case TailProxyType:
|
||||||
|
return buildTailProxyURL(machineCode)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("不支持的代理类型: %s", proxyType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建小象代理URL
|
||||||
|
func buildCalfElephantProxyURL(username, password string) (string, error) {
|
||||||
|
server := randomServer()
|
||||||
|
proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
|
||||||
|
url.QueryEscape(username),
|
||||||
|
url.QueryEscape(password),
|
||||||
|
server,
|
||||||
|
10030)
|
||||||
|
|
||||||
|
// 检测代理可用性
|
||||||
|
if err := checkProxyHealth(proxyURL); err != nil {
|
||||||
|
log.Printf("[WARN] 代理 %s 检测失败: %v", server, err)
|
||||||
|
// 尝试下一个代理服务器
|
||||||
|
return tryNextCalfElephantProxy(username, password, server)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] 使用小象代理: %s", server)
|
||||||
|
return proxyURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试下一个小象代理服务器
|
||||||
|
func tryNextCalfElephantProxy(username, password, failedServer string) (string, error) {
|
||||||
|
// 创建服务器副本并排除失败的服务器
|
||||||
|
availableServers := make([]string, 0)
|
||||||
|
for _, server := range servers {
|
||||||
|
if server != failedServer {
|
||||||
|
availableServers = append(availableServers, server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(availableServers) == 0 {
|
||||||
|
return "", fmt.Errorf("所有小象代理服务器都不可用")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机尝试可用服务器
|
||||||
|
for _, server := range shuffleServers(availableServers) {
|
||||||
|
proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
|
||||||
|
url.QueryEscape(username),
|
||||||
|
url.QueryEscape(password),
|
||||||
|
server,
|
||||||
|
10030)
|
||||||
|
|
||||||
|
if err := checkProxyHealth(proxyURL); err == nil {
|
||||||
|
log.Printf("[INFO] 切换到可用代理: %s", server)
|
||||||
|
return proxyURL, nil
|
||||||
|
}
|
||||||
|
log.Printf("[WARN] 代理 %s 检测失败", server)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("所有可用的小象代理服务器都检测失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建内置代理URL
|
||||||
|
func buildTailProxyURL(machineCode string) (string, error) {
|
||||||
|
proxies, err := getProxies(machineCode)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(proxies) == 0 {
|
||||||
|
return "", fmt.Errorf("未获取到有效代理")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤并选择健康的代理
|
||||||
|
healthyProxies := filterHealthyProxies(proxies)
|
||||||
|
if len(healthyProxies) > 0 {
|
||||||
|
proxyURL := "http://" + randomElement(healthyProxies)
|
||||||
|
log.Printf("[INFO] 使用健康尾巴代理: %s", proxyURL)
|
||||||
|
return proxyURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有健康代理,检测所有代理
|
||||||
|
log.Printf("[INFO] 未找到健康代理,开始检测代理可用性...")
|
||||||
|
return findWorkingTailProxy(proxies)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤健康代理
|
||||||
|
func filterHealthyProxies(proxies []string) []string {
|
||||||
|
proxyHealthMutex.RLock()
|
||||||
|
defer proxyHealthMutex.RUnlock()
|
||||||
|
|
||||||
|
var healthy []string
|
||||||
|
for _, proxy := range proxies {
|
||||||
|
if health, exists := proxyHealthMaps[proxy]; exists && health.IsHealthy {
|
||||||
|
// 检查是否在最近检查过(5分钟内)
|
||||||
|
if time.Since(health.LastCheck) < 5*time.Minute {
|
||||||
|
healthy = append(healthy, proxy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return healthy
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找可用的尾巴代理
|
||||||
|
func findWorkingTailProxy(proxies []string) (string, error) {
|
||||||
|
// 打乱代理顺序
|
||||||
|
shuffledProxies := shuffleServers(proxies)
|
||||||
|
|
||||||
|
// 并发检测代理
|
||||||
|
type proxyResult struct {
|
||||||
|
proxy string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan proxyResult, len(shuffledProxies))
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// 限制并发数
|
||||||
|
sem := make(chan struct{}, 5)
|
||||||
|
|
||||||
|
for _, proxy := range shuffledProxies {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p string) {
|
||||||
|
defer wg.Done()
|
||||||
|
sem <- struct{}{}
|
||||||
|
defer func() { <-sem }()
|
||||||
|
|
||||||
|
proxyURL := "http://" + p
|
||||||
|
err := checkProxyHealth(proxyURL)
|
||||||
|
ch <- proxyResult{proxy: p, err: err}
|
||||||
|
}(proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(ch)
|
||||||
|
|
||||||
|
// 收集结果
|
||||||
|
var workingProxies []string
|
||||||
|
for result := range ch {
|
||||||
|
if result.err == nil {
|
||||||
|
workingProxies = append(workingProxies, result.proxy)
|
||||||
|
// 更新健康状态
|
||||||
|
updateProxyHealth(result.proxy, true, 0)
|
||||||
|
} else {
|
||||||
|
updateProxyHealth(result.proxy, false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(workingProxies) > 0 {
|
||||||
|
selected := randomElement(workingProxies)
|
||||||
|
log.Printf("[INFO] 找到可用代理: %s (共 %d 个可用)", selected, len(workingProxies))
|
||||||
|
return "http://" + selected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("所有尾巴代理都不可用")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测代理健康状态
|
||||||
|
func checkProxyHealth(proxyURL string) error {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
req := gorequest.New().Proxy(proxyURL).Timeout(10 * time.Second)
|
||||||
|
resp, _, errs := req.Get("https://shop.kongfz.com/").End()
|
||||||
|
|
||||||
|
responseTime := time.Since(start)
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
updateProxyHealth(proxyURL, false, responseTime)
|
||||||
|
return fmt.Errorf("代理连接失败: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
updateProxyHealth(proxyURL, false, responseTime)
|
||||||
|
return fmt.Errorf("代理响应状态码错误: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProxyHealth(proxyURL, true, responseTime)
|
||||||
|
log.Printf("[DEBUG] 代理 %s 检测成功, 响应时间: %v", getProxyHost(proxyURL), responseTime)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新代理健康状态
|
||||||
|
func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration) {
|
||||||
|
proxyHealthMutex.Lock()
|
||||||
|
defer proxyHealthMutex.Unlock()
|
||||||
|
|
||||||
|
host := getProxyHost(proxyURL)
|
||||||
|
if _, exists := proxyHealthMaps[host]; !exists {
|
||||||
|
proxyHealthMaps[host] = &ProxyHealth{}
|
||||||
|
}
|
||||||
|
|
||||||
|
health := proxyHealthMaps[host]
|
||||||
|
health.LastCheck = time.Now()
|
||||||
|
|
||||||
|
if success {
|
||||||
|
health.SuccessCount++
|
||||||
|
health.FailCount = 0
|
||||||
|
health.ResponseTime = responseTime
|
||||||
|
health.IsHealthy = true
|
||||||
|
} else {
|
||||||
|
health.FailCount++
|
||||||
|
health.SuccessCount = 0
|
||||||
|
// 连续失败3次标记为不健康
|
||||||
|
if health.FailCount >= 3 {
|
||||||
|
health.IsHealthy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取代理主机名
|
||||||
|
func getProxyHost(proxyURL string) string {
|
||||||
|
if strings.HasPrefix(proxyURL, "http://") {
|
||||||
|
proxyURL = proxyURL[7:]
|
||||||
|
}
|
||||||
|
// 去除认证信息
|
||||||
|
if atIndex := strings.Index(proxyURL, "@"); atIndex != -1 {
|
||||||
|
proxyURL = proxyURL[atIndex+1:]
|
||||||
|
}
|
||||||
|
// 去除端口
|
||||||
|
if colonIndex := strings.Index(proxyURL, ":"); colonIndex != -1 {
|
||||||
|
proxyURL = proxyURL[:colonIndex]
|
||||||
|
}
|
||||||
|
return proxyURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机代理服务器
|
||||||
|
func randomServer() string {
|
||||||
|
return randomElement(servers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 线程安全的随机元素选择
|
||||||
|
func randomElement(slice []string) string {
|
||||||
|
randMutex.Lock()
|
||||||
|
defer randMutex.Unlock()
|
||||||
|
return slice[globalRand.Intn(len(slice))]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打乱服务器顺序
|
||||||
|
func shuffleServers(servers []string) []string {
|
||||||
|
randMutex.Lock()
|
||||||
|
defer randMutex.Unlock()
|
||||||
|
|
||||||
|
shuffled := make([]string, len(servers))
|
||||||
|
copy(shuffled, servers)
|
||||||
|
globalRand.Shuffle(len(shuffled), func(i, j int) {
|
||||||
|
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
||||||
|
})
|
||||||
|
return shuffled
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查卡密是否过期
|
||||||
|
func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
|
||||||
|
code, err := getMachineCode(tailCardSecret)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("请求错误: %v", err)
|
||||||
|
}
|
||||||
|
targetTime, err := time.Parse("2006-01-02 15:04:05", code.Data.IpExpTime)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("时间格式错误: %v", err)
|
||||||
|
}
|
||||||
|
currentTime := time.Now()
|
||||||
|
if targetTime.After(currentTime) {
|
||||||
|
return true, nil
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("卡密已经过期!过期时间: %v", targetTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义响应结构体
|
||||||
|
type getMachineCodeResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data struct {
|
||||||
|
MachineCode string `json:"machine_code"`
|
||||||
|
IpExpTime string `json:"ip_exp_time"`
|
||||||
|
IpThread int `json:"ip_thread"`
|
||||||
|
IpCardCode string `json:"ip_card_code"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询机器码
|
||||||
|
func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
|
||||||
|
url := "http://114.66.2.223:7842/api/proxies/ip_show"
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"ip_card_code": tailCardSecret,
|
||||||
|
"agent_id": 9999,
|
||||||
|
}
|
||||||
|
_, body, errs := gorequest.New().Post(url).Send(data).End()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, fmt.Errorf("查询机器码失败: %v", errs)
|
||||||
|
}
|
||||||
|
var resp getMachineCodeResp
|
||||||
|
if err := json.Unmarshal([]byte(body), &resp); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Code == 201 {
|
||||||
|
machineCode, err := rechargeCard(tailCardSecret, resp.Data.MachineCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.Data.MachineCode = machineCode
|
||||||
|
return &resp, nil
|
||||||
|
} else if resp.Code == 200 {
|
||||||
|
return &resp, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("查询机器码失败: %s", resp.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值卡密
|
||||||
|
func rechargeCard(tailCardSecret, machineCode string) (string, error) {
|
||||||
|
url := "http://114.66.2.223:7842/api/proxies/ip_recharge"
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"machine_code": machineCode,
|
||||||
|
"ip_card_code": tailCardSecret,
|
||||||
|
"agent_id": 9999,
|
||||||
|
}
|
||||||
|
_, body, errs := gorequest.New().Post(url).Send(data).End()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return "", fmt.Errorf("充值卡密失败: %v", errs)
|
||||||
|
}
|
||||||
|
var resp struct {
|
||||||
|
Code int `json:"code"` // 状态码
|
||||||
|
Message string `json:"message"` // 返回消息
|
||||||
|
Data struct {
|
||||||
|
IpExpTime string `json:"ip_exp_time"` // 过期时间
|
||||||
|
IpThread int `json:"ip_thread"` // 线程
|
||||||
|
MachineCode string `json:"machine_code"` // 机器码
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(body), &resp); err != nil {
|
||||||
|
return "", fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return "", fmt.Errorf("充值卡密失败: %s", resp.Message)
|
||||||
|
}
|
||||||
|
return resp.Data.MachineCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取代理服务器列表
|
||||||
|
func getProxies(machineCode string) ([]string, error) {
|
||||||
|
log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
|
||||||
|
// 生成签名
|
||||||
|
sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode))))
|
||||||
|
|
||||||
|
GetProxiesUrl := fmt.Sprintf("http://114.66.2.223:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999",
|
||||||
|
machineCode, sign)
|
||||||
|
|
||||||
|
req := gorequest.New().Get(GetProxiesUrl).Timeout(20 * time.Second)
|
||||||
|
_, body, errs := req.End()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, fmt.Errorf("获取代理失败: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为JSON错误响应
|
||||||
|
if strings.HasPrefix(strings.TrimSpace(body), "{") && strings.HasSuffix(strings.TrimSpace(body), "}") {
|
||||||
|
// 尝试解析为JSON错误响应
|
||||||
|
var errorResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(body), &errorResp); err == nil {
|
||||||
|
return nil, fmt.Errorf("获取代理失败: %s (错误码: %d)", errorResp.Message, errorResp.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
lines := strings.Split(strings.TrimSpace(body), "\n")
|
||||||
|
var proxies []string
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line != "" && !strings.HasPrefix(line, "{") {
|
||||||
|
proxies = append(proxies, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(proxies) == 0 {
|
||||||
|
return nil, fmt.Errorf("未获取到有效代理")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] 获取到 %d 个代理", len(proxies))
|
||||||
|
return proxies, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化代理信息
|
||||||
|
func initProxy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出函数:获取代理健康状态(用于调试)
|
||||||
|
//
|
||||||
|
//export GetProxyHealth
|
||||||
|
func GetProxyHealth() *C.char {
|
||||||
|
proxyHealthMutex.RLock()
|
||||||
|
defer proxyHealthMutex.RUnlock()
|
||||||
|
|
||||||
|
healthInfo := make(map[string]interface{})
|
||||||
|
for proxy, health := range proxyHealthMaps {
|
||||||
|
healthInfo[proxy] = map[string]interface{}{
|
||||||
|
"success_count": health.SuccessCount,
|
||||||
|
"fail_count": health.FailCount,
|
||||||
|
"last_check": health.LastCheck.Format(time.RFC3339),
|
||||||
|
"response_time": health.ResponseTime.String(),
|
||||||
|
"is_healthy": health.IsHealthy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(healthInfo)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf(`{"error": "序列化健康信息失败: %v"}`, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.CString(string(jsonData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出函数:代理类型管理器
|
||||||
|
//
|
||||||
|
//export ProxyTypeManager
|
||||||
|
func ProxyTypeManager(proxyType, username, password, machineCode *C.char) *C.char {
|
||||||
|
goProxyType := C.GoString(proxyType)
|
||||||
|
goUsername := C.GoString(username)
|
||||||
|
goPassword := C.GoString(password)
|
||||||
|
goMachineCode := C.GoString(machineCode)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] 代理类型管理器调用: type=%s", goProxyType)
|
||||||
|
|
||||||
|
proxyURL, err := proxyTypeManager(goProxyType, goUsername, goPassword, goMachineCode)
|
||||||
|
if err != nil {
|
||||||
|
errorMsg := fmt.Sprintf("ERROR: %v", err)
|
||||||
|
log.Printf("[ERROR] 代理类型管理器错误: %v", err)
|
||||||
|
return C.CString(errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] 代理类型管理器返回: %s", proxyURL)
|
||||||
|
return C.CString(proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 导出函数:释放C字符串内存
|
||||||
|
////
|
||||||
|
////export FreeCString
|
||||||
|
//func FreeCString(str *C.char) {
|
||||||
|
// C.free(unsafe.Pointer(str))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func main() {
|
||||||
|
//
|
||||||
|
//}
|
||||||
1009
zjdydll.go
Normal file
1009
zjdydll.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user