更新修改功能
This commit is contained in:
parent
d41bfcb4ca
commit
909313e2b5
3031
csv/csv.go
3031
csv/csv.go
File diff suppressed because it is too large
Load Diff
843
csv/csvDll.go
Normal file
843
csv/csvDll.go
Normal file
@ -0,0 +1,843 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
//var (
|
||||||
|
// headerOnce sync.Once
|
||||||
|
// fileHandle int64 = -1
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
//// testwrite 函数 - 使用sync.Once确保表头只写入一次
|
||||||
|
//func testwrite(mgr *CSVManager, batchIndex int, wg *sync.WaitGroup) {
|
||||||
|
// defer wg.Done()
|
||||||
|
//
|
||||||
|
// fmt.Printf("协程 %d 开始执行...\n", batchIndex)
|
||||||
|
//
|
||||||
|
// // 1. 打开CSV文件 - 所有协程操作同一个文件
|
||||||
|
// filename := "csv/test_concurrent.csv"
|
||||||
|
// delimiter := ','
|
||||||
|
// hasHeader := true
|
||||||
|
//
|
||||||
|
// handleID, err := mgr.OpenCSVFile(filename, delimiter, hasHeader)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("协程 %d 打开文件失败: %v\n", batchIndex, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("协程 %d 成功打开文件,句柄ID: %d\n", batchIndex, handleID)
|
||||||
|
//
|
||||||
|
// // 2. 使用sync.Once确保表头只写入一次
|
||||||
|
// headerOnce.Do(func() {
|
||||||
|
// header := []string{
|
||||||
|
// "ID", "Name",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// err = mgr.WriteHeader(handleID, header)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("写入表头失败: %v\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Println("表头写入成功(仅第一次)")
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // 3. 生成并写入1000条数据
|
||||||
|
// batchSize := 1000
|
||||||
|
// //testData := generateTestData(batchIndex, batchSize)
|
||||||
|
//
|
||||||
|
// var rows [][]string
|
||||||
|
//
|
||||||
|
// for i := 0; i < batchSize; i++ {
|
||||||
|
// //rowIndex := batchIndex*batchSize + i + 1
|
||||||
|
// row := []string{
|
||||||
|
// fmt.Sprintf("%d", i),
|
||||||
|
// fmt.Sprintf("用户_%d", i),
|
||||||
|
// }
|
||||||
|
// rows = append(rows, row)
|
||||||
|
// // 批量写入数据
|
||||||
|
// _, err = mgr.AppendRows(handleID, rows)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("协程 %d 写入数据失败: %v\n", batchIndex, err)
|
||||||
|
// mgr.CloseHandle(handleID)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("协程 %d 成功写入 %d 条数据\n", batchIndex, i)
|
||||||
|
// rows = nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //return rows
|
||||||
|
//
|
||||||
|
// // 4. 关闭句柄
|
||||||
|
// err = mgr.CloseHandle(handleID)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("协程 %d 关闭句柄失败: %v\n", batchIndex, err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("协程 %d 关闭句柄成功: %d\n", batchIndex, handleID)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("协程 %d 执行完成\n", batchIndex)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 生成测试数据
|
||||||
|
//func generateTestData(batchIndex, batchSize int) [][]string {
|
||||||
|
// var rows [][]string
|
||||||
|
//
|
||||||
|
// for i := 0; i < batchSize; i++ {
|
||||||
|
// rowIndex := batchIndex*batchSize + i + 1
|
||||||
|
// row := []string{
|
||||||
|
// fmt.Sprintf("%d", rowIndex),
|
||||||
|
// fmt.Sprintf("用户_%d", rowIndex),
|
||||||
|
// fmt.Sprintf("user%d@example.com", rowIndex),
|
||||||
|
// fmt.Sprintf("%d", 20+(rowIndex%50)),
|
||||||
|
// fmt.Sprintf("地址_%d", rowIndex),
|
||||||
|
// fmt.Sprintf("13800138%03d", rowIndex%1000),
|
||||||
|
// []string{"active", "inactive", "pending"}[rowIndex%3],
|
||||||
|
// fmt.Sprintf("%d", 50+(rowIndex%50)),
|
||||||
|
// []string{"A", "B", "C", "D"}[rowIndex%4],
|
||||||
|
// fmt.Sprintf("tag%d,tag%d", rowIndex, rowIndex+1),
|
||||||
|
// }
|
||||||
|
// rows = append(rows, row)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return rows
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func main() {
|
||||||
|
// fmt.Println("开始多协程CSV写入测试...")
|
||||||
|
// fmt.Println("所有协程同时操作同一个文件:test_concurrent.csv")
|
||||||
|
// fmt.Println("每个协程执行:打开文件 → 写入表头(仅第一次)→ 写入1000条数据 → 关闭文件")
|
||||||
|
// fmt.Println("启动10个协程,总共写入10000条数据")
|
||||||
|
//
|
||||||
|
// // 获取CSV管理器
|
||||||
|
// mgr := GetManager()
|
||||||
|
//
|
||||||
|
// // 创建等待组
|
||||||
|
// var wg sync.WaitGroup
|
||||||
|
//
|
||||||
|
// // 启动10个协程,每个协程执行完整的操作流程
|
||||||
|
// totalGoroutines := 4
|
||||||
|
//
|
||||||
|
// for i := 0; i < totalGoroutines; i++ {
|
||||||
|
// wg.Add(1)
|
||||||
|
// go testwrite(mgr, i, &wg)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 等待所有协程完成
|
||||||
|
// wg.Wait()
|
||||||
|
//
|
||||||
|
// fmt.Println("\n所有协程执行完成!")
|
||||||
|
// fmt.Println("测试文件:test_concurrent.csv")
|
||||||
|
//}
|
||||||
|
|
||||||
|
//import "C"
|
||||||
|
//import (
|
||||||
|
// "bytes"
|
||||||
|
// "encoding/csv"
|
||||||
|
// "encoding/json"
|
||||||
|
// "fmt"
|
||||||
|
// "io"
|
||||||
|
// "net/http"
|
||||||
|
// "os"
|
||||||
|
// "path/filepath"
|
||||||
|
// "syscall"
|
||||||
|
// "unsafe"
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
//// CsvDLL 代理DLL结构
|
||||||
|
//type csvDLL struct {
|
||||||
|
// dll *syscall.DLL
|
||||||
|
// openCSVFile *syscall.Proc // 打开/创建CSV文件
|
||||||
|
// readRows *syscall.Proc // 读取多行数据
|
||||||
|
// writeRows *syscall.Proc // 写入/覆盖行数据
|
||||||
|
// appendRows *syscall.Proc // 追加行数据
|
||||||
|
// getRowCount *syscall.Proc // 获取总行数
|
||||||
|
// findRows *syscall.Proc // 搜索行
|
||||||
|
// closeCSVFile *syscall.Proc // 关闭CSV文件
|
||||||
|
// mergeCSVFiles *syscall.Proc // 合并多个CSV文件
|
||||||
|
// getError *syscall.Proc // 获取错误信息
|
||||||
|
// updateCSVRowSafe *syscall.Proc
|
||||||
|
// createOpenCSVFile *syscall.Proc
|
||||||
|
// freeCString *syscall.Proc // 释放C字符串
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 初始化csvDLL
|
||||||
|
//func InitCsvDLL() (*csvDLL, error) {
|
||||||
|
// dllPath := filepath.Join("csv/dll", "csv.dll")
|
||||||
|
// if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||||
|
// return nil, fmt.Errorf("csv DLL 不存在: %s", dllPath)
|
||||||
|
// }
|
||||||
|
// if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||||
|
// return nil, fmt.Errorf("加载csv DLL 失败: %s", err)
|
||||||
|
// } else {
|
||||||
|
// return &csvDLL{
|
||||||
|
// dll: dll,
|
||||||
|
// openCSVFile: dll.MustFindProc("OpenCSVFile"),
|
||||||
|
// updateCSVRowSafe: dll.MustFindProc("UpdateCSVRowSafe"),
|
||||||
|
// readRows: dll.MustFindProc("ReadRows"),
|
||||||
|
// writeRows: dll.MustFindProc("WriteRows"),
|
||||||
|
// appendRows: dll.MustFindProc("AppendRows"),
|
||||||
|
// getRowCount: dll.MustFindProc("GetRowCount"),
|
||||||
|
// findRows: dll.MustFindProc("FindRows"),
|
||||||
|
// closeCSVFile: dll.MustFindProc("CloseCSVFile"),
|
||||||
|
// mergeCSVFiles: dll.MustFindProc("MergeCSVFiles"),
|
||||||
|
// createOpenCSVFile: dll.MustFindProc("CreateOpenCSVFile"),
|
||||||
|
// getError: dll.MustFindProc("GetError"),
|
||||||
|
// freeCString: dll.MustFindProc("FreeCString"),
|
||||||
|
// }, nil
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// cStr 获取C字符串
|
||||||
|
//func (m *csvDLL) cStr(p uintptr) string {
|
||||||
|
// if p == 0 {
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
// b := []byte{}
|
||||||
|
// for i := uintptr(0); ; i++ {
|
||||||
|
// c := *(*byte)(unsafe.Pointer(p + i))
|
||||||
|
// if c == 0 {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// b = append(b, c)
|
||||||
|
// }
|
||||||
|
// s := string(b)
|
||||||
|
// if m.freeCString != nil {
|
||||||
|
// m.freeCString.Call(p)
|
||||||
|
// }
|
||||||
|
// return s
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 打开/创建CSV文件
|
||||||
|
//func (m *csvDLL) OpenCSVFile(filePath string, delimiter byte, hasHeader bool) (int64, error) {
|
||||||
|
// proc, err := m.dll.FindProc("OpenCSVFile")
|
||||||
|
// if err != nil {
|
||||||
|
// return -1, fmt.Errorf("找不到函数 OpenCSVFile: %v", err)
|
||||||
|
// }
|
||||||
|
// filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
||||||
|
// hasHeaderInt := 0
|
||||||
|
// if hasHeader {
|
||||||
|
// hasHeaderInt = 1
|
||||||
|
// }
|
||||||
|
// info, _, _ := proc.Call(
|
||||||
|
// uintptr(unsafe.Pointer(filePathPtr)),
|
||||||
|
// uintptr(delimiter),
|
||||||
|
// uintptr(hasHeaderInt))
|
||||||
|
//
|
||||||
|
// return int64(info), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// CSV响应结构体
|
||||||
|
//type CSVResponses struct {
|
||||||
|
// Success bool `json:"success"`
|
||||||
|
// Message string `json:"message,omitempty"`
|
||||||
|
// Data CSVData `json:"data,omitempty"`
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//type CSVData struct {
|
||||||
|
// HandleID int64 `json:"handleID"`
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (m *csvDLL) CreateOpenCSVFile(filePath string, delimiter byte, hasHeader bool) (*CSVResponses, error) {
|
||||||
|
// proc, err := m.dll.FindProc("CreateOpenCSVFile")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("找不到函数 CreateOpenCSVFile: %v", err)
|
||||||
|
// }
|
||||||
|
// filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
||||||
|
// hasHeaderInt := 0
|
||||||
|
// if hasHeader {
|
||||||
|
// hasHeaderInt = 1
|
||||||
|
// }
|
||||||
|
// info, _, _ := proc.Call(
|
||||||
|
// uintptr(unsafe.Pointer(filePathPtr)),
|
||||||
|
// uintptr(delimiter),
|
||||||
|
// uintptr(hasHeaderInt))
|
||||||
|
// var csvResponse CSVResponses
|
||||||
|
// str := m.cStr(info)
|
||||||
|
// if err := json.Unmarshal([]byte(str), &csvResponse); err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return &csvResponse, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (m *csvDLL) UpdateCSVRowSafe(handleID int64, rowNum int, newRow []string) (*CSVResponses, error) {
|
||||||
|
// proc, err := m.dll.FindProc("UpdateCSVRowSafe")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("找不到函数 UpdateCSVRowSafe: %v", err)
|
||||||
|
// }
|
||||||
|
// jsonString, _ := json.Marshal(newRow)
|
||||||
|
// newRowPtr, _ := syscall.BytePtrFromString(string(jsonString))
|
||||||
|
// info, _, _ := proc.Call(
|
||||||
|
// uintptr(handleID),
|
||||||
|
// uintptr(rowNum),
|
||||||
|
// uintptr(unsafe.Pointer(newRowPtr)))
|
||||||
|
// var csvResponse CSVResponses
|
||||||
|
// str := m.cStr(info)
|
||||||
|
// if err := json.Unmarshal([]byte(str), &csvResponse); err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return &csvResponse, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
////// 读取多行数据
|
||||||
|
////func (m *csvDLL) ReadRows(handle int64) ([]string, error) {
|
||||||
|
//// proc, err := m.dll.FindProc("ReadRows")
|
||||||
|
//// if err != nil {
|
||||||
|
//// return nil, fmt.Errorf("找不到函数 ReadRows: %v", err)
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
//// 写入/覆盖行数据
|
||||||
|
//func (m *csvDLL) WriteRows(handle int64, rowsData [][]string, header int) (int, error) {
|
||||||
|
// proc, err := m.dll.FindProc("WriteRows")
|
||||||
|
// if err != nil {
|
||||||
|
// return -1, fmt.Errorf("找不到函数 WriteRows: %v", err)
|
||||||
|
// }
|
||||||
|
// var buffer bytes.Buffer
|
||||||
|
// writer := csv.NewWriter(&buffer)
|
||||||
|
// // 写入所有行
|
||||||
|
// if err := writer.WriteAll(rowsData); err != nil {
|
||||||
|
// return -1, fmt.Errorf("序列化 CSV 数据失败: %v", err)
|
||||||
|
// }
|
||||||
|
// writer.Flush()
|
||||||
|
//
|
||||||
|
// // 转换为 C 字符串
|
||||||
|
// cStr := C.CString(buffer.String())
|
||||||
|
//
|
||||||
|
// ret, _, _ := proc.Call(
|
||||||
|
// uintptr(handle),
|
||||||
|
// uintptr(unsafe.Pointer(cStr)),
|
||||||
|
// uintptr(header))
|
||||||
|
//
|
||||||
|
// freeProc, _ := m.dll.FindProc("FreeCString")
|
||||||
|
// if freeProc != nil {
|
||||||
|
// defer freeProc.Call(uintptr(unsafe.Pointer(cStr)))
|
||||||
|
// }
|
||||||
|
// return int(ret), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 定义请求结构体
|
||||||
|
//type OpenCSVRequest struct {
|
||||||
|
// FilePath string `json:"filePath"`
|
||||||
|
// Delimiter string `json:"delimiter"` // 可以是逗号、分号等字符
|
||||||
|
// HasHeader bool `json:"hasHeader"`
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 定义响应结构体
|
||||||
|
//type OpenCSVResponse struct {
|
||||||
|
// Success bool `json:"success"`
|
||||||
|
// Message string `json:"message"`
|
||||||
|
// Handle int64 `json:"handle,omitempty"`
|
||||||
|
// Error string `json:"error,omitempty"`
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func handleOpenCSVFile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// // 设置响应头
|
||||||
|
// w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
//
|
||||||
|
// // 只允许 POST 请求
|
||||||
|
// if r.Method != http.MethodPost {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "只支持POST请求",
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 1.初始化DLL管理器
|
||||||
|
// dll, err := InitCsvDLL()
|
||||||
|
// if err != nil {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "初始化DLL失败",
|
||||||
|
// Error: err.Error(),
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 2.读取请求体
|
||||||
|
// body, err := io.ReadAll(r.Body)
|
||||||
|
// if err != nil {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "读取请求体失败",
|
||||||
|
// Error: err.Error(),
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// defer r.Body.Close()
|
||||||
|
//
|
||||||
|
// // 3.解析JSON请求
|
||||||
|
// var req OpenCSVRequest
|
||||||
|
// if err := json.Unmarshal(body, &req); err != nil {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "JSON解析失败",
|
||||||
|
// Error: err.Error(),
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 4.验证必填参数
|
||||||
|
// if req.FilePath == "" {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "filePath参数不能为空",
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusBadRequest)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 5. 处理分隔符参数
|
||||||
|
// delimiter := ','
|
||||||
|
// if req.Delimiter != "" {
|
||||||
|
// // 确保分隔符是单个字符
|
||||||
|
// if len(req.Delimiter) == 1 {
|
||||||
|
// delimiter = rune(req.Delimiter[0])
|
||||||
|
// } else {
|
||||||
|
// // 尝试解析常见分隔符的字符串表示
|
||||||
|
// switch req.Delimiter {
|
||||||
|
// case "comma", ",":
|
||||||
|
// delimiter = ','
|
||||||
|
// case "semicolon", ";":
|
||||||
|
// delimiter = ';'
|
||||||
|
// case "tab", "\t":
|
||||||
|
// delimiter = '\t'
|
||||||
|
// case "pipe", "|":
|
||||||
|
// delimiter = '|'
|
||||||
|
// default:
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "无效的分隔符,请使用单个字符或预定义的分隔符名称",
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusBadRequest)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 6. 调用DLL函数
|
||||||
|
// handle, err := dll.OpenCSVFile(req.FilePath, byte(delimiter), req.HasHeader)
|
||||||
|
// if err != nil {
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: false,
|
||||||
|
// Message: "打开CSV文件失败",
|
||||||
|
// Error: err.Error(),
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 7. 返回成功响应
|
||||||
|
// response := OpenCSVResponse{
|
||||||
|
// Success: true,
|
||||||
|
// Message: "CSV文件打开成功",
|
||||||
|
// Handle: handle,
|
||||||
|
// }
|
||||||
|
// w.WriteHeader(http.StatusOK)
|
||||||
|
// json.NewEncoder(w).Encode(response)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 解码从DLL读取的数据
|
||||||
|
//func decodeRowData(buffer []byte, maxBytes int) [][]string {
|
||||||
|
// var rows [][]string
|
||||||
|
// var currentRow []string
|
||||||
|
//
|
||||||
|
// offset := 0
|
||||||
|
// for offset < maxBytes {
|
||||||
|
// if offset+4 > maxBytes {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 读取单元格长度
|
||||||
|
// cellLen := int(uint32(buffer[offset]) |
|
||||||
|
// uint32(buffer[offset+1])<<8 |
|
||||||
|
// uint32(buffer[offset+2])<<16 |
|
||||||
|
// uint32(buffer[offset+3])<<24)
|
||||||
|
// offset += 4
|
||||||
|
//
|
||||||
|
// if cellLen == 0 {
|
||||||
|
// // 行结束
|
||||||
|
// if len(currentRow) > 0 {
|
||||||
|
// rows = append(rows, currentRow)
|
||||||
|
// currentRow = nil
|
||||||
|
// }
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if offset+cellLen > maxBytes {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 读取单元格数据
|
||||||
|
// cell := string(buffer[offset : offset+cellLen])
|
||||||
|
// offset += cellLen
|
||||||
|
// currentRow = append(currentRow, cell)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return rows
|
||||||
|
//}
|
||||||
|
|
||||||
|
//func main() {
|
||||||
|
// 使用dll文件
|
||||||
|
//dll, err := InitCsvDLL()
|
||||||
|
//if err != nil {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
//file, err := dll.CreateOpenCSVFile("csv/taskLog.csv", ',', true)
|
||||||
|
//if err != nil {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
//newRow := []string{"9787115524539", "100.00", "1", "上传成功", ""}
|
||||||
|
//safe, err := dll.UpdateCSVRowSafe(file.Data.HandleID, 9, newRow)
|
||||||
|
//if err != nil {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
//marshal, _ := json.Marshal(safe)
|
||||||
|
//fmt.Println(string(marshal))
|
||||||
|
|
||||||
|
//file, err := GetManager().OpenCSVFile("csv/taskLog1.csv", ',', true)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(file)
|
||||||
|
|
||||||
|
//handle, err := GetManager().getHandle(2)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
// 获取指定数量的行
|
||||||
|
//row, err := handle.readRows(100)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//for _, i := range row {
|
||||||
|
// fmt.Println(i)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 获取总行数
|
||||||
|
//row, err := handle.getTotalRows()
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(row)
|
||||||
|
//newRow := []string{
|
||||||
|
// "9787107267505", "20.00", "10", "上传成功", "877133619369",
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//row, err := GetManager().modifyRow(file, 1, newRow)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(row)
|
||||||
|
|
||||||
|
//http.HandleFunc("/csv/openCSVFile", handleOpenCSVFile)
|
||||||
|
//port := "8080"
|
||||||
|
//server := &http.Server{
|
||||||
|
// Addr: ":" + port,
|
||||||
|
// Handler: nil,
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 4. 优雅关闭设置
|
||||||
|
//done := make(chan bool, 1)
|
||||||
|
//quit := make(chan os.Signal, 1)
|
||||||
|
//signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
//
|
||||||
|
//// 5. 优雅关闭协程
|
||||||
|
//go func() {
|
||||||
|
// <-quit
|
||||||
|
// fmt.Println("\n服务器正在关闭...")
|
||||||
|
//
|
||||||
|
// ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
// defer cancel()
|
||||||
|
//
|
||||||
|
// if err := server.Shutdown(ctx); err != nil {
|
||||||
|
// fmt.Printf("强制关闭服务器: %v\n", err)
|
||||||
|
// }
|
||||||
|
// close(done)
|
||||||
|
//}()
|
||||||
|
//
|
||||||
|
//// 启动服务器
|
||||||
|
//if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
// fmt.Printf("服务器启动失败: %s\n", err)
|
||||||
|
//}
|
||||||
|
//// 7. 等待关闭完成
|
||||||
|
//<-done
|
||||||
|
//fmt.Println("服务器已关闭")
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 主函数 - 测试代码
|
||||||
|
func main() {
|
||||||
|
fmt.Println("=== CSV句柄管理器测试 ===") //7984
|
||||||
|
|
||||||
|
filename := "csv/test1.csv"
|
||||||
|
////dstFile := "csv/taskLog3.csv"
|
||||||
|
//fmt.Printf("1. 创建测试文件: %s\n", filename)
|
||||||
|
//
|
||||||
|
// 创建 Redis 客户端
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: "192.168.101.209:6379", // Redis 地址
|
||||||
|
Password: "", // 密码,没有则为空
|
||||||
|
DB: 0, // 使用的数据库编号
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建上下文
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 测试连接
|
||||||
|
pong, err := rdb.Ping(ctx).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("连接 Redis 失败:", err)
|
||||||
|
}
|
||||||
|
fmt.Println("连接成功:", pong)
|
||||||
|
|
||||||
|
key := "2006557053525397505_2010589232836288513_20260112_1"
|
||||||
|
|
||||||
|
//// 获取值
|
||||||
|
//val, err := rdb.Get(ctx, "1995373681100910593_2010521216979214337_20260112_1").Result()
|
||||||
|
//if err != nil {
|
||||||
|
// log.Fatal("获取键值失败:", err)
|
||||||
|
//}
|
||||||
|
//fmt.Println("key:", val)
|
||||||
|
|
||||||
|
handleID, err := GetManager().OpenCSVFile(filename, ',', true)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("打开文件失败: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("2. 打开文件成功,句柄ID: %d\n", handleID)
|
||||||
|
|
||||||
|
// 测试写入表头
|
||||||
|
header := []string{"ID", "data"}
|
||||||
|
err = GetManager().WriteHeader(handleID, header)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("写入表头失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("写入表头成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取列表所有元素
|
||||||
|
listValues, err := rdb.LRange(ctx, key, 0, -1).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("获取列表失败:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("列表包含 %d 个元素:\n", len(listValues))
|
||||||
|
for i, value := range listValues {
|
||||||
|
|
||||||
|
fmt.Printf(" [%d] %s\n", i, value)
|
||||||
|
|
||||||
|
rowsData := [][]string{
|
||||||
|
{fmt.Sprintf("%d", i), value},
|
||||||
|
}
|
||||||
|
|
||||||
|
totalRows, err := GetManager().WriteRows(handleID, rowsData)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("批量写入数据失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("批量写入数据成功,行数: %d\n", totalRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 也可以检查键是否存在
|
||||||
|
exists, err := rdb.Exists(ctx, key).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("检查键是否存在失败:", err)
|
||||||
|
}
|
||||||
|
if exists == 0 {
|
||||||
|
fmt.Printf("键 '%s' 不存在\n", key)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("键 '%s' 存在\n", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
defer rdb.Close()
|
||||||
|
|
||||||
|
//
|
||||||
|
//// 2. 创建目标文件(部分数据)
|
||||||
|
//dstHandle, err := GetManager().OpenCSVFile(dstFile, ',', true)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("创建目标文件失败: %v", err)
|
||||||
|
//}
|
||||||
|
//// 合并
|
||||||
|
//totalRows, err := GetManager().MergeCSVFilesSimple(handleID, dstHandle, true)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("合并文件失败: %v", err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(totalRows)
|
||||||
|
|
||||||
|
//for i := 0; i < 10000; i++ {
|
||||||
|
// // 打开CSV文件
|
||||||
|
// handleID, err := GetManager().OpenCSVFile(filename, ',', true)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("打开文件失败: %v\n", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("2. 打开文件成功,句柄ID: %d\n", handleID)
|
||||||
|
//
|
||||||
|
// // 测试写入表头
|
||||||
|
// header := []string{"ID", "Name", "Age", "Email"}
|
||||||
|
// err = GetManager().WriteHeader(handleID, header)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("写入表头失败: %v\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Println("写入表头成功")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //// 写入测试数据
|
||||||
|
// //rowsData := [][]string{
|
||||||
|
// // {"1", fmt.Sprintf("张三", i), "25", "zhangsan@example.com"},
|
||||||
|
// // {"2", "李四", "30", "lisi@example.com"},
|
||||||
|
// // {"3", "王五", "28", "wangwu@example.com"},
|
||||||
|
// // {"4", "赵六", "35", "zhaoliu@example.com"},
|
||||||
|
// //}
|
||||||
|
//
|
||||||
|
// // 写入测试数据
|
||||||
|
// rowsData := [][]string{
|
||||||
|
// {fmt.Sprintf("%d", i+100), "张三", fmt.Sprintf("%d", i+1000), fmt.Sprintf("%d", i+10000)},
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// totalRows, err := GetManager().WriteRows(handleID, rowsData)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Printf("批量写入数据失败: %v\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("批量写入数据成功,行数: %d\n", totalRows)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// GetManager().CloseHandle(handleID)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 测试写入表头
|
||||||
|
//header := []string{"ID", "Name", "Age", "Email"}
|
||||||
|
//err = GetManager().WriteHeader(handleID, header)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("写入表头失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Println("写入表头成功")
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 写入测试数据
|
||||||
|
//rowsData := [][]string{
|
||||||
|
// {"1", "张三", "25", "zhangsan@example.com"},
|
||||||
|
// {"2", "李四", "30", "lisi@example.com"},
|
||||||
|
// {"3", "王五", "28", "wangwu@example.com"},
|
||||||
|
// {"4", "赵六", "35", "zhaoliu@example.com"},
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//totalRows, err := GetManager().WriteRows(handleID, rowsData)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("批量写入数据失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("批量写入数据成功,行数: %d\n", totalRows)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 获取写入后的总行数
|
||||||
|
//totalRows, err = GetManager().GetTotalRows(handleID)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取总行数失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("写入后总行数: %d\n", totalRows)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// ============ 测试修改行功能 ============
|
||||||
|
//
|
||||||
|
//fmt.Println("\n3. 测试修改行功能")
|
||||||
|
//
|
||||||
|
//// 修改第1行数据(索引0)
|
||||||
|
//newRow1 := []string{"1", "张三(已修改1)", "26", "zhangsan_updated@example.com"}
|
||||||
|
//err = GetManager().UpdateRow(handleID, 0, newRow1)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("修改第1行数据失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Println("修改第1行数据成功")
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 修改第3行数据(索引2)
|
||||||
|
//newRow3 := []string{"3", "王五(已修改)", "29", "wangwu_updated@example.com"}
|
||||||
|
//err = GetManager().UpdateRow(handleID, 2, newRow3)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("修改第3行数据失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Println("修改第3行数据成功")
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 获取修改后的行数据
|
||||||
|
//fmt.Println("\n4. 获取修改后的行数据")
|
||||||
|
|
||||||
|
//获取第1行
|
||||||
|
//row1, err := GetManager().GetRow(handleID, 10)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取第1行失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("第1行数据: %v\n", row1)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 获取第3行
|
||||||
|
//row3, err := GetManager().GetRow(handleID, 2)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取第3行失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("第3行数据: %v\n", row3)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// ============ 测试批量修改功能 ============
|
||||||
|
//
|
||||||
|
//fmt.Println("\n5. 测试批量修改功能")
|
||||||
|
//
|
||||||
|
//updates := map[int64][]string{
|
||||||
|
// 1: {"2", "李四(批量修改)", "31", "lisi_batch@example.com"},
|
||||||
|
// 3: {"4", "赵六(批量修改)", "36", "zhaoliu_batch@example.com"},
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//updatedCount, err := GetManager().UpdateRows(handleID, updates)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("批量修改失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("批量修改成功,修改了%d行\n", updatedCount)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 验证批量修改结果
|
||||||
|
//row2, err := GetManager().GetRow(handleID, 1)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取第2行失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("第2行数据(批量修改后): %v\n", row2)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//row4, err := GetManager().GetRow(handleID, 3)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取第4行失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("第4行数据(批量修改后): %v\n", row4)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 获取最终总行数
|
||||||
|
//finalTotalRows, err := GetManager().GetTotalRows(handleID)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("获取最终总行数失败: %v\n", err)
|
||||||
|
//} else {
|
||||||
|
// fmt.Printf("最终总行数: %d\n", finalTotalRows)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
//GetManager().closeAllHandles()
|
||||||
|
fmt.Println("\n测试完成!")
|
||||||
|
}
|
||||||
@ -1,449 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/csv"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CsvDLL 代理DLL结构
|
|
||||||
type csvDLL struct {
|
|
||||||
dll *syscall.DLL
|
|
||||||
openCSVFile *syscall.Proc // 打开/创建CSV文件
|
|
||||||
readRows *syscall.Proc // 读取多行数据
|
|
||||||
writeRows *syscall.Proc // 写入/覆盖行数据
|
|
||||||
appendRows *syscall.Proc // 追加行数据
|
|
||||||
getRowCount *syscall.Proc // 获取总行数
|
|
||||||
findRows *syscall.Proc // 搜索行
|
|
||||||
closeCSVFile *syscall.Proc // 关闭CSV文件
|
|
||||||
mergeCSVFiles *syscall.Proc // 合并多个CSV文件
|
|
||||||
getError *syscall.Proc // 获取错误信息
|
|
||||||
updateCSVRowSafe *syscall.Proc
|
|
||||||
createOpenCSVFile *syscall.Proc
|
|
||||||
freeCString *syscall.Proc // 释放C字符串
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化csvDLL
|
|
||||||
func InitCsvDLL() (*csvDLL, error) {
|
|
||||||
dllPath := filepath.Join("csv/dll", "csv.dll")
|
|
||||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("csv DLL 不存在: %s", dllPath)
|
|
||||||
}
|
|
||||||
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
|
||||||
return nil, fmt.Errorf("加载csv DLL 失败: %s", err)
|
|
||||||
} else {
|
|
||||||
return &csvDLL{
|
|
||||||
dll: dll,
|
|
||||||
openCSVFile: dll.MustFindProc("OpenCSVFile"),
|
|
||||||
updateCSVRowSafe: dll.MustFindProc("UpdateCSVRowSafe"),
|
|
||||||
readRows: dll.MustFindProc("ReadRows"),
|
|
||||||
writeRows: dll.MustFindProc("WriteRows"),
|
|
||||||
appendRows: dll.MustFindProc("AppendRows"),
|
|
||||||
getRowCount: dll.MustFindProc("GetRowCount"),
|
|
||||||
findRows: dll.MustFindProc("FindRows"),
|
|
||||||
closeCSVFile: dll.MustFindProc("CloseCSVFile"),
|
|
||||||
mergeCSVFiles: dll.MustFindProc("MergeCSVFiles"),
|
|
||||||
createOpenCSVFile: dll.MustFindProc("CreateOpenCSVFile"),
|
|
||||||
getError: dll.MustFindProc("GetError"),
|
|
||||||
freeCString: dll.MustFindProc("FreeCString"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cStr 获取C字符串
|
|
||||||
func (m *csvDLL) cStr(p uintptr) string {
|
|
||||||
if p == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
b := []byte{}
|
|
||||||
for i := uintptr(0); ; i++ {
|
|
||||||
c := *(*byte)(unsafe.Pointer(p + i))
|
|
||||||
if c == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b = append(b, c)
|
|
||||||
}
|
|
||||||
s := string(b)
|
|
||||||
if m.freeCString != nil {
|
|
||||||
m.freeCString.Call(p)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开/创建CSV文件
|
|
||||||
func (m *csvDLL) OpenCSVFile(filePath string, delimiter byte, hasHeader bool) (int64, error) {
|
|
||||||
proc, err := m.dll.FindProc("OpenCSVFile")
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("找不到函数 OpenCSVFile: %v", err)
|
|
||||||
}
|
|
||||||
filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
|
||||||
hasHeaderInt := 0
|
|
||||||
if hasHeader {
|
|
||||||
hasHeaderInt = 1
|
|
||||||
}
|
|
||||||
info, _, _ := proc.Call(
|
|
||||||
uintptr(unsafe.Pointer(filePathPtr)),
|
|
||||||
uintptr(delimiter),
|
|
||||||
uintptr(hasHeaderInt))
|
|
||||||
|
|
||||||
return int64(info), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSV响应结构体
|
|
||||||
type CSVResponses struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
Data CSVData `json:"data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CSVData struct {
|
|
||||||
HandleID int64 `json:"handleID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *csvDLL) CreateOpenCSVFile(filePath string, delimiter byte, hasHeader bool) (*CSVResponses, error) {
|
|
||||||
proc, err := m.dll.FindProc("CreateOpenCSVFile")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("找不到函数 CreateOpenCSVFile: %v", err)
|
|
||||||
}
|
|
||||||
filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
|
||||||
hasHeaderInt := 0
|
|
||||||
if hasHeader {
|
|
||||||
hasHeaderInt = 1
|
|
||||||
}
|
|
||||||
info, _, _ := proc.Call(
|
|
||||||
uintptr(unsafe.Pointer(filePathPtr)),
|
|
||||||
uintptr(delimiter),
|
|
||||||
uintptr(hasHeaderInt))
|
|
||||||
var csvResponse CSVResponses
|
|
||||||
str := m.cStr(info)
|
|
||||||
if err := json.Unmarshal([]byte(str), &csvResponse); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &csvResponse, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *csvDLL) UpdateCSVRowSafe(handleID int64, rowNum int, newRow []string) (*CSVResponses, error) {
|
|
||||||
proc, err := m.dll.FindProc("UpdateCSVRowSafe")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("找不到函数 UpdateCSVRowSafe: %v", err)
|
|
||||||
}
|
|
||||||
jsonString, _ := json.Marshal(newRow)
|
|
||||||
newRowPtr, _ := syscall.BytePtrFromString(string(jsonString))
|
|
||||||
info, _, _ := proc.Call(
|
|
||||||
uintptr(handleID),
|
|
||||||
uintptr(rowNum),
|
|
||||||
uintptr(unsafe.Pointer(newRowPtr)))
|
|
||||||
var csvResponse CSVResponses
|
|
||||||
str := m.cStr(info)
|
|
||||||
if err := json.Unmarshal([]byte(str), &csvResponse); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &csvResponse, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//// 读取多行数据
|
|
||||||
//func (m *csvDLL) ReadRows(handle int64) ([]string, error) {
|
|
||||||
// proc, err := m.dll.FindProc("ReadRows")
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, fmt.Errorf("找不到函数 ReadRows: %v", err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 写入/覆盖行数据
|
|
||||||
func (m *csvDLL) WriteRows(handle int64, rowsData [][]string, header int) (int, error) {
|
|
||||||
proc, err := m.dll.FindProc("WriteRows")
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("找不到函数 WriteRows: %v", err)
|
|
||||||
}
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
writer := csv.NewWriter(&buffer)
|
|
||||||
// 写入所有行
|
|
||||||
if err := writer.WriteAll(rowsData); err != nil {
|
|
||||||
return -1, fmt.Errorf("序列化 CSV 数据失败: %v", err)
|
|
||||||
}
|
|
||||||
writer.Flush()
|
|
||||||
|
|
||||||
// 转换为 C 字符串
|
|
||||||
cStr := C.CString(buffer.String())
|
|
||||||
|
|
||||||
ret, _, _ := proc.Call(
|
|
||||||
uintptr(handle),
|
|
||||||
uintptr(unsafe.Pointer(cStr)),
|
|
||||||
uintptr(header))
|
|
||||||
|
|
||||||
freeProc, _ := m.dll.FindProc("FreeCString")
|
|
||||||
if freeProc != nil {
|
|
||||||
defer freeProc.Call(uintptr(unsafe.Pointer(cStr)))
|
|
||||||
}
|
|
||||||
return int(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义请求结构体
|
|
||||||
type OpenCSVRequest struct {
|
|
||||||
FilePath string `json:"filePath"`
|
|
||||||
Delimiter string `json:"delimiter"` // 可以是逗号、分号等字符
|
|
||||||
HasHeader bool `json:"hasHeader"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义响应结构体
|
|
||||||
type OpenCSVResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Handle int64 `json:"handle,omitempty"`
|
|
||||||
Error string `json:"error,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleOpenCSVFile(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// 设置响应头
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
|
|
||||||
// 只允许 POST 请求
|
|
||||||
if r.Method != http.MethodPost {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "只支持POST请求",
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.初始化DLL管理器
|
|
||||||
dll, err := InitCsvDLL()
|
|
||||||
if err != nil {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "初始化DLL失败",
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.读取请求体
|
|
||||||
body, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "读取请求体失败",
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer r.Body.Close()
|
|
||||||
|
|
||||||
// 3.解析JSON请求
|
|
||||||
var req OpenCSVRequest
|
|
||||||
if err := json.Unmarshal(body, &req); err != nil {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "JSON解析失败",
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4.验证必填参数
|
|
||||||
if req.FilePath == "" {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "filePath参数不能为空",
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 处理分隔符参数
|
|
||||||
delimiter := ','
|
|
||||||
if req.Delimiter != "" {
|
|
||||||
// 确保分隔符是单个字符
|
|
||||||
if len(req.Delimiter) == 1 {
|
|
||||||
delimiter = rune(req.Delimiter[0])
|
|
||||||
} else {
|
|
||||||
// 尝试解析常见分隔符的字符串表示
|
|
||||||
switch req.Delimiter {
|
|
||||||
case "comma", ",":
|
|
||||||
delimiter = ','
|
|
||||||
case "semicolon", ";":
|
|
||||||
delimiter = ';'
|
|
||||||
case "tab", "\t":
|
|
||||||
delimiter = '\t'
|
|
||||||
case "pipe", "|":
|
|
||||||
delimiter = '|'
|
|
||||||
default:
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "无效的分隔符,请使用单个字符或预定义的分隔符名称",
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 调用DLL函数
|
|
||||||
handle, err := dll.OpenCSVFile(req.FilePath, byte(delimiter), req.HasHeader)
|
|
||||||
if err != nil {
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "打开CSV文件失败",
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. 返回成功响应
|
|
||||||
response := OpenCSVResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "CSV文件打开成功",
|
|
||||||
Handle: handle,
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解码从DLL读取的数据
|
|
||||||
func decodeRowData(buffer []byte, maxBytes int) [][]string {
|
|
||||||
var rows [][]string
|
|
||||||
var currentRow []string
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
for offset < maxBytes {
|
|
||||||
if offset+4 > maxBytes {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取单元格长度
|
|
||||||
cellLen := int(uint32(buffer[offset]) |
|
|
||||||
uint32(buffer[offset+1])<<8 |
|
|
||||||
uint32(buffer[offset+2])<<16 |
|
|
||||||
uint32(buffer[offset+3])<<24)
|
|
||||||
offset += 4
|
|
||||||
|
|
||||||
if cellLen == 0 {
|
|
||||||
// 行结束
|
|
||||||
if len(currentRow) > 0 {
|
|
||||||
rows = append(rows, currentRow)
|
|
||||||
currentRow = nil
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if offset+cellLen > maxBytes {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取单元格数据
|
|
||||||
cell := string(buffer[offset : offset+cellLen])
|
|
||||||
offset += cellLen
|
|
||||||
currentRow = append(currentRow, cell)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
//func main() {
|
|
||||||
// 使用dll文件
|
|
||||||
//dll, err := InitCsvDLL()
|
|
||||||
//if err != nil {
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
//file, err := dll.CreateOpenCSVFile("csv/taskLog.csv", ',', true)
|
|
||||||
//if err != nil {
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
//newRow := []string{"9787115524539", "100.00", "1", "上传成功", ""}
|
|
||||||
//safe, err := dll.UpdateCSVRowSafe(file.Data.HandleID, 9, newRow)
|
|
||||||
//if err != nil {
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
//marshal, _ := json.Marshal(safe)
|
|
||||||
//fmt.Println(string(marshal))
|
|
||||||
|
|
||||||
//file, err := GetManager().OpenCSVFile("csv/taskLog1.csv", ',', true)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//fmt.Println(file)
|
|
||||||
|
|
||||||
//handle, err := GetManager().getHandle(2)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
// 获取指定数量的行
|
|
||||||
//row, err := handle.readRows(100)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//for _, i := range row {
|
|
||||||
// fmt.Println(i)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// 获取总行数
|
|
||||||
//row, err := handle.getTotalRows()
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//fmt.Println(row)
|
|
||||||
//newRow := []string{
|
|
||||||
// "9787107267505", "20.00", "10", "上传成功", "877133619369",
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//row, err := GetManager().modifyRow(file, 1, newRow)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//fmt.Println(row)
|
|
||||||
|
|
||||||
//http.HandleFunc("/csv/openCSVFile", handleOpenCSVFile)
|
|
||||||
//port := "8080"
|
|
||||||
//server := &http.Server{
|
|
||||||
// Addr: ":" + port,
|
|
||||||
// Handler: nil,
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// 4. 优雅关闭设置
|
|
||||||
//done := make(chan bool, 1)
|
|
||||||
//quit := make(chan os.Signal, 1)
|
|
||||||
//signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
//
|
|
||||||
//// 5. 优雅关闭协程
|
|
||||||
//go func() {
|
|
||||||
// <-quit
|
|
||||||
// fmt.Println("\n服务器正在关闭...")
|
|
||||||
//
|
|
||||||
// ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
// defer cancel()
|
|
||||||
//
|
|
||||||
// if err := server.Shutdown(ctx); err != nil {
|
|
||||||
// fmt.Printf("强制关闭服务器: %v\n", err)
|
|
||||||
// }
|
|
||||||
// close(done)
|
|
||||||
//}()
|
|
||||||
//
|
|
||||||
//// 启动服务器
|
|
||||||
//if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
||||||
// fmt.Printf("服务器启动失败: %s\n", err)
|
|
||||||
//}
|
|
||||||
//// 7. 等待关闭完成
|
|
||||||
//<-done
|
|
||||||
//fmt.Println("服务器已关闭")
|
|
||||||
//}
|
|
||||||
BIN
csv/dll/csv.dll
BIN
csv/dll/csv.dll
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
//
|
||||||
///*
|
///*
|
||||||
//#include <stdlib.h>
|
//#include <stdlib.h>
|
||||||
//*/
|
//*/
|
||||||
@ -96,6 +97,31 @@ package main
|
|||||||
// // 创建CSV全局管理器
|
// // 创建CSV全局管理器
|
||||||
// mgr := getManager()
|
// mgr := getManager()
|
||||||
//
|
//
|
||||||
|
// // 先检查文件是否已经打开
|
||||||
|
// var existingHandle int64 = -1
|
||||||
|
// mgr.files.Range(func(key, value interface{}) bool {
|
||||||
|
// file := value.(*CSVFile)
|
||||||
|
// // 比较文件路径(使用绝对路径确保一致性)
|
||||||
|
// absFilename, _ := filepath.Abs(filename)
|
||||||
|
// absExisting, _ := filepath.Abs(file.filename)
|
||||||
|
//
|
||||||
|
// if absFilename == absExisting {
|
||||||
|
// fmt.Println("absFilename", absFilename)
|
||||||
|
// fmt.Println("absExisting", absExisting)
|
||||||
|
// fmt.Println("bool", absFilename == absExisting)
|
||||||
|
//
|
||||||
|
// existingHandle = key.(int64)
|
||||||
|
// return false // 停止遍历
|
||||||
|
// }
|
||||||
|
// return true // 继续遍历
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // 如果文件已经打开,返回现有句柄
|
||||||
|
// if existingHandle != -1 {
|
||||||
|
// fmt.Printf("文件已打开,返回现有句柄: %d\n", existingHandle)
|
||||||
|
// return existingHandle
|
||||||
|
// }
|
||||||
|
//
|
||||||
// // 生成句柄
|
// // 生成句柄
|
||||||
// handle := atomic.AddInt64(&mgr.nextHandle, 1)
|
// handle := atomic.AddInt64(&mgr.nextHandle, 1)
|
||||||
//
|
//
|
||||||
@ -111,7 +137,6 @@ package main
|
|||||||
//
|
//
|
||||||
// // 存储到管理器
|
// // 存储到管理器
|
||||||
// mgr.files.Store(handle, file)
|
// mgr.files.Store(handle, file)
|
||||||
//
|
|
||||||
// return handle
|
// return handle
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
@ -548,7 +573,10 @@ package main
|
|||||||
// // 异步保存
|
// // 异步保存
|
||||||
// go file.saveAsync()
|
// go file.saveAsync()
|
||||||
//
|
//
|
||||||
// return 0
|
// // 获取总行数
|
||||||
|
// count := getRowCount(handle)
|
||||||
|
// fmt.Println("count:", count)
|
||||||
|
// return int(count)
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//// 打开/创建CSV文件(句柄)
|
//// 打开/创建CSV文件(句柄)
|
||||||
@ -705,7 +733,9 @@ package main
|
|||||||
// file := val.(*CSVFile)
|
// file := val.(*CSVFile)
|
||||||
// file.mu.RLock()
|
// file.mu.RLock()
|
||||||
// defer file.mu.RUnlock()
|
// defer file.mu.RUnlock()
|
||||||
//
|
// if file.header != nil {
|
||||||
|
// return int64(len(file.data) + 1)
|
||||||
|
// }
|
||||||
// return int64(len(file.data))
|
// return int64(len(file.data))
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
@ -997,10 +1027,10 @@ package main
|
|||||||
// // 从管理器移除
|
// // 从管理器移除
|
||||||
// mgr.files.Delete(handle)
|
// mgr.files.Delete(handle)
|
||||||
//
|
//
|
||||||
// // 安全关闭通道(如果存在)
|
// //// 安全关闭通道(如果存在)
|
||||||
// if file.done != nil {
|
// //if file.done != nil {
|
||||||
// close(file.done)
|
// // close(file.done)
|
||||||
// }
|
// //}
|
||||||
//
|
//
|
||||||
// return 0
|
// return 0
|
||||||
//}
|
//}
|
||||||
@ -1144,7 +1174,12 @@ package main
|
|||||||
//func WriteRows(handle C.longlong, rowsData *C.char, header C.int) C.int {
|
//func WriteRows(handle C.longlong, rowsData *C.char, header C.int) C.int {
|
||||||
// goData := C.GoString(rowsData)
|
// goData := C.GoString(rowsData)
|
||||||
// goHeader := int(header)
|
// goHeader := int(header)
|
||||||
// data := parseSimpleTable(goData)
|
// //data := parseSimpleTable(goData)
|
||||||
|
// var data [][]string
|
||||||
|
// err := json.Unmarshal([]byte(goData), &data)
|
||||||
|
// if err != nil {
|
||||||
|
// setError(err.Error())
|
||||||
|
// }
|
||||||
// result := writeRows(int64(handle), data, goHeader)
|
// result := writeRows(int64(handle), data, goHeader)
|
||||||
// return C.int(result)
|
// return C.int(result)
|
||||||
//}
|
//}
|
||||||
@ -1224,3 +1259,36 @@ package main
|
|||||||
//// main 函数是必需的,即使为空
|
//// main 函数是必需的,即使为空
|
||||||
//func main() {
|
//func main() {
|
||||||
//}
|
//}
|
||||||
|
//
|
||||||
|
//// 编码多行数据
|
||||||
|
//func encodeRowsData(rows [][]string) []byte {
|
||||||
|
// var result []byte
|
||||||
|
// for _, row := range rows {
|
||||||
|
// rowData := encodeRowData(row)
|
||||||
|
// result = append(result, rowData...)
|
||||||
|
// }
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// 编码行数据为DLL期望的格式
|
||||||
|
//func encodeRowData(row []string) []byte {
|
||||||
|
// var result []byte
|
||||||
|
//
|
||||||
|
// for _, cell := range row {
|
||||||
|
// // 写入4字节长度
|
||||||
|
// length := len(cell)
|
||||||
|
// result = append(result,
|
||||||
|
// byte(length&0xFF),
|
||||||
|
// byte((length>>8)&0xFF),
|
||||||
|
// byte((length>>16)&0xFF),
|
||||||
|
// byte((length>>24)&0xFF))
|
||||||
|
//
|
||||||
|
// // 写入单元格数据
|
||||||
|
// result = append(result, []byte(cell)...)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 写入行结束标记 (4个0字节)
|
||||||
|
// result = append(result, 0, 0, 0, 0)
|
||||||
|
//
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
|||||||
BIN
dll/csv.dll
BIN
dll/csv.dll
Binary file not shown.
134
dll/csv.h
134
dll/csv.h
@ -1,134 +0,0 @@
|
|||||||
/* 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 "csv.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) long long int InitCSVManager(void);
|
|
||||||
|
|
||||||
// OpenCSVFile 打开/创建CSV文件
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) long long int OpenCSVFile(char* filename, char delimiter, int hasHeader);
|
|
||||||
|
|
||||||
// ReadRows 读取多行数据
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) long long int ReadRows(long long int handle, long long int startRow, long long int count, char* buffer, int bufferSize);
|
|
||||||
|
|
||||||
// WriteRows 写入/覆盖行数据
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int WriteRows(long long int handle, char* rowsData, int dataSize, long long int rowCount);
|
|
||||||
|
|
||||||
// AppendRows 追加行数据
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int AppendRows(long long int handle, char* rowsData, int dataSize, long long int rowCount);
|
|
||||||
|
|
||||||
// GetRowCount 获取总行数
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) long long int GetRowCount(long long int handle);
|
|
||||||
|
|
||||||
// FindRows 搜索行
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) long long int FindRows(long long int handle, char* searchText, long long int columnIndex, char* resultBuffer, int bufferSize, long long int maxResults);
|
|
||||||
|
|
||||||
// MergeCSVFiles 合并多个CSV文件(线程安全)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) long long int MergeCSVFiles(long long int* handlesPtr, int handlesCount, char* outputFilename, char delimiter, int hasHeader);
|
|
||||||
|
|
||||||
// CloseCSVFile 关闭文件
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int CloseCSVFile(long long int handle);
|
|
||||||
|
|
||||||
// GetError 获取错误信息
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int GetError(char* buffer, int bufferSize);
|
|
||||||
|
|
||||||
// 导出函数:释放C字符串内存
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) void FreeCString(char* str);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
BIN
dll/excel.dll
BIN
dll/excel.dll
Binary file not shown.
141
dll/excel.h
141
dll/excel.h
@ -1,141 +0,0 @@
|
|||||||
/* 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>
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
|
|
||||||
// 创建新的Excel管理器并返回指针
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) uintptr_t NewExcelManagerInstance(void);
|
|
||||||
|
|
||||||
// 释放Excel管理器
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) void FreeExcelManager(uintptr_t handle);
|
|
||||||
|
|
||||||
// 读取Excel数据
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int ReadExcelData(uintptr_t handle, char* filename, char* sheet, char** result);
|
|
||||||
|
|
||||||
// 批量写入数据到Excel文件
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int WriteBatchData(uintptr_t handle, char* filename, char* sheet, char* cells, char* values, int count);
|
|
||||||
|
|
||||||
// 追加数据到Excel文件末尾
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int AppendDataToExcel(uintptr_t handle, char* filename, char* sheet, char* values, int count);
|
|
||||||
|
|
||||||
// 搜索包含关键字的单元格
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int SearchByKeyword(uintptr_t handle, char* filename, char* sheet, char* keyword, char** result);
|
|
||||||
|
|
||||||
// 搜索整行包含关键字的行
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int SearchRowsByKeyword(uintptr_t handle, char* filename, char* sheet, char* keyword, char** result);
|
|
||||||
|
|
||||||
// 创建新文件并写入数据
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int CreateAndWriteExcel(uintptr_t handle, char* filename, char* sheet, char* rowsData);
|
|
||||||
|
|
||||||
// 增强版合并Excel文件(支持指定文件列表)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int MergeExcelFilesEx(uintptr_t handle, char* sourceDir, char* specificFiles, char* outputFile, char* sheetName, int mergeByColumn, int includeHeaders, int skipEmptyRows, char* filePattern, char* sourceSheet, int addSourceColumn, int addIndexColumn);
|
|
||||||
|
|
||||||
// 并行合并Excel文件(增强版)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int MergeExcelFilesParallelEx(uintptr_t handle, char* sourceDir, char** specificFiles, int fileCount, char* outputFile, char* sheetName, int includeHeaders, int skipEmptyRows, char* filePattern, char* sourceSheet, int addSourceColumn, int addIndexColumn, int workers);
|
|
||||||
|
|
||||||
// 合并同一文件中的多个sheet
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) int MergeSheetsInFile(uintptr_t handle, char* filename, char* outputFile, char* targetSheetName);
|
|
||||||
|
|
||||||
// 释放C字符串
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) void FreeCString(char* str);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
146
dll/kongfz.h
146
dll/kongfz.h
@ -1,146 +0,0 @@
|
|||||||
/* 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* OutGetUserMsg(char* token);
|
|
||||||
|
|
||||||
// 获取商品模版(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* itemId, char* proxy);
|
|
||||||
|
|
||||||
// 获取商品列表-已登的店铺(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsListMsgFromSelfShop(char* token, char* proxy, char* itemSn, char* priceMin, char* priceMax, char* startCreateTime, char* endCreateTime, char* requestType, int isItemSnEqual, int page, int size);
|
|
||||||
|
|
||||||
// 删除商品-已登的店铺(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
|
|
||||||
|
|
||||||
// 新增商品(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData);
|
|
||||||
|
|
||||||
// 获取图片URL(官图和拍图)带有店铺过滤
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* isbn, int shopId, char* proxy, int isLiveImage, int isReturnMsg);
|
|
||||||
|
|
||||||
// 获取商品图片(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
|
|
||||||
|
|
||||||
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int retPrice, 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
|
|
||||||
124
dll/proxy.h
124
dll/proxy.h
@ -1,124 +0,0 @@
|
|||||||
/* 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 "proxy.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);
|
|
||||||
|
|
||||||
// 导出函数:查询机器码
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* GetMachineCode(char* tailCardSecret);
|
|
||||||
|
|
||||||
// 导出函数:充值卡密
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* RechargeCard(char* tailCardSecret, char* machineCode);
|
|
||||||
|
|
||||||
// 导出函数:获取代理服务器列表
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* GetProxies(char* machineCode);
|
|
||||||
|
|
||||||
// 导出函数:检查卡密是否过期
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* CheckTailCardSecretExpired(char* tailCardSecret);
|
|
||||||
|
|
||||||
// 导出函数:初始化代理管理器
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* InitProxyManager(char* serversJson, char* username, char* password, char* tailCardSecret, char* proxyType);
|
|
||||||
|
|
||||||
// 导出函数:释放C字符串内存
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) void FreeCString(char* str);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
105
erp/erp.go
Normal file
105
erp/erp.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/parnurzeal/gorequest"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 接口转发
|
||||||
|
* param requestMethod[string] 请求方式 现在只支持GET\POST\PUT
|
||||||
|
* param erpUrl[string] erpURl地址
|
||||||
|
* param requestJson[string] 请求json字符串
|
||||||
|
* return 订单同步结构体字符串,错误信息
|
||||||
|
*/
|
||||||
|
func interfaceForward(requestMethod, erpUrl string, requestJson string) (string, error) {
|
||||||
|
var reqJson map[string]interface{}
|
||||||
|
err := json.Unmarshal([]byte(requestJson), &reqJson)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("requestJson JSON解析失败: %v", err)
|
||||||
|
}
|
||||||
|
request := gorequest.New()
|
||||||
|
switch requestMethod {
|
||||||
|
case "POST":
|
||||||
|
resp, body, errs := request.Post(erpUrl).
|
||||||
|
Type("multipart").
|
||||||
|
Send(reqJson).
|
||||||
|
End()
|
||||||
|
if errs != nil {
|
||||||
|
return "", fmt.Errorf("请求失败: %v", errs)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
case "GET":
|
||||||
|
var erpNewUrl string
|
||||||
|
erpUrl = erpUrl + "?"
|
||||||
|
count := 0
|
||||||
|
total := len(reqJson)
|
||||||
|
|
||||||
|
for k, v := range reqJson {
|
||||||
|
fmt.Println(k, v)
|
||||||
|
erpNewUrl = fmt.Sprintf("%s=%s", k, v)
|
||||||
|
erpUrl = erpUrl + erpNewUrl
|
||||||
|
|
||||||
|
count++
|
||||||
|
if count < total {
|
||||||
|
erpUrl = erpUrl + "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(erpNewUrl)
|
||||||
|
resp, body, errs := request.Get(erpUrl).
|
||||||
|
End()
|
||||||
|
if errs != nil {
|
||||||
|
return "", fmt.Errorf("请求失败: %v", errs)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
case "PUT":
|
||||||
|
resp, body, errs := request.Put(erpUrl).
|
||||||
|
Send(requestJson).
|
||||||
|
End()
|
||||||
|
if errs != nil {
|
||||||
|
return "", fmt.Errorf("请求失败: %v", errs)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("请求失败,请求方式: %s", requestMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceForward 接口转发
|
||||||
|
//
|
||||||
|
//export InterfaceForward
|
||||||
|
func InterfaceForward(requestMethod, erpUrl *C.char, requestJson *C.char) *C.char {
|
||||||
|
goRequestMethod := C.GoString(requestMethod)
|
||||||
|
goErpUrl := C.GoString(erpUrl)
|
||||||
|
goEequestJson := C.GoString(requestJson)
|
||||||
|
synchronization, err := interfaceForward(goRequestMethod, goErpUrl, goEequestJson)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(err.Error())
|
||||||
|
}
|
||||||
|
return C.CString(synchronization)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeCString 释放C字符串内存
|
||||||
|
//
|
||||||
|
//export FreeCString
|
||||||
|
func FreeCString(str *C.char) {
|
||||||
|
C.free(unsafe.Pointer(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空main函数,编译DLL时需要
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
2088
es/main.go
2088
es/main.go
File diff suppressed because it is too large
Load Diff
13
go.mod
13
go.mod
@ -4,14 +4,17 @@ go 1.25
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.10.3
|
github.com/PuerkitoBio/goquery v1.10.3
|
||||||
github.com/chromedp/chromedp v0.14.2
|
github.com/boombuler/barcode v1.1.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/elastic/go-elasticsearch/v8 v8.19.0
|
github.com/elastic/go-elasticsearch/v8 v8.19.0
|
||||||
|
github.com/fogleman/gg v1.3.0
|
||||||
github.com/gin-gonic/gin v1.11.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/makiuchi-d/gozxing v0.1.1
|
github.com/makiuchi-d/gozxing v0.1.1
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/parnurzeal/gorequest v0.3.0
|
github.com/parnurzeal/gorequest v0.3.0
|
||||||
|
github.com/redis/go-redis/v9 v9.17.2
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/xuri/excelize/v2 v2.10.0
|
github.com/xuri/excelize/v2 v2.10.0
|
||||||
golang.org/x/image v0.25.0
|
golang.org/x/image v0.25.0
|
||||||
@ -23,22 +26,18 @@ require (
|
|||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/bytedance/sonic v1.14.0 // indirect
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
|
||||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.7.0 // indirect
|
github.com/elastic/elastic-transport-go/v8 v8.7.0 // indirect
|
||||||
github.com/elazarl/goproxy v1.7.2 // indirect
|
github.com/elazarl/goproxy v1.7.2 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
github.com/go-playground/validator/v10 v10.27.0 // 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/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
|||||||
34
go.sum
34
go.sum
@ -4,21 +4,25 @@ github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiU
|
|||||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
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 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||||
|
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||||
|
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||||
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||||
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
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/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/elastic/elastic-transport-go/v8 v8.7.0 h1:OgTneVuXP2uip4BA658Xi6Hfw+PeIOod2rY3GVMGoVE=
|
github.com/elastic/elastic-transport-go/v8 v8.7.0 h1:OgTneVuXP2uip4BA658Xi6Hfw+PeIOod2rY3GVMGoVE=
|
||||||
@ -27,14 +31,14 @@ github.com/elastic/go-elasticsearch/v8 v8.19.0 h1:VmfBLNRORY7RZL+9hTxBD97ehl9H8N
|
|||||||
github.com/elastic/go-elasticsearch/v8 v8.19.0/go.mod h1:F3j9e+BubmKvzvLjNui/1++nJuJxbkhHefbaT0kFKGY=
|
github.com/elastic/go-elasticsearch/v8 v8.19.0/go.mod h1:F3j9e+BubmKvzvLjNui/1++nJuJxbkhHefbaT0kFKGY=
|
||||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@ -50,16 +54,12 @@ github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHO
|
|||||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
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/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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
@ -72,8 +72,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
|
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
|
||||||
@ -88,8 +86,6 @@ 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/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
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 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
|
||||||
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
@ -102,6 +98,8 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
|||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||||
|
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||||
|
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||||
|
|||||||
3302
goroutine-pool/goroutine_pool.go
Normal file
3302
goroutine-pool/goroutine_pool.go
Normal file
File diff suppressed because it is too large
Load Diff
949
goroutine-pool/goroutine_pool_test.go
Normal file
949
goroutine-pool/goroutine_pool_test.go
Normal file
@ -0,0 +1,949 @@
|
|||||||
|
package goroutine_pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ==================== 测试辅助函数 ====================
|
||||||
|
|
||||||
|
// 测试任务函数1: 字符串处理
|
||||||
|
func testStringTask(param string) (string, error) {
|
||||||
|
time.Sleep(10 * time.Millisecond) // 模拟处理时间
|
||||||
|
return "Processed: " + param, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试任务函数2: 数值计算
|
||||||
|
func testNumberTask(param string) (string, error) {
|
||||||
|
n, err := strconv.Atoi(param)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid number: %s", param)
|
||||||
|
}
|
||||||
|
time.Sleep(5 * time.Millisecond) // 模拟计算时间
|
||||||
|
return strconv.Itoa(n * n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试任务函数3: 错误测试
|
||||||
|
func testErrorTask(param string) (string, error) {
|
||||||
|
time.Sleep(3 * time.Millisecond)
|
||||||
|
return "", fmt.Errorf("simulated error for: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试任务函数4: 长时任务
|
||||||
|
func testLongTask(param string) (string, error) {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
return "Long task completed: " + param, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试任务函数5: 内存密集型
|
||||||
|
func testMemoryIntensiveTask(param string) (string, error) {
|
||||||
|
// 分配一些内存
|
||||||
|
data := make([]byte, 1024*1024) // 1MB
|
||||||
|
for i := range data {
|
||||||
|
data[i] = byte(i % 256)
|
||||||
|
}
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
return fmt.Sprintf("Allocated %d bytes", len(data)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 完整功能测试 ====================
|
||||||
|
|
||||||
|
// TestFullFunctionality 测试完整功能
|
||||||
|
func TestFullFunctionality(t *testing.T) {
|
||||||
|
fmt.Println("========== 开始完整功能测试 ==========")
|
||||||
|
|
||||||
|
// 1. 初始化池
|
||||||
|
fmt.Println("\n1. 初始化协程池...")
|
||||||
|
err := Initialize(3, 10, `{
|
||||||
|
"minWorkers": 3,
|
||||||
|
"maxWorkers": 10,
|
||||||
|
"queueSize": 50,
|
||||||
|
"taskTimeoutMs": 30000,
|
||||||
|
"workerIdleTimeoutMs": 60000,
|
||||||
|
"memoryLimitMB": 512,
|
||||||
|
"shutdownTimeoutMs": 10000,
|
||||||
|
"enableMetrics": true,
|
||||||
|
"enableAutoScaling": true,
|
||||||
|
"priorityQueue": true
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("✓ 池初始化成功")
|
||||||
|
|
||||||
|
// 等待池完全启动
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 2. 注册任务函数
|
||||||
|
fmt.Println("\n2. 注册任务函数...")
|
||||||
|
RegisterFunction("string_task", testStringTask)
|
||||||
|
RegisterFunction("number_task", testNumberTask)
|
||||||
|
RegisterFunction("error_task", testErrorTask)
|
||||||
|
RegisterFunction("long_task", testLongTask)
|
||||||
|
RegisterFunction("memory_task", testMemoryIntensiveTask)
|
||||||
|
fmt.Println("✓ 任务函数注册完成")
|
||||||
|
|
||||||
|
// 3. 基础任务提交测试
|
||||||
|
fmt.Println("\n3. 基础任务提交测试...")
|
||||||
|
testBasicTasks(t)
|
||||||
|
|
||||||
|
//// 4. 并发测试
|
||||||
|
//fmt.Println("\n4. 并发任务测试...")
|
||||||
|
//testConcurrentTasks(t)
|
||||||
|
//
|
||||||
|
//// 5. 工作者控制测试
|
||||||
|
//fmt.Println("\n5. 工作者控制测试...")
|
||||||
|
//testWorkerControl(t)
|
||||||
|
|
||||||
|
//// 6. 池控制测试
|
||||||
|
//fmt.Println("\n6. 池控制测试...")
|
||||||
|
//testPoolControl(t)
|
||||||
|
|
||||||
|
//// 7. 优先级测试
|
||||||
|
//fmt.Println("\n7. 优先级任务测试...")
|
||||||
|
//testPriorityTasks(t)
|
||||||
|
|
||||||
|
//// 8. 错误处理测试
|
||||||
|
//fmt.Println("\n8. 错误处理测试...")
|
||||||
|
//testErrorHandling(t)
|
||||||
|
|
||||||
|
////9. 内存和性能测试
|
||||||
|
//fmt.Println("\n9. 内存和性能测试...")
|
||||||
|
//testMemoryAndPerformance(t)
|
||||||
|
|
||||||
|
// 10. 优雅关闭测试
|
||||||
|
fmt.Println("\n10. 优雅关闭测试...")
|
||||||
|
testGracefulShutdown(t)
|
||||||
|
//
|
||||||
|
//// 11. 重新初始化测试
|
||||||
|
//fmt.Println("\n11. 重新初始化测试...")
|
||||||
|
//testReinitialization(t)
|
||||||
|
|
||||||
|
fmt.Println("\n========== 所有测试完成 ==========")
|
||||||
|
|
||||||
|
// 确保最终清理
|
||||||
|
if IsInitialized() {
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testBasicTasks 测试基础任务功能
|
||||||
|
func testBasicTasks(t *testing.T) {
|
||||||
|
// 提交单个任务
|
||||||
|
taskID, err := SubmitTask(0, "string_task", "Hello World", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("提交任务失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 任务提交成功: ID=%d\n", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待任务完成
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
// 获取任务信息
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取任务信息失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 任务信息: ID=%v, 状态=%v\n", info["id"], info["status"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量提交任务
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int) {
|
||||||
|
defer wg.Done()
|
||||||
|
taskID, err := SubmitTask(0, "number_task", strconv.Itoa(index), PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 批量任务%d提交失败: %v\n", index, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 批量任务%d提交成功: ID=%d\n", index, taskID)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// 等待所有任务完成
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
// 获取统计信息
|
||||||
|
stats, err := GetPoolStats()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取统计信息失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 池统计: 总任务=%v, 成功=%v, 失败=%v, 排队=%v\n",
|
||||||
|
stats["totalTasks"], stats["successTasks"], stats["failedTasks"], stats["queuedTasks"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testConcurrentTasks 测试并发任务
|
||||||
|
func testConcurrentTasks(t *testing.T) {
|
||||||
|
const concurrentCount = 50
|
||||||
|
var completed int32
|
||||||
|
var errors int32
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// 提交大量并发任务
|
||||||
|
for i := 0; i < concurrentCount; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(taskNum int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
taskID, err := SubmitTask(0, "number_task", strconv.Itoa(taskNum), PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
atomic.AddInt32(&errors, 1)
|
||||||
|
fmt.Printf(" 并发任务%d提交失败: %v\n", taskNum, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待任务完成
|
||||||
|
for retry := 0; retry < 10; retry++ {
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err == nil {
|
||||||
|
status := info["status"].(int32)
|
||||||
|
if status == TaskStatusCompleted || status == TaskStatusFailed {
|
||||||
|
atomic.AddInt32(&completed, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
fmt.Printf(" ✓ 并发测试完成: 总数=%d, 完成=%d, 错误=%d, 耗时=%v\n",
|
||||||
|
concurrentCount, completed, errors, duration)
|
||||||
|
fmt.Printf(" 平均吞吐量: %.2f 任务/秒\n", float64(completed)/duration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// testWorkerControl 测试工作者控制
|
||||||
|
func testWorkerControl(t *testing.T) {
|
||||||
|
fmt.Println(" 创建新工作者...")
|
||||||
|
workerID, err := CreateWorker()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("创建工作者失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(" ✓ 工作者创建成功: ID=%d\n", workerID)
|
||||||
|
|
||||||
|
// 等待工作者完全启动
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
// 先获取状态
|
||||||
|
status, err := GetWorkerStatus(workerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取工作者状态失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(" ✓ 初始状态: %v\n", status)
|
||||||
|
|
||||||
|
// 暂停工作者
|
||||||
|
fmt.Println(" 暂停工作者...")
|
||||||
|
err = PauseWorker(workerID)
|
||||||
|
if err != nil {
|
||||||
|
// 如果是"worker not paused"错误,可能是因为状态已经是暂停
|
||||||
|
if strings.Contains(err.Error(), "not paused") {
|
||||||
|
fmt.Println(" ⚠ 工作者可能已经暂停")
|
||||||
|
} else {
|
||||||
|
t.Errorf("暂停工作者失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println(" ✓ 工作者已暂停")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再次检查状态
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
status, err = GetWorkerStatus(workerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取工作者状态失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(" ✓ 暂停后状态: %v\n", status)
|
||||||
|
|
||||||
|
// 恢复工作者
|
||||||
|
fmt.Println(" 恢复工作者...")
|
||||||
|
err = ResumeWorker(workerID)
|
||||||
|
if err != nil {
|
||||||
|
// 如果是"already running"错误,可以接受
|
||||||
|
if strings.Contains(err.Error(), "already running") {
|
||||||
|
fmt.Println(" ⚠ 工作者已经在运行状态")
|
||||||
|
} else {
|
||||||
|
t.Errorf("恢复工作者失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println(" ✓ 工作者已恢复")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待恢复完成
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
status, err = GetWorkerStatus(workerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取工作者状态失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(" ✓ 恢复后状态: %v\n", status)
|
||||||
|
|
||||||
|
// 停止工作者
|
||||||
|
fmt.Println(" 停止工作者...")
|
||||||
|
err = StopWorker(workerID)
|
||||||
|
if err != nil {
|
||||||
|
// 检查是否为可接受的错误
|
||||||
|
acceptableErrors := []string{
|
||||||
|
"worker not found",
|
||||||
|
"worker is stopped",
|
||||||
|
"worker is already stopped",
|
||||||
|
"worker context cancelled",
|
||||||
|
"timeout",
|
||||||
|
"control channel",
|
||||||
|
"no response",
|
||||||
|
}
|
||||||
|
|
||||||
|
isAcceptable := false
|
||||||
|
errStr := strings.ToLower(err.Error())
|
||||||
|
for _, acceptable := range acceptableErrors {
|
||||||
|
if strings.Contains(errStr, acceptable) {
|
||||||
|
isAcceptable = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isAcceptable {
|
||||||
|
t.Errorf("停止工作者失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(" ⚠ 工作者停止(可能已成功): %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println(" ✓ 工作者已停止")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待停止完成
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
// 验证工作者确实已停止
|
||||||
|
status, err = GetWorkerStatus(workerID)
|
||||||
|
if err != nil {
|
||||||
|
// 如果工作者不存在了,也是正常情况
|
||||||
|
if strings.Contains(err.Error(), "worker not found") {
|
||||||
|
fmt.Println(" ✓ 工作者已从池中移除")
|
||||||
|
} else {
|
||||||
|
t.Errorf("获取工作者状态失败: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果工作者还存在,检查状态
|
||||||
|
// 注意:status 已经是 map[string]interface{} 类型,不需要类型断言
|
||||||
|
if s, ok := status["status"].(float64); ok && int(s) == WorkerStatusStopped {
|
||||||
|
fmt.Println(" ✓ 工作者状态为已停止")
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ⚠ 工作者状态为: %v\n", status["status"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testPoolControl 测试池控制
|
||||||
|
func testPoolControl(t *testing.T) {
|
||||||
|
// 暂停所有工作者
|
||||||
|
fmt.Println(" 暂停所有工作者...")
|
||||||
|
count, err := PauseAllWorkers()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("暂停所有工作者失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 已暂停 %d 个工作者\n", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待确保所有工作者都已暂停
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查工作者状态
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
status, err := GetWorkerStatus(i)
|
||||||
|
if err == nil {
|
||||||
|
if s, ok := status["status"].(float64); ok && int(s) == WorkerStatusPaused {
|
||||||
|
fmt.Printf(" ✓ 工作者 %d 已暂停\n", i)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ! 工作者 %d 状态异常: %v\n", i, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交任务应该失败
|
||||||
|
taskID, err := SubmitTask(0, "string_task", "During Pause", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" ✓ 提交任务失败(预期): %v\n", err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("暂停状态下任务不应提交成功,但得到了任务ID: %d", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复所有工作者
|
||||||
|
fmt.Println(" 恢复所有工作者...")
|
||||||
|
count, err = ResumeAllWorkers()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("恢复所有工作者失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 已恢复 %d 个工作者\n", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待工作者完全恢复
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查工作者是否恢复
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
status, err := GetWorkerStatus(i)
|
||||||
|
if err == nil {
|
||||||
|
if s, ok := status["status"].(float64); ok &&
|
||||||
|
(int(s) == WorkerStatusRunning || int(s) == WorkerStatusIdle) {
|
||||||
|
fmt.Printf(" ✓ 工作者 %d 已恢复\n", i)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ! 工作者 %d 未正确恢复,状态: %v\n", i, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 现在工作者恢复了,提交一个新任务
|
||||||
|
taskID, err = SubmitTask(0, "string_task", "After Resume", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("恢复后提交任务失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 恢复后任务已提交: ID=%d\n", taskID)
|
||||||
|
|
||||||
|
// 等待任务执行
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" ! 获取任务信息失败: %v\n", err)
|
||||||
|
} else if info != nil {
|
||||||
|
if status, ok := info["status"].(int32); ok {
|
||||||
|
if status == TaskStatusCompleted {
|
||||||
|
fmt.Println(" ✓ 任务已执行完成")
|
||||||
|
} else if status == TaskStatusRunning {
|
||||||
|
fmt.Println(" ! 任务仍在运行中")
|
||||||
|
} else if status == TaskStatusPending {
|
||||||
|
fmt.Println(" ! 任务仍在排队")
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ! 任务状态异常: %d\n", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testPriorityTasks 测试优先级任务
|
||||||
|
func testPriorityTasks(t *testing.T) {
|
||||||
|
fmt.Println(" 提交不同优先级的任务...")
|
||||||
|
|
||||||
|
// 提交低优先级任务
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
param := fmt.Sprintf("Low Priority %d", i)
|
||||||
|
taskID, err := SubmitTask(0, "string_task", param, PriorityLow)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("低优先级任务%d提交失败: %v", i, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 低优先级任务%d: ID=%d\n", i, taskID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交高优先级任务
|
||||||
|
for i := 1; i <= 2; i++ {
|
||||||
|
param := fmt.Sprintf("High Priority %d", i)
|
||||||
|
taskID, err := SubmitTask(0, "string_task", param, PriorityHigh)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("高优先级任务%d提交失败: %v", i, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 高优先级任务%d: ID=%d\n", i, taskID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交紧急优先级任务
|
||||||
|
urgentTaskID, err := SubmitTask(0, "string_task", "Urgent Task", PriorityUrgent)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("紧急优先级任务提交失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 紧急优先级任务: ID=%d\n", urgentTaskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
fmt.Println(" 优先级验证完成(需检查执行时间戳)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// testErrorHandling 测试错误处理
|
||||||
|
func testErrorHandling(t *testing.T) {
|
||||||
|
// 提交会失败的任务
|
||||||
|
taskID, err := SubmitTask(0, "error_task", "Test Error", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("错误任务提交失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 错误任务已提交: ID=%d\n", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查错误任务状态
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取错误任务信息失败: %v", err)
|
||||||
|
} else {
|
||||||
|
status := info["status"].(int32)
|
||||||
|
if status == TaskStatusFailed {
|
||||||
|
fmt.Println(" ✓ 任务失败处理正确")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试取消任务
|
||||||
|
taskID, err = SubmitTask(0, "long_task", "To be cancelled", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("可取消任务提交失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 可取消任务已提交: ID=%d\n", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即取消
|
||||||
|
err = CancelTask(taskID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("取消任务失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println(" ✓ 任务取消请求已发送")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查取消状态
|
||||||
|
info, err = GetTaskInfo(taskID)
|
||||||
|
if err == nil {
|
||||||
|
status := info["status"].(int32)
|
||||||
|
if status == TaskStatusCancelled {
|
||||||
|
fmt.Println(" ✓ 任务已成功取消")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testMemoryAndPerformance 测试内存和性能
|
||||||
|
func testMemoryAndPerformance(t *testing.T) {
|
||||||
|
fmt.Println(" 开始内存密集型任务测试...")
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
const memoryTasks = 20
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for i := 0; i < memoryTasks; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int) {
|
||||||
|
defer wg.Done()
|
||||||
|
taskID, err := SubmitTask(0, "memory_task", fmt.Sprintf("Memory Test %d", index), PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 内存任务%d提交失败: %v\n", index, err)
|
||||||
|
} else {
|
||||||
|
// 等待完成
|
||||||
|
for retry := 0; retry < 20; retry++ {
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err == nil && (info["status"].(int32) == TaskStatusCompleted ||
|
||||||
|
info["status"].(int32) == TaskStatusFailed) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
fmt.Printf(" ✓ 内存测试完成: 耗时=%v\n", duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testGracefulShutdown 测试优雅关闭
|
||||||
|
func testGracefulShutdown(t *testing.T) {
|
||||||
|
// 提交一些任务
|
||||||
|
fmt.Println(" 提交一些任务以测试优雅关闭...")
|
||||||
|
|
||||||
|
taskIDs := make([]int64, 0, 5)
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
taskID, err := SubmitTask(0, "string_task", fmt.Sprintf("Shutdown Test %d", i), PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("关闭测试任务%d提交失败: %v", i, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 关闭测试任务%d: ID=%d\n", i, taskID)
|
||||||
|
taskIDs = append(taskIDs, taskID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一小会儿让任务开始执行
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查任务状态
|
||||||
|
runningTasks := 0
|
||||||
|
for _, taskID := range taskIDs {
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err == nil && info != nil {
|
||||||
|
if status, ok := info["status"].(int32); ok {
|
||||||
|
if status == TaskStatusRunning || status == TaskStatusPending {
|
||||||
|
runningTasks++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf(" 当前有 %d 个任务正在运行或排队\n", runningTasks)
|
||||||
|
|
||||||
|
// 开始优雅关闭(同步等待)
|
||||||
|
fmt.Println(" 开始优雅关闭...")
|
||||||
|
startTime := time.Now()
|
||||||
|
err := GracefulShutdown()
|
||||||
|
shutdownDuration := time.Since(startTime)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("优雅关闭失败: %v", err)
|
||||||
|
fmt.Printf(" 优雅关闭失败: %v (耗时: %v)\n", err, shutdownDuration)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 优雅关闭成功 (耗时: %v)\n", shutdownDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一小段时间确保状态更新
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查池状态
|
||||||
|
if !IsInitialized() {
|
||||||
|
fmt.Println(" ✓ 池已正确关闭")
|
||||||
|
|
||||||
|
// 检查任务是否都执行完成
|
||||||
|
completedCount := 0
|
||||||
|
for _, taskID := range taskIDs {
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err == nil && info != nil {
|
||||||
|
if status, ok := info["status"].(int32); ok {
|
||||||
|
if status == TaskStatusCompleted || status == TaskStatusFailed {
|
||||||
|
completedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf(" ✓ %d/%d 个任务执行完成\n", completedCount, len(taskIDs))
|
||||||
|
} else {
|
||||||
|
// 如果池未关闭,可能是还在处理中,等待更长时间
|
||||||
|
fmt.Println(" ! 池仍在运行,等待额外时间...")
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
if !IsInitialized() {
|
||||||
|
fmt.Println(" ✓ 池已正确关闭(等待后)")
|
||||||
|
} else {
|
||||||
|
t.Error("池未正确关闭")
|
||||||
|
// 强制关闭
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testReinitialization 测试重新初始化
|
||||||
|
func testReinitialization(t *testing.T) {
|
||||||
|
fmt.Println(" 测试重新初始化...")
|
||||||
|
|
||||||
|
// 重新初始化
|
||||||
|
err := Initialize(2, 5, `{
|
||||||
|
"minWorkers": 2,
|
||||||
|
"maxWorkers": 5,
|
||||||
|
"queueSize": 20,
|
||||||
|
"name": "ReinitializedPool"
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("重新初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println(" ✓ 池重新初始化成功")
|
||||||
|
|
||||||
|
// 重新注册函数
|
||||||
|
RegisterFunction("string_task", testStringTask)
|
||||||
|
RegisterFunction("number_task", testNumberTask)
|
||||||
|
|
||||||
|
// 测试新池功能
|
||||||
|
taskID, err := SubmitTask(0, "string_task", "Reinitialization Test", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("重新初始化后提交任务失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ✓ 重新初始化后任务提交成功: ID=%d\n", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 获取任务信息
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err == nil && info["status"].(int32) == TaskStatusCompleted {
|
||||||
|
fmt.Println(" ✓ 重新初始化后任务执行成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 高级测试 ====================
|
||||||
|
|
||||||
|
// TestAdvancedFeatures2 测试高级功能(重命名避免冲突)
|
||||||
|
func TestAdvancedFeatures2(t *testing.T) {
|
||||||
|
fmt.Println("\n========== 开始高级功能测试 ==========")
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
err := Initialize(4, 8, `{
|
||||||
|
"minWorkers": 4,
|
||||||
|
"maxWorkers": 8,
|
||||||
|
"queueSize": 100,
|
||||||
|
"priorityQueue": true
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterFunction("string_task", testStringTask)
|
||||||
|
RegisterFunction("number_task", testNumberTask)
|
||||||
|
|
||||||
|
// 1. 动态扩缩容测试
|
||||||
|
fmt.Println("\n1. 动态扩缩容测试...")
|
||||||
|
testAutoScaling2(t)
|
||||||
|
|
||||||
|
// 2. 故障恢复测试
|
||||||
|
fmt.Println("\n2. 故障恢复测试...")
|
||||||
|
testFaultRecovery2(t)
|
||||||
|
|
||||||
|
// 3. 配置热更新测试
|
||||||
|
fmt.Println("\n3. 配置热更新测试...")
|
||||||
|
testHotConfigUpdate2(t)
|
||||||
|
|
||||||
|
fmt.Println("\n========== 高级测试完成 ==========")
|
||||||
|
|
||||||
|
// 确保清理
|
||||||
|
if IsInitialized() {
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testAutoScaling2 测试自动扩缩容
|
||||||
|
func testAutoScaling2(t *testing.T) {
|
||||||
|
// 获取初始统计
|
||||||
|
stats, err := GetPoolStats()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取统计失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
initialWorkers := stats["totalWorkers"].(int32)
|
||||||
|
fmt.Printf(" 初始工作者数: %d\n", initialWorkers)
|
||||||
|
|
||||||
|
// 提交大量任务触发扩容
|
||||||
|
const burstTasks = 100
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
fmt.Printf(" 提交 %d 个任务触发扩容...\n", burstTasks)
|
||||||
|
for i := 0; i < burstTasks; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int) {
|
||||||
|
defer wg.Done()
|
||||||
|
SubmitTask(0, "number_task", strconv.Itoa(index), PriorityNormal)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
// 检查扩容
|
||||||
|
stats, err = GetPoolStats()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("获取统计失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
finalWorkers := stats["totalWorkers"].(int32)
|
||||||
|
fmt.Printf(" 扩容后工作者数: %d (增加: %d)\n", finalWorkers, finalWorkers-initialWorkers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testFaultRecovery2 测试故障恢复
|
||||||
|
func testFaultRecovery2(t *testing.T) {
|
||||||
|
// 创建专门用于测试的工作者
|
||||||
|
workerID, err := CreateWorker()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("创建测试工作者失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" 创建测试工作者: ID=%d\n", workerID)
|
||||||
|
|
||||||
|
// 注册一个会panic的任务函数
|
||||||
|
panicFunc := func(param string) (string, error) {
|
||||||
|
panic("simulated panic in task")
|
||||||
|
}
|
||||||
|
RegisterFunction("panic_task", panicFunc)
|
||||||
|
|
||||||
|
// 提交会panic的任务
|
||||||
|
taskID, err := SubmitTask(workerID, "panic_task", "Trigger Panic", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("提交panic任务失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" ✓ 提交panic任务: ID=%d\n", taskID)
|
||||||
|
|
||||||
|
// 等待恢复
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// 提交正常任务测试恢复后的功能
|
||||||
|
taskID, err = SubmitTask(workerID, "string_task", "After Recovery", PriorityNormal)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf(" ✓ 恢复后任务提交成功: ID=%d\n", taskID)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testHotConfigUpdate2 测试配置热更新
|
||||||
|
func testHotConfigUpdate2(t *testing.T) {
|
||||||
|
fmt.Println(" 更新池配置...")
|
||||||
|
|
||||||
|
newConfig := `{
|
||||||
|
"minWorkers": 6,
|
||||||
|
"maxWorkers": 12,
|
||||||
|
"queueSize": 200,
|
||||||
|
"taskTimeoutMs": 10000,
|
||||||
|
"workerIdleTimeoutMs": 60000,
|
||||||
|
"memoryLimitMB": 1024,
|
||||||
|
"name": "UpdatedPool"
|
||||||
|
}`
|
||||||
|
|
||||||
|
err := UpdatePoolConfig(newConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("配置热更新失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println(" ✓ 配置热更新成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 集成测试示例 ====================
|
||||||
|
|
||||||
|
// TestExampleUsage 使用示例
|
||||||
|
func TestExampleUsage(t *testing.T) {
|
||||||
|
fmt.Println("========== 协程池使用示例 ==========")
|
||||||
|
|
||||||
|
// 1. 初始化
|
||||||
|
err := Initialize(2, 5, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
defer Shutdown()
|
||||||
|
|
||||||
|
// 2. 注册任务函数
|
||||||
|
RegisterFunction("process_data", func(param string) (string, error) {
|
||||||
|
// 模拟数据处理
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
return "Processed: " + param, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. 提交任务
|
||||||
|
taskIDs := make([]int64, 0, 10)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
taskID, err := SubmitTask(0, "process_data",
|
||||||
|
fmt.Sprintf("Data item %d", i), PriorityNormal)
|
||||||
|
if err == nil {
|
||||||
|
taskIDs = append(taskIDs, taskID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("提交了 %d 个任务\n", len(taskIDs))
|
||||||
|
|
||||||
|
// 4. 等待任务完成
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
// 5. 获取统计
|
||||||
|
stats, _ := GetPoolStats()
|
||||||
|
fmt.Printf("池统计: 总任务=%v, 成功=%v, 失败=%v\n",
|
||||||
|
stats["totalTasks"], stats["successTasks"], stats["failedTasks"])
|
||||||
|
|
||||||
|
fmt.Println("示例完成")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 主测试函数 ====================
|
||||||
|
|
||||||
|
// TestAll 运行所有测试
|
||||||
|
func TestAll(t *testing.T) {
|
||||||
|
// 设置恢复
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("测试发生panic: %v", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终清理
|
||||||
|
if IsInitialized() {
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println("开始运行协程池完整测试套件")
|
||||||
|
fmt.Println("======================================")
|
||||||
|
|
||||||
|
// 运行基础功能测试
|
||||||
|
TestFullFunctionality(t)
|
||||||
|
|
||||||
|
// 运行高级功能测试
|
||||||
|
TestAdvancedFeatures2(t)
|
||||||
|
|
||||||
|
// 运行使用示例
|
||||||
|
TestExampleUsage(t)
|
||||||
|
|
||||||
|
fmt.Println("\n所有测试运行完成!")
|
||||||
|
fmt.Println("======================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugBasic(t *testing.T) {
|
||||||
|
fmt.Println("=== 调试测试开始 ===")
|
||||||
|
|
||||||
|
// 1. 简单初始化
|
||||||
|
err := Initialize(2, 2, `{
|
||||||
|
"minWorkers": 2,
|
||||||
|
"maxWorkers": 2,
|
||||||
|
"queueSize": 10,
|
||||||
|
"taskTimeoutMs": 1000,
|
||||||
|
"workerIdleTimeoutMs": 1000,
|
||||||
|
"enableMetrics": false
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
defer Shutdown()
|
||||||
|
|
||||||
|
// 等待池启动
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 2. 注册简单任务
|
||||||
|
RegisterFunction("echo", func(param string) (string, error) {
|
||||||
|
fmt.Printf("任务执行: %s\n", param)
|
||||||
|
return "Echo: " + param, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. 提交单个任务
|
||||||
|
taskID, err := SubmitTask(0, "echo", "Test1", PriorityNormal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("提交任务失败: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("任务提交成功: ID=%d\n", taskID)
|
||||||
|
|
||||||
|
// 4. 等待执行
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
// 5. 检查状态
|
||||||
|
stats, err := GetPoolStats()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("获取统计失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("池统计: %+v\n", stats)
|
||||||
|
|
||||||
|
// 6. 获取任务信息
|
||||||
|
info, err := GetTaskInfo(taskID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("获取任务信息失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("任务信息: %+v\n", info)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("=== 调试测试结束 ===")
|
||||||
|
}
|
||||||
Binary file not shown.
@ -88,7 +88,7 @@ extern "C" {
|
|||||||
|
|
||||||
|
|
||||||
// =================== C 导出函数 =======================
|
// =================== C 导出函数 =======================
|
||||||
// 处理图片成功
|
// 检测图片纯白占比
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* ProcessImage(char* jsonConfig);
|
extern __declspec(dllexport) char* ProcessImage(char* jsonConfig);
|
||||||
|
|
||||||
@ -104,6 +104,22 @@ extern __declspec(dllexport) char* ResizeToHeightQuality(char* jsonConfig, int t
|
|||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* RemoveWhiteBorderAndPNG(char* jsonConfig);
|
extern __declspec(dllexport) char* RemoveWhiteBorderAndPNG(char* jsonConfig);
|
||||||
|
|
||||||
|
// ResizeWTToHeightQuality 图片缩放
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* ResizeWTToHeightQuality(char* jsonConfig, int dsWidth, int dsHeight);
|
||||||
|
|
||||||
|
// CropImage 图片裁切
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* CropImage(char* jsonConfig, int x, int y, int width, int height);
|
||||||
|
|
||||||
|
// CreateChineseTextImage 创建带中文字体的文本图片,支持超出部分显示...
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* CreateChineseTextImage(char* text, int width, int height, char* fontSize, char* outputPath);
|
||||||
|
|
||||||
|
// DrawChineseInfo 绘制书名,作者,出版社信息
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* DrawChineseInfo(char* filePath, char* title, char* author, char* publisher, char* outputPath);
|
||||||
|
|
||||||
// 导出函数:释放C字符串内存
|
// 导出函数:释放C字符串内存
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) void FreeCString(char* str);
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|||||||
812
image/image.go
812
image/image.go
@ -5,20 +5,31 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/boombuler/barcode"
|
||||||
|
"github.com/boombuler/barcode/code128"
|
||||||
|
"github.com/boombuler/barcode/code39"
|
||||||
|
"github.com/boombuler/barcode/ean"
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
"github.com/golang/freetype"
|
||||||
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/makiuchi-d/gozxing"
|
"github.com/makiuchi-d/gozxing"
|
||||||
"github.com/makiuchi-d/gozxing/qrcode"
|
"github.com/makiuchi-d/gozxing/qrcode"
|
||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
"golang.org/x/image/draw"
|
"golang.org/x/image/draw"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -706,20 +717,6 @@ func cropImage(config *Config, x, y, width, height int) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
//// 执行裁切
|
|
||||||
//var cropped image.Image
|
|
||||||
//if width > 0 && height > 0 {
|
|
||||||
// cropped, err = SmartCrop(img, x, y, width, height int)
|
|
||||||
//} else {
|
|
||||||
// // 如果未指定尺寸,使用中心裁切
|
|
||||||
// cropped, err = CropCenter(img, config.Width, config.Height)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//if err != nil {
|
|
||||||
// return "", err
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
|
|
||||||
// 保存图片到指定目录下
|
// 保存图片到指定目录下
|
||||||
filename := filepath.Base(config.FileName)
|
filename := filepath.Base(config.FileName)
|
||||||
destPath := filepath.Join(config.OutputDir, config.CropDir, filename)
|
destPath := filepath.Join(config.OutputDir, config.CropDir, filename)
|
||||||
@ -766,77 +763,6 @@ func basicCrop(src image.Image, x, y, width, height int) (image.Image, error) {
|
|||||||
return cropped, nil
|
return cropped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//// smartCrop 智能裁切,处理边界和自动调整 -- 暂时没用
|
|
||||||
//func smartCrop(src image.Image, x, y, width, height int) (image.Image, error) {
|
|
||||||
// srcBounds := src.Bounds()
|
|
||||||
// srcWidth := srcBounds.Dx()
|
|
||||||
// srcHeight := srcBounds.Dy()
|
|
||||||
//
|
|
||||||
// // 如果宽度或高度为0,使用图像的最大可能尺寸
|
|
||||||
// if width == 0 {
|
|
||||||
// width = srcWidth - x
|
|
||||||
// }
|
|
||||||
// if height == 0 {
|
|
||||||
// height = srcHeight - y
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 调整裁切区域以确保在边界内
|
|
||||||
// newX := max(0, min(x, srcWidth-1))
|
|
||||||
// newY := max(0, min(x, srcHeight-1))
|
|
||||||
// width = max(1, min(width, srcWidth-x))
|
|
||||||
// height = max(1, min(height, srcHeight-y))
|
|
||||||
//
|
|
||||||
// // 如果需要保持宽高比,调整裁切区域
|
|
||||||
// if width > 0 && height > 0 {
|
|
||||||
// currentRatio := float64(width) / float64(height)
|
|
||||||
// originalRatio := float64(srcWidth) / float64(srcHeight)
|
|
||||||
//
|
|
||||||
// if math.Abs(currentRatio-originalRatio) > 0.01 {
|
|
||||||
// // 调整宽度以匹配原始宽高比
|
|
||||||
// newWidth := int(float64(height) * originalRatio)
|
|
||||||
// if newWidth <= (srcWidth - newX) {
|
|
||||||
// width = newWidth
|
|
||||||
// } else {
|
|
||||||
// // 调整高度以匹配原始宽高比
|
|
||||||
// newHeight := int(float64(width) / originalRatio)
|
|
||||||
// if newHeight <= (srcHeight - newY) {
|
|
||||||
// height = newHeight
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 执行裁切
|
|
||||||
// cropped, err := basicCrop(src, newX, newY, width, height)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return cropped, nil
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// CropCenter 中心裁切 -- 暂时没用
|
|
||||||
//func CropCenter(src image.Image, width, height int) (image.Image, error) {
|
|
||||||
// srcBounds := src.Bounds()
|
|
||||||
// srcWidth := srcBounds.Dx()
|
|
||||||
// srcHeight := srcBounds.Dy()
|
|
||||||
//
|
|
||||||
// // 如果裁切尺寸大于原图,返回原图或调整裁切尺寸
|
|
||||||
// if width >= srcWidth && height >= srcHeight {
|
|
||||||
// return src, nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 计算中心裁切的起始点
|
|
||||||
// x := (srcWidth - width) / 2
|
|
||||||
// y := (srcHeight - height) / 2
|
|
||||||
//
|
|
||||||
// // 确保不超出边界
|
|
||||||
// x = max(0, min(x, srcWidth-width))
|
|
||||||
// y = max(0, min(y, srcHeight-height))
|
|
||||||
//
|
|
||||||
// return BasicCrop(src, x, y, width, height)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 识别二维码
|
// 识别二维码
|
||||||
func scanQRCode(fileName string) (bool, string, error) {
|
func scanQRCode(fileName string) (bool, string, error) {
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
@ -1107,6 +1033,652 @@ func generateQRCode(content string, width int, height int, fileName string) (str
|
|||||||
return fmt.Sprintf("生成二维码成功: %v", fileName), nil
|
return fmt.Sprintf("生成二维码成功: %v", fileName), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 简单的自动换行实现(按字符数,适合中英文混合)
|
||||||
|
// 返回处理后的行列表和是否还有更多内容未显示
|
||||||
|
func simpleAutoWrap(text string, maxCharsPerLine int) ([]string, bool) {
|
||||||
|
var lines []string
|
||||||
|
var currentLine strings.Builder
|
||||||
|
charCount := 0
|
||||||
|
|
||||||
|
for _, r := range text {
|
||||||
|
// 换行符处理
|
||||||
|
if r == '\n' {
|
||||||
|
if currentLine.Len() > 0 {
|
||||||
|
lines = append(lines, currentLine.String())
|
||||||
|
currentLine.Reset()
|
||||||
|
charCount = 0
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算字符宽度(中文算2个字符,英文算1个)
|
||||||
|
charWidth := 1
|
||||||
|
if r >= 0x4E00 && r <= 0x9FFF { // 中文范围
|
||||||
|
charWidth = 2
|
||||||
|
} else if r >= 0x3000 && r <= 0x303F { // 中文标点
|
||||||
|
charWidth = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否需要换行
|
||||||
|
if charCount+charWidth > maxCharsPerLine && currentLine.Len() > 0 {
|
||||||
|
lines = append(lines, currentLine.String())
|
||||||
|
currentLine.Reset()
|
||||||
|
charCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLine.WriteRune(r)
|
||||||
|
charCount += charWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentLine.Len() > 0 {
|
||||||
|
lines = append(lines, currentLine.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines, false // 返回false表示所有内容都已处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建带中文字体的文本图片,支持超出部分显示...
|
||||||
|
// text 文本, width, height 宽度高度, fontSize 文字大小, outputPath 输入路径
|
||||||
|
func createChineseTextImage(text string, width, height int, fontSize float64, outputPath string) (string, error) {
|
||||||
|
// 获取字体路径
|
||||||
|
fontPath := getDefaultFontPath()
|
||||||
|
if fontPath == "" {
|
||||||
|
return "", fmt.Errorf("未找到系统字体文件,请手动指定字体路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取字体文件
|
||||||
|
fontBytes, err := ioutil.ReadFile(fontPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取字体文件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析字体
|
||||||
|
f, err := truetype.Parse(fontBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("解析字体失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建图片
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
// 白色背景
|
||||||
|
white := color.RGBA{255, 255, 255, 255}
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
img.Set(x, y, white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建freetype上下文
|
||||||
|
c := freetype.NewContext()
|
||||||
|
c.SetDPI(72)
|
||||||
|
c.SetFont(f)
|
||||||
|
c.SetFontSize(fontSize)
|
||||||
|
c.SetClip(img.Bounds())
|
||||||
|
c.SetDst(img)
|
||||||
|
c.SetSrc(image.NewUniform(color.Black))
|
||||||
|
|
||||||
|
// 计算可用的文本宽度(左右各留50像素边距)
|
||||||
|
availableWidth := width - 100
|
||||||
|
|
||||||
|
// 根据字体大小计算每行大约可以显示多少个字符
|
||||||
|
charsPerLine := int(float64(availableWidth) / fontSize * 1.7)
|
||||||
|
if charsPerLine < 10 {
|
||||||
|
charsPerLine = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算最大可显示行数
|
||||||
|
// X坐标:从左侧20像素开始
|
||||||
|
// Y坐标:从顶部20像素 + 字体高度
|
||||||
|
lineSpacing := int(c.PointToFixed(fontSize*1.5) >> 6)
|
||||||
|
topMargin := 50 + int(c.PointToFixed(fontSize)>>6)
|
||||||
|
bottomMargin := 50
|
||||||
|
maxLines := (height - topMargin - bottomMargin) / lineSpacing
|
||||||
|
if maxLines <= 0 {
|
||||||
|
maxLines = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用简单的自动换行,获取所有行
|
||||||
|
allLines, _ := simpleAutoWrap(text, charsPerLine)
|
||||||
|
|
||||||
|
// 检查是否有超出图片的内容
|
||||||
|
hasMore := len(allLines) > maxLines
|
||||||
|
displayLines := allLines
|
||||||
|
if hasMore {
|
||||||
|
// 只显示前 maxLines-1 行,最后一行添加 "..."
|
||||||
|
displayLines = allLines[:maxLines-1]
|
||||||
|
|
||||||
|
// 获取最后一行文本,并确保能显示 "..."
|
||||||
|
lastLine := allLines[maxLines-1]
|
||||||
|
lastLineChars := 0
|
||||||
|
var truncatedLine strings.Builder
|
||||||
|
|
||||||
|
for _, r := range lastLine {
|
||||||
|
// 计算字符宽度
|
||||||
|
charWidth := 1
|
||||||
|
if r >= 0x4E00 && r <= 0x9FFF {
|
||||||
|
charWidth = 2
|
||||||
|
} else if r >= 0x3000 && r <= 0x303F {
|
||||||
|
charWidth = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否还能添加字符(留出3个字符给"...")
|
||||||
|
if lastLineChars+charWidth > charsPerLine-3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
truncatedLine.WriteRune(r)
|
||||||
|
lastLineChars += charWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 "..."
|
||||||
|
truncatedText := truncatedLine.String() + " ..."
|
||||||
|
displayLines = append(displayLines, truncatedText)
|
||||||
|
} else {
|
||||||
|
// 如果内容不多,直接显示所有行
|
||||||
|
displayLines = allLines
|
||||||
|
if len(displayLines) > maxLines {
|
||||||
|
displayLines = displayLines[:maxLines]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置起始位置
|
||||||
|
startX := 50
|
||||||
|
startY := topMargin
|
||||||
|
|
||||||
|
// 绘制每行文字
|
||||||
|
pt := freetype.Pt(startX, startY)
|
||||||
|
for i, line := range displayLines {
|
||||||
|
// 确保不会超出图片底部
|
||||||
|
if i*lineSpacing > height-bottomMargin {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制文字
|
||||||
|
_, err = c.DrawString(line, pt)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("绘制文字失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动到下一行
|
||||||
|
pt.Y += c.PointToFixed(fontSize * 1.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存图片
|
||||||
|
file, err := os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = png.Encode(file, img)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("编码并写入失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取系统字体路径
|
||||||
|
func getDefaultFontPath() string {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
paths := []string{
|
||||||
|
"C:/Windows/Fonts/simhei.ttf", // 黑体
|
||||||
|
"C:/Windows/Fonts/simsun.ttc", // 宋体
|
||||||
|
"C:/Windows/Fonts/msyh.ttc", // 微软雅黑
|
||||||
|
}
|
||||||
|
for _, path := range paths {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "darwin":
|
||||||
|
return "/System/Library/Fonts/PingFang.ttc"
|
||||||
|
case "linux":
|
||||||
|
paths := []string{
|
||||||
|
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
||||||
|
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
||||||
|
}
|
||||||
|
for _, path := range paths {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制中文文本(支持自动换行)
|
||||||
|
func drawChineseText(img *image.RGBA, title, author, publisher string) error {
|
||||||
|
// 获取字体路径
|
||||||
|
fontPath := getDefaultFontPath()
|
||||||
|
if fontPath == "" {
|
||||||
|
return fmt.Errorf("未找到系统字体文件,请手动指定字体路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取字体文件
|
||||||
|
fontBytes, err := ioutil.ReadFile(fontPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("读取字体文件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析字体
|
||||||
|
fontObj, err := truetype.Parse(fontBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("解析字体失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建freetype上下文
|
||||||
|
c := freetype.NewContext()
|
||||||
|
c.SetDPI(72)
|
||||||
|
c.SetFont(fontObj)
|
||||||
|
c.SetSrc(image.NewUniform(color.Black))
|
||||||
|
c.SetClip(img.Bounds())
|
||||||
|
c.SetDst(img)
|
||||||
|
|
||||||
|
// 定义绘制区域的宽度
|
||||||
|
maxWidth := 400 // 可根据需要调整
|
||||||
|
|
||||||
|
// 1. 绘制书名(使用较大字体,支持多行)
|
||||||
|
c.SetFontSize(45)
|
||||||
|
titleLines := wrapText(title, fontObj, 45, maxWidth)
|
||||||
|
// 限制书名最多显示3行
|
||||||
|
if len(titleLines) > 3 {
|
||||||
|
titleLines = titleLines[:3]
|
||||||
|
}
|
||||||
|
titleY := 250 // 起始Y坐标
|
||||||
|
titleLineHeight := 60 // 行高
|
||||||
|
|
||||||
|
for i, line := range titleLines {
|
||||||
|
lineWidth := calculateStringWidth(line, fontObj, 45)
|
||||||
|
pt := freetype.Pt((800-int(lineWidth))/2+30, titleY+i*titleLineHeight)
|
||||||
|
if _, err := c.DrawString(line, pt); err != nil {
|
||||||
|
return fmt.Errorf("绘制书名失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 绘制作者(使用中等字体,支持多行)
|
||||||
|
c.SetFontSize(28)
|
||||||
|
authorLines := wrapText(author, fontObj, 28, maxWidth)
|
||||||
|
// 限制作者最多显示2行
|
||||||
|
if len(authorLines) > 2 {
|
||||||
|
authorLines = authorLines[:2]
|
||||||
|
}
|
||||||
|
authorY := 420 // 起始Y坐标
|
||||||
|
authorLineHeight := 40 // 行高
|
||||||
|
|
||||||
|
for i, line := range authorLines {
|
||||||
|
lineWidth := calculateStringWidth(line, fontObj, 28)
|
||||||
|
pt := freetype.Pt((800-int(lineWidth))/2+30, authorY+i*authorLineHeight)
|
||||||
|
if _, err := c.DrawString(line, pt); err != nil {
|
||||||
|
return fmt.Errorf("绘制作者失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 绘制出版社(使用中等字体,支持多行)
|
||||||
|
c.SetFontSize(18)
|
||||||
|
publisherLines := wrapText(publisher, fontObj, 18, maxWidth)
|
||||||
|
// 限制出版社最多显示2行
|
||||||
|
if len(publisherLines) > 1 {
|
||||||
|
publisherLines = publisherLines[:1]
|
||||||
|
}
|
||||||
|
publisherY := 700 // 起始Y坐标
|
||||||
|
publisherLineHeight := 30 // 行高
|
||||||
|
|
||||||
|
for i, line := range publisherLines {
|
||||||
|
lineWidth := calculateStringWidth(line, fontObj, 18)
|
||||||
|
pt := freetype.Pt((800-int(lineWidth))/2+30, publisherY+i*publisherLineHeight)
|
||||||
|
if _, err := c.DrawString(line, pt); err != nil {
|
||||||
|
return fmt.Errorf("绘制出版社失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 绘制右下角文字
|
||||||
|
err = drawBottomRightText(img, fontObj)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("绘制右下角文字失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制右下角文字
|
||||||
|
func drawBottomRightText(img *image.RGBA, fontObj *truetype.Font) error {
|
||||||
|
// 设置文字内容
|
||||||
|
line1 := "此为实例图片"
|
||||||
|
line2 := "联系客服获取实图"
|
||||||
|
|
||||||
|
// 设置字体大小
|
||||||
|
fontSize := 8.0
|
||||||
|
|
||||||
|
// 计算右边界距
|
||||||
|
rightMargin := 10 // 距离右边界的像素
|
||||||
|
bottomMargin := 10 // 距离底部的像素
|
||||||
|
|
||||||
|
// 计算行高
|
||||||
|
lineHeight := int(fontSize * 1.5)
|
||||||
|
|
||||||
|
// 创建freetype上下文
|
||||||
|
c := freetype.NewContext()
|
||||||
|
c.SetDPI(72)
|
||||||
|
c.SetFont(fontObj)
|
||||||
|
c.SetClip(img.Bounds())
|
||||||
|
c.SetDst(img)
|
||||||
|
|
||||||
|
// 计算第二行(最后一行)的位置
|
||||||
|
line2Width := calculateStringWidth(line2, fontObj, fontSize)
|
||||||
|
x2 := img.Bounds().Dx() - line2Width - rightMargin
|
||||||
|
y2 := img.Bounds().Dy() - bottomMargin
|
||||||
|
|
||||||
|
// 计算第一行的位置
|
||||||
|
line1Width := calculateStringWidth(line1, fontObj, fontSize)
|
||||||
|
x1 := img.Bounds().Dx() - line1Width - rightMargin
|
||||||
|
y1 := y2 - lineHeight
|
||||||
|
|
||||||
|
// 设置字体大小
|
||||||
|
c.SetFontSize(fontSize)
|
||||||
|
|
||||||
|
// 方法1:多层绘制实现描边效果
|
||||||
|
strokeRadius := 1.0 // 描边半径
|
||||||
|
|
||||||
|
// 绘制描边(灰色,8个方向)
|
||||||
|
strokeColor := color.RGBA{128, 128, 128, 150} // 灰色
|
||||||
|
c.SetSrc(image.NewUniform(strokeColor))
|
||||||
|
|
||||||
|
// 为第二行绘制描边
|
||||||
|
offsets := []struct{ dx, dy float64 }{
|
||||||
|
{-strokeRadius, -strokeRadius}, {0, -strokeRadius}, {strokeRadius, -strokeRadius},
|
||||||
|
{-strokeRadius, 0}, {strokeRadius, 0},
|
||||||
|
{-strokeRadius, strokeRadius}, {0, strokeRadius}, {strokeRadius, strokeRadius},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, offset := range offsets {
|
||||||
|
pt2 := freetype.Pt(int(float64(x2)+offset.dx), int(float64(y2)+offset.dy))
|
||||||
|
if _, err := c.DrawString(line2, pt2); err != nil {
|
||||||
|
return fmt.Errorf("绘制第二行描边失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pt1 := freetype.Pt(int(float64(x1)+offset.dx), int(float64(y1)+offset.dy))
|
||||||
|
if _, err := c.DrawString(line1, pt1); err != nil {
|
||||||
|
return fmt.Errorf("绘制第一行描边失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制白色文字(覆盖在中间)
|
||||||
|
textColor := color.RGBA{255, 255, 255, 255} // 白色
|
||||||
|
c.SetSrc(image.NewUniform(textColor))
|
||||||
|
|
||||||
|
// 绘制第二行文字
|
||||||
|
pt2 := freetype.Pt(x2, y2)
|
||||||
|
if _, err := c.DrawString(line2, pt2); err != nil {
|
||||||
|
return fmt.Errorf("绘制第二行文字失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制第一行文字
|
||||||
|
pt1 := freetype.Pt(x1, y1)
|
||||||
|
if _, err := c.DrawString(line1, pt1); err != nil {
|
||||||
|
return fmt.Errorf("绘制第一行文字失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文本换行函数
|
||||||
|
func wrapText(text string, fontObj *truetype.Font, fontSize float64, maxWidth int) []string {
|
||||||
|
var lines []string
|
||||||
|
var currentLine string
|
||||||
|
var currentWidth int
|
||||||
|
|
||||||
|
for _, ch := range text {
|
||||||
|
char := string(ch)
|
||||||
|
charWidth := calculateStringWidth(char, fontObj, fontSize)
|
||||||
|
|
||||||
|
// 如果当前字符是换行符,直接换行
|
||||||
|
if char == "\n" {
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
}
|
||||||
|
currentLine = ""
|
||||||
|
currentWidth = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果加上当前字符会超出宽度,开始新行
|
||||||
|
if currentWidth+charWidth > maxWidth && currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
currentLine = char
|
||||||
|
currentWidth = charWidth
|
||||||
|
} else {
|
||||||
|
currentLine += char
|
||||||
|
currentWidth += charWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加最后一行
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算字符串宽度
|
||||||
|
func calculateStringWidth(text string, fontObj *truetype.Font, fontSize float64) int {
|
||||||
|
width := 0
|
||||||
|
for _, ch := range text {
|
||||||
|
idx := fontObj.Index(ch)
|
||||||
|
horizAdvance := fontObj.HMetric(fixed.Int26_6(fontSize), idx).AdvanceWidth
|
||||||
|
width += int(horizAdvance)
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载PNG图片
|
||||||
|
func loadPNG(filename string) (*image.RGBA, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
img, err := png.Decode(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为RGBA
|
||||||
|
rgba := image.NewRGBA(img.Bounds())
|
||||||
|
for y := 0; y < img.Bounds().Dy(); y++ {
|
||||||
|
for x := 0; x < img.Bounds().Dx(); x++ {
|
||||||
|
rgba.Set(x, y, img.At(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存为PNG文件
|
||||||
|
func savePNG(img image.Image, filename string) error {
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return png.Encode(file, img)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
CODE128 = "code128"
|
||||||
|
EAN13 = "ean13"
|
||||||
|
CODE39 = "code39"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 根据类型生成条形码
|
||||||
|
func generateBarcode(barcodeType, content, filename string) (string, error) {
|
||||||
|
switch barcodeType {
|
||||||
|
case CODE128:
|
||||||
|
return generateCode128(content, filename)
|
||||||
|
case EAN13:
|
||||||
|
return generateEAN13(content, filename)
|
||||||
|
case CODE39:
|
||||||
|
return generateCode39(content, filename)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("条形码类型不存在: %s", barcodeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成Code128条形码
|
||||||
|
func generateCode128(content, filename string) (string, error) {
|
||||||
|
// 创建条形码
|
||||||
|
code, err := code128.Encode(content)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建条形码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩放条形码尺寸
|
||||||
|
scaledCode, err := barcode.Scale(code, 250, 85)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("缩放条形码尺寸失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建带文字的图像
|
||||||
|
imgWidth := 250
|
||||||
|
imgHeight := 110 // 条形码高度 + 文字区域高度
|
||||||
|
dc := gg.NewContext(imgWidth, imgHeight)
|
||||||
|
|
||||||
|
// 设置白色背景
|
||||||
|
dc.SetColor(color.White)
|
||||||
|
dc.Clear()
|
||||||
|
|
||||||
|
dc.DrawImage(scaledCode, 0, 10)
|
||||||
|
|
||||||
|
fontPath := getDefaultFontPath()
|
||||||
|
if fontPath == "" {
|
||||||
|
return "", fmt.Errorf("未找到系统字体文件,请手动指定字体路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置文字属性
|
||||||
|
if err := dc.LoadFontFace("/System/Library/Fonts/Helvetica.ttc", 14); err != nil {
|
||||||
|
// 如果字体文件不存在,使用默认字体
|
||||||
|
dc.LoadFontFace("Arial.ttf", 14)
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.SetColor(color.Black)
|
||||||
|
|
||||||
|
// 在条形码下方居中绘制文字0
|
||||||
|
textY := float64(85 + 20) // 条形码高度100 + 20像素间距
|
||||||
|
textWidth, _ := dc.MeasureString(content)
|
||||||
|
textX := (float64(imgWidth) - textWidth) / 2
|
||||||
|
dc.DrawString(content, textX, textY)
|
||||||
|
|
||||||
|
// 保存图像
|
||||||
|
err = dc.SavePNG(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("保存图像失败: %v", err)
|
||||||
|
}
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成EAN13条形码
|
||||||
|
func generateEAN13(content, filename string) (string, error) {
|
||||||
|
eanCode, err := ean.Encode(content)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建条形码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scaledEanCode, err := barcode.Scale(eanCode, 250, 85)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("缩放条形码尺寸失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建带文字的图像
|
||||||
|
imgWidth := 250
|
||||||
|
imgHeight := 110 // 条形码高度 + 文字区域高度
|
||||||
|
dc := gg.NewContext(imgWidth, imgHeight)
|
||||||
|
|
||||||
|
// 设置白色背景
|
||||||
|
dc.SetColor(color.White)
|
||||||
|
dc.Clear()
|
||||||
|
|
||||||
|
dc.DrawImage(scaledEanCode, 0, 10)
|
||||||
|
|
||||||
|
fontPath := getDefaultFontPath()
|
||||||
|
if fontPath == "" {
|
||||||
|
return "", fmt.Errorf("未找到系统字体文件,请手动指定字体路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置文字属性
|
||||||
|
if err := dc.LoadFontFace("/System/Library/Fonts/Helvetica.ttc", 14); err != nil {
|
||||||
|
// 如果字体文件不存在,使用默认字体
|
||||||
|
dc.LoadFontFace("Arial.ttf", 14)
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.SetColor(color.Black)
|
||||||
|
|
||||||
|
// 在条形码下方居中绘制文字0
|
||||||
|
textY := float64(85 + 20) // 条形码高度100 + 20像素间距
|
||||||
|
textWidth, _ := dc.MeasureString(content)
|
||||||
|
textX := (float64(imgWidth) - textWidth) / 2
|
||||||
|
dc.DrawString(content, textX, textY)
|
||||||
|
|
||||||
|
// 保存图像
|
||||||
|
err = dc.SavePNG(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("保存图像失败: %v", err)
|
||||||
|
}
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成Code39
|
||||||
|
func generateCode39(content, filename string) (string, error) {
|
||||||
|
code, err := code39.Encode(content, true, true)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建条形码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled, err := barcode.Scale(code, 250, 85)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("缩放条形码尺寸失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建带文字的图像
|
||||||
|
imgWidth := 250
|
||||||
|
imgHeight := 110 // 条形码高度 + 文字区域高度
|
||||||
|
dc := gg.NewContext(imgWidth, imgHeight)
|
||||||
|
|
||||||
|
// 设置白色背景
|
||||||
|
dc.SetColor(color.White)
|
||||||
|
dc.Clear()
|
||||||
|
|
||||||
|
dc.DrawImage(scaled, 0, 10)
|
||||||
|
|
||||||
|
fontPath := getDefaultFontPath()
|
||||||
|
if fontPath == "" {
|
||||||
|
return "", fmt.Errorf("未找到系统字体文件,请手动指定字体路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置文字属性
|
||||||
|
if err := dc.LoadFontFace("/System/Library/Fonts/Helvetica.ttc", 14); err != nil {
|
||||||
|
// 如果字体文件不存在,使用默认字体
|
||||||
|
dc.LoadFontFace("Arial.ttf", 14)
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.SetColor(color.Black)
|
||||||
|
|
||||||
|
// 在条形码下方居中绘制文字0
|
||||||
|
textY := float64(85 + 20) // 条形码高度100 + 20像素间距
|
||||||
|
textWidth, _ := dc.MeasureString(content)
|
||||||
|
textX := (float64(imgWidth) - textWidth) / 2
|
||||||
|
dc.DrawString(content, textX, textY)
|
||||||
|
|
||||||
|
// 保存图像
|
||||||
|
err = dc.SavePNG(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("保存图像失败: %v", err)
|
||||||
|
}
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
// =================== 辅助函数 =======================
|
// =================== 辅助函数 =======================
|
||||||
|
|
||||||
// 辅助函数
|
// 辅助函数
|
||||||
@ -1207,21 +1779,81 @@ func ResizeWTToHeightQuality(jsonConfig *C.char, dsWidth, dsHeight C.int) *C.cha
|
|||||||
//export CropImage
|
//export CropImage
|
||||||
func CropImage(jsonConfig *C.char, x, y, width, height C.int) *C.char {
|
func CropImage(jsonConfig *C.char, x, y, width, height C.int) *C.char {
|
||||||
configStr := C.GoString(jsonConfig)
|
configStr := C.GoString(jsonConfig)
|
||||||
xStr := int(x)
|
xInt := int(x)
|
||||||
yStr := int(y)
|
yInt := int(y)
|
||||||
widthStr := int(width)
|
widthInt := int(width)
|
||||||
heightStr := int(height)
|
heightInt := int(height)
|
||||||
var config *Config
|
var config *Config
|
||||||
if err := json.Unmarshal([]byte(configStr), &config); err != nil {
|
if err := json.Unmarshal([]byte(configStr), &config); err != nil {
|
||||||
return C.CString(fmt.Sprintf("解析 config 失败: %v", err))
|
return C.CString(fmt.Sprintf("解析 config 失败: %v", err))
|
||||||
}
|
}
|
||||||
fileName, err := cropImage(config, xStr, yStr, widthStr, heightStr)
|
fileName, err := cropImage(config, xInt, yInt, widthInt, heightInt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return C.CString(fmt.Sprintf("%v", err))
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
}
|
}
|
||||||
return C.CString(fileName)
|
return C.CString(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChineseTextImage 创建带中文字体的文本图片,支持超出部分显示...
|
||||||
|
//
|
||||||
|
//export CreateChineseTextImage
|
||||||
|
func CreateChineseTextImage(text *C.char, width, height C.int, fontSize *C.char, outputPath *C.char) *C.char {
|
||||||
|
textStr := C.GoString(text)
|
||||||
|
widthInt := int(width)
|
||||||
|
heightInt := int(height)
|
||||||
|
fontSizeStr := C.GoString(fontSize)
|
||||||
|
float, err := strconv.ParseFloat(fontSizeStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("转换float64类型失败: %v", err))
|
||||||
|
}
|
||||||
|
outputPathStr := C.GoString(outputPath)
|
||||||
|
textImage, err := createChineseTextImage(textStr, widthInt, heightInt, float, outputPathStr)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
return C.CString(textImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawChineseInfo 绘制书名,作者,出版社信息
|
||||||
|
//
|
||||||
|
//export DrawChineseInfo
|
||||||
|
func DrawChineseInfo(filePath, title, author, publisher, outputPath *C.char) *C.char {
|
||||||
|
filePathStr := C.GoString(filePath)
|
||||||
|
titleStr := C.GoString(title)
|
||||||
|
authorStr := C.GoString(author)
|
||||||
|
publishertr := C.GoString(publisher)
|
||||||
|
outputPathStr := C.GoString(outputPath)
|
||||||
|
|
||||||
|
img, err := loadPNG(filePathStr)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
err = drawChineseText(img, titleStr, authorStr, publishertr)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
err = savePNG(img, outputPathStr)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
return C.CString(fmt.Sprintf("图片保存成功,路径: %s", outputPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateBarcode 根据类型生成条形码
|
||||||
|
//
|
||||||
|
//export GenerateBarcode
|
||||||
|
func GenerateBarcode(barcodeType, content, filename *C.char) *C.char {
|
||||||
|
barcodeTypeStr := C.GoString(barcodeType)
|
||||||
|
contentStr := C.GoString(content)
|
||||||
|
filenameStr := C.GoString(filename)
|
||||||
|
|
||||||
|
filename, err := generateBarcode(barcodeTypeStr, contentStr, filenameStr)
|
||||||
|
if err != nil {
|
||||||
|
return C.CString(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
return C.CString(filename)
|
||||||
|
}
|
||||||
|
|
||||||
// 导出函数:释放C字符串内存
|
// 导出函数:释放C字符串内存
|
||||||
//
|
//
|
||||||
//export FreeCString
|
//export FreeCString
|
||||||
|
|||||||
@ -79,68 +79,68 @@ func cStr(ptr uintptr) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
//func main() {
|
||||||
|
//
|
||||||
//config := &Config{
|
// //config := &Config{
|
||||||
// OutputDir: "D:\\isbn_images\\result", // 输出根目录
|
// // OutputDir: "D:\\isbn_images\\result", // 输出根目录
|
||||||
// FileName: "D:\\isbn_images\\result\\97800079351851.jpg",
|
// // FileName: "D:\\isbn_images\\result\\97800079351851.jpg",
|
||||||
// //MatchDir: "matched", // 满足条件的图片目录
|
// // //MatchDir: "matched", // 满足条件的图片目录
|
||||||
// //UnmatchDir: "unmatched", // 不满足条件的图片目录
|
// // //UnmatchDir: "unmatched", // 不满足条件的图片目录
|
||||||
// //WhiteDir: "white",
|
// // //WhiteDir: "white",
|
||||||
// //EqualHeightDir: "equalHeight",
|
// // //EqualHeightDir: "equalHeight",
|
||||||
// //WhiteHeightZoomDir: "whiteHeightZoom",
|
// // //WhiteHeightZoomDir: "whiteHeightZoom",
|
||||||
// CropDir: "crop",
|
// // CropDir: "crop",
|
||||||
// MinWhitePct: 0.1, // 纯白占比下限 10%
|
// // MinWhitePct: 0.1, // 纯白占比下限 10%
|
||||||
// MaxWhitePct: 0.65, // 纯白占比上限 90%
|
// // MaxWhitePct: 0.65, // 纯白占比上限 90%
|
||||||
// Extensions: []string{"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
// // Extensions: []string{"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
||||||
//}
|
// //}
|
||||||
|
//
|
||||||
//dll, err := InitImageDll()
|
// //dll, err := InitImageDll()
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
////err = dll.ProcessImage(config)
|
|
||||||
// //if err != nil {
|
// //if err != nil {
|
||||||
// // fmt.Println(err)
|
// // fmt.Println(err)
|
||||||
// //}
|
// //}
|
||||||
|
// ////err = dll.ProcessImage(config)
|
||||||
|
// ////if err != nil {
|
||||||
|
// //// fmt.Println(err)
|
||||||
|
// ////}
|
||||||
|
// //
|
||||||
|
// //image, err := dll.CreateWhiteBottomCenteredImage(config, 800, 800)
|
||||||
|
// //if err != nil {
|
||||||
|
// // fmt.Println(err)
|
||||||
|
// //}
|
||||||
|
// //fmt.Println(image)
|
||||||
//
|
//
|
||||||
//image, err := dll.CreateWhiteBottomCenteredImage(config, 800, 800)
|
// // 图片缩放
|
||||||
//if err != nil {
|
// //quality, err := resizeWTToHeightQuality(config, 800, 500)
|
||||||
// fmt.Println(err)
|
// //if err != nil {
|
||||||
//}
|
// // fmt.Println(err)
|
||||||
//fmt.Println(image)
|
// //}
|
||||||
|
// //fmt.Println(quality)
|
||||||
// 图片缩放
|
|
||||||
//quality, err := resizeWTToHeightQuality(config, 800, 500)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//fmt.Println(quality)
|
|
||||||
|
|
||||||
//// 图片裁切
|
|
||||||
//image, err := cropImage(config, 100, 0, 300, 300)
|
|
||||||
//if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
//}
|
|
||||||
//fmt.Println(image)
|
|
||||||
|
|
||||||
//file := "D:\\isbn_images\\result\\123\\qrcode.jpg"
|
|
||||||
//
|
//
|
||||||
//code, s, err := scanQRCodeNew(file)
|
// //// 图片裁切
|
||||||
|
// //image, err := cropImage(config, 100, 0, 300, 300)
|
||||||
|
// //if err != nil {
|
||||||
|
// // fmt.Println(err)
|
||||||
|
// //}
|
||||||
|
// //fmt.Println(image)
|
||||||
|
//
|
||||||
|
// //file := "D:\\isbn_images\\result\\123\\qrcode.jpg"
|
||||||
|
// //
|
||||||
|
// //code, s, err := scanQRCodeNew(file)
|
||||||
|
// //if err != nil {
|
||||||
|
// // fmt.Println(err)
|
||||||
|
// //}
|
||||||
|
// //if code {
|
||||||
|
// // fmt.Println(s)
|
||||||
|
// //}
|
||||||
|
//
|
||||||
|
// code, err := generateQRCode("你好", 500, 500, "D:\\isbn_images\\result\\123\\qrcode.jpg")
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
//if code {
|
// fmt.Println(code)
|
||||||
// fmt.Println(s)
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
code, err := generateQRCode("你好", 500, 500, "D:\\isbn_images\\result\\123\\qrcode.jpg")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
fmt.Println(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成二维码
|
// 生成二维码
|
||||||
func generateQRCode1() {
|
func generateQRCode1() {
|
||||||
// 一行代码生成二维码
|
// 一行代码生成二维码
|
||||||
|
|||||||
@ -1,41 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
//func main() {
|
//func main() {
|
||||||
// // ==================== 在这里设置你的参数 ====================
|
// //// 测试长文本
|
||||||
// config := &Config{
|
// //longText := "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
|
||||||
// OutputDir: "D:\\isbn_images\\result", // 输出根目录
|
// // "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
|
||||||
// FileName: "D:\\isbn_images\\result\\9771671688095.jpg",
|
// // "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
|
||||||
// MatchDir: "matched", // 满足条件的图片目录
|
// // "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
|
||||||
// UnmatchDir: "unmatched", // 不满足条件的图片目录
|
// // "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
|
||||||
// EqualHeightDir: "equalHeight",
|
// // "这是一段非常长的中文文本,需要测试自动换行功能。我们希望当文本超过图片宽度时,能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容,比如这里有一些English words mixed with中文。同时,我们也要测试标点符号的处理,例如逗号、句号、感叹号!问号?以及各种括号(包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。"
|
||||||
// WhiteDir: "white",
|
// //
|
||||||
// WhiteBorderPngDir: "whiteBorderPng",
|
// //text := longText
|
||||||
// MinWhitePct: 0.1, // 纯白占比下限 10%
|
// //
|
||||||
// MaxWhitePct: 0.65, // 纯白占比上限 90%
|
// //err := createChineseTextImage(text, 800, 600, 20, "chinese_output.png")
|
||||||
// Extensions: []string{"jpg", "jpeg", "png", "gif", "bmp", "webp"},
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //err := processImage(config)
|
|
||||||
// //if err != nil {
|
// //if err != nil {
|
||||||
// // fmt.Println(err)
|
// // fmt.Printf("生成图片失败: %v\n", err)
|
||||||
|
// // fmt.Println("\n解决方案:")
|
||||||
|
// // fmt.Println("1. 下载中文字体(如思源黑体)")
|
||||||
|
// // fmt.Println("2. 修改代码中的字体路径")
|
||||||
|
// // fmt.Println("3. 将字体文件放在项目目录中")
|
||||||
|
// // return
|
||||||
// //}
|
// //}
|
||||||
// //
|
// //
|
||||||
// //file, err := resizeToHeightQuality(config, 700)
|
// //fmt.Println("图片已生成: chinese_output.png")
|
||||||
// //if err != nil {
|
|
||||||
// // fmt.Println(err)
|
|
||||||
// //}
|
|
||||||
// //fmt.Println(file)
|
|
||||||
// //config.FileName = file
|
|
||||||
// //fmt.Println(config)
|
|
||||||
// //file1, err := createWhiteBottomCenteredImage(config, 800, 800)
|
|
||||||
// //if err != nil {
|
|
||||||
// // fmt.Println(err)
|
|
||||||
// //}
|
|
||||||
// //fmt.Println(file1)
|
|
||||||
//
|
//
|
||||||
// png, err := removeWhiteBorderAndPNG(config)
|
// img, err := loadPNG("image/123.png")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// // 绘制文本信息
|
||||||
|
// err = drawChineseText(img, "Go语言高级编程Go语言高级编程Go语言高级编程Go语言高级编程Go语言高级编程Go语言高级编程", "作者:王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明王小明", "出版社:人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社人民邮电出版社")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// err = savePNG(img, "image/book_cover.png")
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// fmt.Println(png)
|
|
||||||
//}
|
//}
|
||||||
|
|||||||
Binary file not shown.
@ -108,6 +108,10 @@ extern __declspec(dllexport) char* OutGetGoodsListMsgFromSelfShop(char* token, c
|
|||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData);
|
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData);
|
||||||
|
|
||||||
|
// OutAddGoodsAndFile 整合添加商品和获取孔网图片
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* OutAddGoodsAndFile(char* token, char* proxy, char* filePath, char* formData);
|
||||||
|
|
||||||
// OutDelGoodsFromSelfShop 删除商品-已登的店铺
|
// OutDelGoodsFromSelfShop 删除商品-已登的店铺
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
|
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
|
||||||
@ -144,6 +148,22 @@ extern __declspec(dllexport) char* KongfzOrderDeliver(int appId, char* appSecret
|
|||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* KongfzOrderSynchronization(int appId, char* appSecret, char* accessToken, char* shippingComName, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
|
extern __declspec(dllexport) char* KongfzOrderSynchronization(int appId, char* appSecret, char* accessToken, char* shippingComName, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
|
||||||
|
|
||||||
|
// KongfzImageUpload 上传图片接口
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* KongfzImageUpload(int appId, char* appSecret, char* accessToken, char* filePath, char* savePath);
|
||||||
|
|
||||||
|
// KongfzShopItemAdd 添加店铺商品
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* KongfzShopItemAdd(int appId, char* appSecret, char* accessToken, char* shopItemAddJson);
|
||||||
|
|
||||||
|
// KongfzOrderList 查询订单列表
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* KongfzOrderList(int appId, char* appSecret, char* accessToken, char* orderListJson);
|
||||||
|
|
||||||
|
// KongfzOrderGet 查询单个订单
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* KongfzOrderGet(int appId, char* appSecret, char* accessToken, char* userType, int orderId);
|
||||||
|
|
||||||
// Initialize 初始化配置
|
// Initialize 初始化配置
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* Initialize(char* configJSON);
|
extern __declspec(dllexport) char* Initialize(char* configJSON);
|
||||||
|
|||||||
847
kongfz/kongfz.go
847
kongfz/kongfz.go
File diff suppressed because it is too large
Load Diff
190
kongfz/kongfzDll.go
Normal file
190
kongfz/kongfzDll.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||||
|
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb",
|
||||||
|
// "D:/work/image/9787511220660.jpg",
|
||||||
|
// "D:\\work\\project\\kfzgw-info\\kongfz")
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(upload)
|
||||||
|
|
||||||
|
//var q OrderQueryParams
|
||||||
|
//q.UserType = "buyer"
|
||||||
|
//marshal, err := json.Marshal(q)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(string(marshal))
|
||||||
|
//
|
||||||
|
//list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||||
|
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal))
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(list)
|
||||||
|
//
|
||||||
|
//key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "")
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(key)
|
||||||
|
//
|
||||||
|
//upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "",
|
||||||
|
// "D:/work/image/9787115460622.jpg")
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(upload)
|
||||||
|
|
||||||
|
//uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "",
|
||||||
|
// "D:/work/image/9787511220660.jpg")
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(uploadd)
|
||||||
|
|
||||||
|
appId := 576
|
||||||
|
appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8"
|
||||||
|
accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92"
|
||||||
|
shippingComName := "韵达快递"
|
||||||
|
orderId := 275723461
|
||||||
|
shippingId := ""
|
||||||
|
shippingCom := ""
|
||||||
|
shipmentNum := "312944151258647"
|
||||||
|
userDefined := ""
|
||||||
|
moreShipmentNum := ""
|
||||||
|
|
||||||
|
synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
fmt.Println(synchronization)
|
||||||
|
|
||||||
|
//data := ItemInfo{
|
||||||
|
// ItemName: "至关重要的关系",
|
||||||
|
// ISBN: "9787550213869",
|
||||||
|
// Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译",
|
||||||
|
// Press: "北京联合出版公司",
|
||||||
|
// YearsGroup: "1",
|
||||||
|
// PubDate: "2013-04",
|
||||||
|
// PubDateYear: "2013",
|
||||||
|
// PubDateMonth: "4",
|
||||||
|
// Edition: "1",
|
||||||
|
// Binding: "2", // 2可能表示平装
|
||||||
|
// Quality: "95",
|
||||||
|
// Price: "1500",
|
||||||
|
// Number: "2",
|
||||||
|
// ItemSn: "a1",
|
||||||
|
// MouldId: "909963",
|
||||||
|
// DeliverTime: "48h",
|
||||||
|
// IsDeliverTimeDefault: "48h",
|
||||||
|
// CatId: "43000000000000000",
|
||||||
|
// PageType: "add",
|
||||||
|
// OldCatId: "0000000000000000000",
|
||||||
|
// Tpl: "13",
|
||||||
|
// BearShipping: "buyer",
|
||||||
|
// WeightPiece: "1",
|
||||||
|
// IsOnSale: "1",
|
||||||
|
// Weight: "0.5",
|
||||||
|
// IsUseMould: "1",
|
||||||
|
// DeliverTimeGroup: "1",
|
||||||
|
//}
|
||||||
|
//marshal, err := json.Marshal(data)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//file, err := outAddGoodsAndFile("7feb7d192911b5440a4c83d81decd568e3cb4833", "", "D:/work/image/9787115460622.jpg", string(marshal))
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(string(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ItemInfo struct {
|
||||||
|
ItemName string `json:"itemName"`
|
||||||
|
ISBN string `json:"isbn"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Press string `json:"press"`
|
||||||
|
YearsGroup string `json:"yearsGroup"`
|
||||||
|
PubDate string `json:"pubDate"`
|
||||||
|
PubDateYear string `json:"pubDateYear"`
|
||||||
|
PubDateMonth string `json:"pubDateMonth"`
|
||||||
|
Edition string `json:"edition"`
|
||||||
|
Binding string `json:"binding"`
|
||||||
|
Quality string `json:"quality"`
|
||||||
|
Price string `json:"price"`
|
||||||
|
Number string `json:"number"`
|
||||||
|
ItemSn string `json:"itemSn"`
|
||||||
|
Images []ImageInfo `json:"images"`
|
||||||
|
MouldId string `json:"mouldId"`
|
||||||
|
DeliverTime string `json:"deliverTime"`
|
||||||
|
IsDeliverTimeDefault string `json:"isDeliverTimeDefault"`
|
||||||
|
CatId string `json:"catId"`
|
||||||
|
PageType string `json:"pageType"`
|
||||||
|
OldCatId string `json:"oldCatId"`
|
||||||
|
Tpl string `json:"tpl"`
|
||||||
|
BearShipping string `json:"bearShipping"`
|
||||||
|
WeightPiece string `json:"weightPiece"`
|
||||||
|
IsOnSale string `json:"isOnSale"`
|
||||||
|
Weight string `json:"weight"`
|
||||||
|
IsUseMould string `json:"isUseMould"`
|
||||||
|
DeliverTimeGroup string `json:"deliverTimeGroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageInfo struct {
|
||||||
|
ImgUrl string `json:"imgUrl"`
|
||||||
|
IsMain int `json:"isMain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderQueryParams 订单查询参数结构体
|
||||||
|
type OrderQueryParams struct {
|
||||||
|
// UserType 用户类型。取值 seller: 卖家;buyer: 买家。
|
||||||
|
UserType string `json:"userType" binding:"required"`
|
||||||
|
|
||||||
|
// OrderStatus 订单状态。默认为全部订单。
|
||||||
|
// Trading:交易中;
|
||||||
|
// Pending:待确认;
|
||||||
|
// ConfirmedToPay:待付款;
|
||||||
|
// PaidToShip:待发货;
|
||||||
|
// ShippedToReceipt:待确认收货;
|
||||||
|
// Refund:退货退款中;
|
||||||
|
// sellerReviewed:待评价;
|
||||||
|
// Successful:成功完成;
|
||||||
|
// RefundDeald:已退货退款;
|
||||||
|
// BuyerCancelled:买家取消;
|
||||||
|
// SellerCancelledBeforeConfirm:卖家取消;
|
||||||
|
// AdminClosedBeforeConfirm:管理员关闭。
|
||||||
|
OrderStatus string `json:"orderStatus,omitempty"`
|
||||||
|
|
||||||
|
// PageNum 页码。默认为:1
|
||||||
|
PageNum int `json:"pageNum,omitempty"`
|
||||||
|
|
||||||
|
// PageSize 每页最大条数。默认为:20,最大值为:100
|
||||||
|
PageSize int `json:"pageSize,omitempty"`
|
||||||
|
|
||||||
|
// IsDelete 是否已删除。默认查询未删除的
|
||||||
|
IsDelete int `json:"isDelete,omitempty"`
|
||||||
|
|
||||||
|
// StartDate 根据订单生成时间查询的起始日期,格式为yyyy-mm-dd,时区为GMT+8
|
||||||
|
StartDate string `json:"startDate,omitempty"`
|
||||||
|
|
||||||
|
// EndDate 根据订单生成时间查询的截止日期,格式为yyyy-mm-dd,时区为GMT+8
|
||||||
|
EndDate string `json:"endDate,omitempty"`
|
||||||
|
|
||||||
|
// StartTime 根据订单生成时间查询的起始时间,格式为yyyy-mm-dd hh:mm:ss,时区为GMT+8
|
||||||
|
StartTime string `json:"startTime,omitempty"`
|
||||||
|
|
||||||
|
// EndTime 根据订单生成时间查询的截止时间,格式为yyyy-mm-dd hh:mm:ss,时区为GMT+8
|
||||||
|
EndTime string `json:"endTime,omitempty"`
|
||||||
|
|
||||||
|
// StartUpdateTime 根据订单更新时间查询的起始时间,格式为yyyy-mm-dd hh:mm:ss,时区为GMT+8
|
||||||
|
StartUpdateTime string `json:"startUpdateTime,omitempty"`
|
||||||
|
|
||||||
|
// EndUpdateTime 根据订单更新时间查询的截止时间,格式为yyyy-mm-dd hh:mm:ss,时区为GMT+8
|
||||||
|
EndUpdateTime string `json:"endUpdateTime,omitempty"`
|
||||||
|
}
|
||||||
@ -149,10 +149,11 @@ dll.AppendRows(handle,rowsData,rowCount)
|
|||||||
|--|--------|--|----------------------------|
|
|--|--------|--|----------------------------|
|
||||||
| handle | int64 | 是 | 文件句柄 |
|
| handle | int64 | 是 | 文件句柄 |
|
||||||
| rowsData | []byte | 是 | 写入的数据,将[][]string转换成[]byte |
|
| rowsData | []byte | 是 | 写入的数据,将[][]string转换成[]byte |
|
||||||
|
| dataSize | int | 是 | 写入数据的大小 |
|
||||||
| rowCount | int | 是 | 数据行数 |
|
| rowCount | int | 是 | 数据行数 |
|
||||||
### 响应示例
|
### 响应示例
|
||||||
```
|
```
|
||||||
文件数据行数:int64
|
文件是否追加成功:int64 0:成功
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5.获取总行数--GetRowCount
|
## 5.获取总行数--GetRowCount
|
||||||
|
|||||||
296
md/kongfz.md
296
md/kongfz.md
@ -1696,7 +1696,7 @@ orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
|||||||
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
||||||
| shippingComName | string | 是 | 快递名称 |
|
| shippingComName | string | 是 | 快递名称 |
|
||||||
| orderId | int | 是 | 订单编号 |
|
| orderId | int | 是 | 订单编号 |
|
||||||
| shippingId | string | 是 | 配送方式 |
|
| shippingId | string | 否 | 配送方式 |
|
||||||
| shippingCom | string | 否 | 快递公司。当shippingId!=noLogistics时,此参数为必填。取值参考kongfz.delivery.method.list接口的返回值 |
|
| shippingCom | string | 否 | 快递公司。当shippingId!=noLogistics时,此参数为必填。取值参考kongfz.delivery.method.list接口的返回值 |
|
||||||
| shipmentNum | string | 否 | 快递单号。当shippingId!=noLogistics时,此参数为必填。 |
|
| shipmentNum | string | 否 | 快递单号。当shippingId!=noLogistics时,此参数为必填。 |
|
||||||
| userDefined | string | 否 | 用户自定义物流公司。当shippingCom=other时,此参数为必填。 |
|
| userDefined | string | 否 | 用户自定义物流公司。当shippingCom=other时,此参数为必填。 |
|
||||||
@ -1717,6 +1717,300 @@ orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 15.上传图片接口--KongfzImageUpload
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.KongfzImageUpload(appId, appSecret, accessToken, filePath, savePath)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|----|-------------------------------|
|
||||||
|
| appId | int | 是 | 开放平台分配给应用的AppId |
|
||||||
|
| appSecret | string | 是 | App密钥 |
|
||||||
|
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
||||||
|
| filePath | string | 是 | 图片url/本地图片路径 |
|
||||||
|
| savePath | string | 是 | 图片下载路径,如果是图片url需要下载到本地 |
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"requestId": "bAo3ZN679gaPgR9Y",
|
||||||
|
"requestMethod": "kongfz.image.upload",
|
||||||
|
"successResponse": {
|
||||||
|
"image": {
|
||||||
|
"url": "https://www.kfzimg.com/sw/kfzimg/2868/02698c12fec32cc69c_s.jpg" //图片地址
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errorResponse": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 16.添加店铺商品--KongfzShopItemAdd
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.KongfzShopItemAdd(appId, appSecret, accessToken, shopItemAddJson)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|----|-------------------------------|
|
||||||
|
| appId | int | 是 | 开放平台分配给应用的AppId |
|
||||||
|
| appSecret | string | 是 | App密钥 |
|
||||||
|
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
||||||
|
| shopItemAddJson | string | 是 | 店铺商品请求结构体字符串 |
|
||||||
|
#### shopItemAddJson 请求结构体
|
||||||
|
添加商品的业务参数比较复杂,根据商品分类的不同,适配不同的模板。以下参数将分别列出模板公共参数和模板特有参数。
|
||||||
|
可以查看:https://open.kongfz.com/doc/api/shop/kongfz-shop-item-add
|
||||||
|
### 成功响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kongfzShopItemAddResponse": {
|
||||||
|
"requestId": "mKPxiCD0dgaPSRib",
|
||||||
|
"requestMethod": "kongfz.shop.item.add",
|
||||||
|
"successResponse": {
|
||||||
|
"item": {
|
||||||
|
"itemId": 1343492600, //商品编号
|
||||||
|
"addTime": "2019-06-22 16:12:17" //添加时间
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errorResponse": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 失败响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"requestId": "8lDbCNSxzH4UHRx8",
|
||||||
|
"requestMethod": "kongfz.shop.item.add",
|
||||||
|
"successResponse": null,
|
||||||
|
"errorResponse": {
|
||||||
|
"code": 3003,
|
||||||
|
"msg": "Service error",
|
||||||
|
"subCode": "Failed To Add Item",
|
||||||
|
"subMsg": "添加商品失败",
|
||||||
|
"data": { //字段错误的具体信息会在data中给出,key是出错的字段名,value是错误的原因
|
||||||
|
"mouldId": "请先添加运费模板"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 17.查询订单列表--KongfzOrderList
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.KongfzOrderList(appId, appSecret, accessToken, orderListJson)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|----|-------------------------------|
|
||||||
|
| appId | int | 是 | 开放平台分配给应用的AppId |
|
||||||
|
| appSecret | string | 是 | App密钥 |
|
||||||
|
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
||||||
|
| orderListJson | string | 是 | 查询订单请求结构体字符串 |
|
||||||
|
#### orderListJson 请求结构体
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userType": "String",
|
||||||
|
"orderStatus": "String",
|
||||||
|
"pageNum": "Integer",
|
||||||
|
"pageSize": "Integer",
|
||||||
|
"isDelete": "Integer",
|
||||||
|
"startDate": "String",
|
||||||
|
"endDate": "String",
|
||||||
|
"startTime": "String",
|
||||||
|
"endTime": "String",
|
||||||
|
"startUpdateTime": "String",
|
||||||
|
"endUpdateTime": "String"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"requestId": "VNI5AWvIW5cRLR9l",
|
||||||
|
"requestMethod": "kongfz.order.list",
|
||||||
|
"successResponse": {
|
||||||
|
"total": 5, //总条数
|
||||||
|
"pages": 1, //总页数
|
||||||
|
"size": 5, //当前页条数
|
||||||
|
"pageSize": 20, //每页最大条数
|
||||||
|
"pageNum": 1, //当前页码
|
||||||
|
"list": [ //订单列表
|
||||||
|
{
|
||||||
|
"orderId": 25259131, //订单编号
|
||||||
|
"createdTime": "2014/06/25 14:36:59", //订单生成时间
|
||||||
|
"shopId": 22281, //店铺编号
|
||||||
|
"shopName": "俞含的书摊", //店铺名称
|
||||||
|
"shopkeeperId": 2522488, //卖家用户编号
|
||||||
|
"shipmentNum": "", //快递单号
|
||||||
|
"shippingCom": "", //快递公司编码
|
||||||
|
"shippingComName": "", //快递公司名称
|
||||||
|
"shippingId": "express", //配送方式
|
||||||
|
"shippingName": "快递", //配送方式名称
|
||||||
|
"shippingFee": "8.00", //快递费
|
||||||
|
"goodsAmount": "15.00", //订单商品金额
|
||||||
|
"favorableMoney": "0.00", //商品优惠金额
|
||||||
|
"orderAmount": "23.00", //订单金额
|
||||||
|
"userId": 3723679, //买家用户编号
|
||||||
|
"nickname": "买家昵称", //买家昵称
|
||||||
|
"receiver": "姓名, 18812345678, 山东省青岛市市北区顺兴路152号404户, 266021", //收件人信息
|
||||||
|
"receiverInfo": {
|
||||||
|
"area": "24006002000",
|
||||||
|
"zipCode": "266021",
|
||||||
|
"provName": "山东省",
|
||||||
|
"address": "顺兴路152号404户",
|
||||||
|
"cityName": "青岛市",
|
||||||
|
"areaName": "市北区",
|
||||||
|
"receiverName": "姓名",
|
||||||
|
"mobile": "18812345678",
|
||||||
|
"phoneNum": ""
|
||||||
|
},
|
||||||
|
"sellerFlagType": 1, //备注类型,孔网是按颜色区分,0是未打标记,1:红,2:黄,3:绿,4:蓝,5:紫
|
||||||
|
"sellerRemarkText": "测试备注信息",
|
||||||
|
"orderStatus": "SellerCancelledBeforeConfirm", //订单状态
|
||||||
|
"orderStatusName": "卖家已取消", //订单状态名称
|
||||||
|
"promotionId": 0, //活动编号
|
||||||
|
"itemsCount": 1, //订单商品数
|
||||||
|
"items": [ //订单商品
|
||||||
|
{
|
||||||
|
"itemId": 250140272, //商品编号
|
||||||
|
"itemSn": "740", //商品货号
|
||||||
|
"number": 1, //购买商品数
|
||||||
|
"itemName": "玉娇龙 下", //商品名称
|
||||||
|
"img": "", //商品图片
|
||||||
|
"isCancel": false, //商品是否被取消
|
||||||
|
"orderId": 25259131, //订单编号
|
||||||
|
"price": "15.00", //商品价格
|
||||||
|
"favorableAmount": "0.00", //商品总优惠金额,即:setFavAmount+couponFavAmount,如果购买了多件,为多件商品的总优惠
|
||||||
|
"setFavAmount": "0.00", //卖家设置优惠金额,卖家设置的订单优惠均摊到商品上的优惠,如果购买了多件,为多件商品的总优惠
|
||||||
|
"couponFavAmount": "0.00", //优惠券优惠金额,订单使用了优惠券时,均摊到商品上的优惠金额,如果购买了多件,为多件商品的总优惠
|
||||||
|
"realAmount": "15.00", //商品实付金额, 如果购买了多件,为多件商品的实付总金额
|
||||||
|
"cancelMan": "unkown", //取消商品者,unkown:未知,seller:卖家,buyer:买家
|
||||||
|
"quality": "", //商品品相
|
||||||
|
"isbn": "" //图书ISBN
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errorResponse": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 18.整合添加商品和获取孔网图片--OutAddGoodsAndFile
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.OutAddGoodsAndFile(token, proxy, filePath, formData)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|----|---------------------------|
|
||||||
|
| token | string | 是 | 孔网token |
|
||||||
|
| proxy | string | 是 | 代理服务器IP |
|
||||||
|
| filePath | string | 否 | 图片路径 |
|
||||||
|
| formData | string | 是 | 商品JSON字符串 |
|
||||||
|
### 成功响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data":9395776720,
|
||||||
|
"errType":"",
|
||||||
|
"message":"",
|
||||||
|
"status":1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询单个订单--KongfzOrderGet
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.KongfzOrderGet(appId, appSecret, accessToken, userType,orderId)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|----|---------------------------|
|
||||||
|
| appId | int | 是 | 开放平台分配给应用的AppId |
|
||||||
|
| appSecret | string | 是 | App密钥 |
|
||||||
|
| accessToken | string | 否 | 用户登录授权成功后,开放平台颁发给应用的授权信息 |
|
||||||
|
| userType | string | 是 | 用户类型。取值 seller: 卖家;buyer: 买家 |
|
||||||
|
| orderId | string | 是 | 订单编号 |
|
||||||
|
### 成功响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"requestId": "GH13BxApG5mRPRl0",
|
||||||
|
"requestMethod": "kongfz.order.get",
|
||||||
|
"successResponse": {
|
||||||
|
"orderId": 73412393, //订单编号
|
||||||
|
"createdTime": "2019-05-13 09:31:26", //订单生成时间
|
||||||
|
"shopId": 24671, //店铺编号
|
||||||
|
"shopName": "网上测试书店(不售书)", //店铺名称
|
||||||
|
"shopkeeperId": 3361935, //卖家用户编号
|
||||||
|
"shipmentNum": "sa04465846222", //快递单号
|
||||||
|
"shippingCom": "registeredPrint", //快递公司编码
|
||||||
|
"shippingComName": "挂号印刷品", //快递公司名称
|
||||||
|
"shippingId": "registerPost", //配送方式
|
||||||
|
"shippingName": "挂号印刷品", //配送方式名称
|
||||||
|
"shippingFee": "10.00", //运费
|
||||||
|
"goodsAmount": "1.00", //商品金额
|
||||||
|
"favorableMoney": "0.00", //订单商品优惠金额
|
||||||
|
"orderAmount": "11.00", //订单金额
|
||||||
|
"userId": 3361935, //用户编号
|
||||||
|
"nickname": "网络测试书店", //用户昵称
|
||||||
|
"receiver": "姓名, 18812345678, 山东省青岛市市北区顺兴路152号404户, 266021", //收件人信息
|
||||||
|
"receiverInfo": {
|
||||||
|
"area": "24006002000",
|
||||||
|
"zipCode": "266021",
|
||||||
|
"provName": "山东省",
|
||||||
|
"address": "顺兴路152号404户",
|
||||||
|
"cityName": "青岛市",
|
||||||
|
"areaName": "市北区",
|
||||||
|
"receiverName": "姓名",
|
||||||
|
"mobile": "18812345678",
|
||||||
|
"phoneNum": ""
|
||||||
|
},
|
||||||
|
"sellerFlagType": 1, //备注类型,孔网是按颜色区分,0是未打标记,1:红,2:黄,3:绿,4:蓝,5:紫
|
||||||
|
"sellerRemarkText": "测试备注信息",
|
||||||
|
"orderStatus": "Shipped-Returning", //订单状态
|
||||||
|
"orderStatusName": "申请退货中", //订单状态名称
|
||||||
|
"promotionId": 0, //活动编号
|
||||||
|
"itemsCount": 1, //订单商品数
|
||||||
|
"items": [ //订单商品
|
||||||
|
{
|
||||||
|
"itemId": 1265078240, //商品编号
|
||||||
|
"itemSn": "", //商品货号
|
||||||
|
"number": 1, //购买商品数
|
||||||
|
"itemName": "测试勿拍19051003", //商品名称
|
||||||
|
"img": "//www.kfzimg.com/G07/M00/10/0E/q4YBAFzVOgGAUQvkAAFgEf3-3BU117_s.jpg",//商品图片
|
||||||
|
"isCancel": false, //商品是否被取消
|
||||||
|
"orderId": 73412393, //订单编号
|
||||||
|
"price": "1.00", //商品价格
|
||||||
|
"favorableAmount": "0.00", //商品总优惠金额,即:setFavAmount+couponFavAmount,如果购买了多件,为多件商品的总优惠
|
||||||
|
"setFavAmount": "0.00", //卖家设置优惠金额,卖家设置的订单优惠均摊到商品上的优惠,如果购买了多件,为多件商品的总优惠
|
||||||
|
"couponFavAmount": "0.00", //优惠券优惠金额,订单使用了优惠券时,均摊到商品上的优惠金额,如果购买了多件,为多件商品的总优惠
|
||||||
|
"realAmount": "1.00", //商品实付金额, 如果购买了多件,为多件商品的实付总金额
|
||||||
|
"cancelMan": "unkown", //取消商品者,unkown:未知,seller:卖家,buyer:买家
|
||||||
|
"quality": "六五品", //商品品相
|
||||||
|
"isbn": "" //图书ISBN
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"express": [ //物流信息
|
||||||
|
{
|
||||||
|
"context": "已签收,他人代收:中通快递收,投递员:齐东事:18226889971,"
|
||||||
|
"time": "2019-05-20 11:47:38",
|
||||||
|
"status": "签收" //状态字段不是都存在,一般只存在于最新的一条记录中
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"records": [ //订单操作记录
|
||||||
|
{
|
||||||
|
"nickname": "网络测试书店",
|
||||||
|
"remark": "买家创建退货协议",
|
||||||
|
"time": "2019-05-19 16:38:59",
|
||||||
|
"userType": "buyer"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errorResponse": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 12.初始化--Initialize(可以不调用)
|
## 12.初始化--Initialize(可以不调用)
|
||||||
### 请求信息
|
### 请求信息
|
||||||
```gotemplate
|
```gotemplate
|
||||||
|
|||||||
307
md/newcsv.md
Normal file
307
md/newcsv.md
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
# csv.dll 使用教程
|
||||||
|
## 1.创建DLL工具实例
|
||||||
|
### 加载DLL文件
|
||||||
|
```gotemplate
|
||||||
|
// CSVDLL CSV文件工具DLL结构
|
||||||
|
type csvDLL struct {
|
||||||
|
dll *syscall.DLL
|
||||||
|
openCSVFile *syscall.Proc // 打开CSV文件
|
||||||
|
writeHeader *syscall.Proc // 写入表头
|
||||||
|
writeRows *syscall.Proc // 写入/覆盖行数据
|
||||||
|
updateRow *syscall.Proc // 修改指定行数据
|
||||||
|
getRow *syscall.Proc // 获取指定行数据
|
||||||
|
closeHandles *syscall.Proc // 关闭指定句柄
|
||||||
|
closeAllHandles *syscall.Proc // 关闭所有句柄
|
||||||
|
freeCString *syscall.Proc // 释放C字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化csvDLL
|
||||||
|
func InitCSVDLL() (*csvDLL, error) {
|
||||||
|
dllPath := filepath.Join("dll", "csv.dll")
|
||||||
|
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("csv DLL 不存在: %s", dllPath)
|
||||||
|
}
|
||||||
|
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||||
|
return nil, fmt.Errorf("加载csv DLL 失败: %s", err)
|
||||||
|
} else {
|
||||||
|
return &csvDLL{
|
||||||
|
dll: dll,
|
||||||
|
openCSVFile: dll.MustFindProc("OpenCSVFile"),
|
||||||
|
writeHeader: dll.MustFindProc("WriteHeader"),
|
||||||
|
writeRows: dll.MustFindProc("WriteRows"),
|
||||||
|
updateRow: dll.MustFindProc("UpdateRow"),
|
||||||
|
getRow: dll.MustFindProc("GetRow"),
|
||||||
|
closeHandles: dll.MustFindProc("CloseHandles"),
|
||||||
|
closeAllHandles: dll.MustFindProc("CloseAllHandles"),
|
||||||
|
freeCString: dll.MustFindProc("FreeCString"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dll, err := InitCSVDLL()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取C字符串
|
||||||
|
```gotemplate
|
||||||
|
// cStr 获取C字符串
|
||||||
|
func (m *csvDLL) cStr(p uintptr) string {
|
||||||
|
if p == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
b := []byte{}
|
||||||
|
for i := uintptr(0); ; i++ {
|
||||||
|
c := *(*byte)(unsafe.Pointer(p + i))
|
||||||
|
if c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
s := string(b)
|
||||||
|
if m.freeCString != nil {
|
||||||
|
m.freeCString.Call(p)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 使用dll函数示例
|
||||||
|
```gotemplate
|
||||||
|
// 打开CSV文件
|
||||||
|
func (m *csvDLL) OpenCSVFile(filename string, delimiter byte, hasHeader bool) (string, error) {
|
||||||
|
proc, err := m.dll.FindProc("OpenCSVFile")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("找不到函数 OpenCSVFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenamePtr, _ := syscall.BytePtrFromString(filename)
|
||||||
|
hasHeaderInt := C.int(0)
|
||||||
|
if hasHeader {
|
||||||
|
hasHeaderInt = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
resultPtr, _, _ := proc.Call(
|
||||||
|
uintptr(unsafe.Pointer(filenamePtr)),
|
||||||
|
uintptr(delimiter),
|
||||||
|
uintptr(hasHeaderInt),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := m.cStr(resultPtr)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 接口详情
|
||||||
|
## 打开CSV文件--OpenCSVFile
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.OpenCSVFile(filename, delimiter, hasHeader)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|----------------|
|
||||||
|
| filename | string | 是 | 文件名称(带路径) |
|
||||||
|
| delimiter | string | 是 | 分隔符(如 ',') |
|
||||||
|
| hasHeader | string | 是 | 是否有表头(0=是) |
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"handleID": 123456789
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "文件不存在: /path/to/file.csv"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 写入表头--WriteHeader
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.WriteHeader(handleID, header)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|--------------|
|
||||||
|
| handleID | int | 是 | CSV文件句柄ID |
|
||||||
|
| header | string | 是 | 表头JSON数组字符串 |
|
||||||
|
### header参数示例
|
||||||
|
```json
|
||||||
|
["列1", "列2", "列3", "列4"]
|
||||||
|
```
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "header JSON解析失败: invalid character '[' looking for beginning of value"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 写入/覆盖行数据--WriteRows
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.WriteRows(handleID, rowsData)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|---------------|
|
||||||
|
| handleID | int | 是 | CSV文件句柄ID |
|
||||||
|
| rowsData | string | 是 | 行数据JSON数组字符串 |
|
||||||
|
### rowsData参数示例
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
["数据1-1", "数据1-2", "数据1-3", "数据1-4"],
|
||||||
|
["数据2-1", "数据2-2", "数据2-3", "数据2-4"],
|
||||||
|
["数据3-1", "数据3-2", "数据3-3", "数据3-4"]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"totalRows": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "rowsData JSON解析失败: invalid character '[' looking for beginning of value"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 修改指定行数据--UpdateRow
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.UpdateRow(handleID, rowNumber, rowData)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|---------------|
|
||||||
|
| handleID | int | 是 | CSV文件句柄ID |
|
||||||
|
| rowNumber | int | 是 | 要修改的行号(从0开始) |
|
||||||
|
| rowData | string | 是 | 新行数据JSON数组字符串 |
|
||||||
|
### rowsData参数示例
|
||||||
|
```json
|
||||||
|
["新数据1", "新数据2", "新数据3", "新数据4"]
|
||||||
|
```
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "成功修改第2行数据"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "行号超出范围: 10, 总行数: 5"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 获取指定行数据--GetRow
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.GetRow(handleID, rowNumber)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|---------------|
|
||||||
|
| handleID | int | 是 | CSV文件句柄ID |
|
||||||
|
| rowNumber | int | 是 | 要获取的行号(从0开始) |
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": ["数据1", "数据2", "数据3", "数据4"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "行号超出范围: 10, 总行数: 5"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 合并两个csv文件--MergeCSVFilesSimple
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.MergeCSVFilesSimple(srcHandleID, dstHandleID, appendMode)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|--------------------------|
|
||||||
|
| srcHandleID | int64 | 是 | 源文件句柄ID |
|
||||||
|
| dstHandleID | int64 | 是 | 目标文件句柄ID(合并后结果) |
|
||||||
|
| appendMode | int | 是 | 0:true=追加模式,1:false=覆盖模式 |
|
||||||
|
### 响应示例
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"totalRows": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "rowsData JSON解析失败: invalid character '[' looking for beginning of value"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关闭指定句柄--CloseHandles
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.CloseHandles(handleID)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|---------------|
|
||||||
|
| handleID | int | 是 | CSV文件句柄ID |
|
||||||
|
### 响应示例
|
||||||
|
```text
|
||||||
|
关闭句柄 123456789 成功!
|
||||||
|
```
|
||||||
|
### 错误响应示例
|
||||||
|
```text
|
||||||
|
句柄不存在: 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关闭所有句柄--CloseAllHandles
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.CloseAllHandles()
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
无
|
||||||
|
### 响应示例
|
||||||
|
```text
|
||||||
|
关闭句柄成功!
|
||||||
|
```
|
||||||
|
|
||||||
|
## 释放C字符串内存--FreeCString
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.FreeCString(str)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--------|--|-----------|
|
||||||
|
| str | string | 是 | 需要释放的字符串 |
|
||||||
18
md/pdd.md
18
md/pdd.md
@ -209,8 +209,7 @@ orderSn, orderState, waybillNo)
|
|||||||
## 4. 拼多多订单同步--PddOrderSynchronization
|
## 4. 拼多多订单同步--PddOrderSynchronization
|
||||||
### 请求信息
|
### 请求信息
|
||||||
```gotemplate
|
```gotemplate
|
||||||
dll.PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsId,
|
dll.PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson)
|
||||||
orderSn, orderState, waybillNo)
|
|
||||||
```
|
```
|
||||||
### 请求参数
|
### 请求参数
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
@ -219,10 +218,7 @@ orderSn, orderState, waybillNo)
|
|||||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||||
| accessToken | string | 是 | 授权令牌 |
|
| accessToken | string | 是 | 授权令牌 |
|
||||||
| logisticsCompany | string | 是 | 物流公司名称 |
|
| logisticsCompany | string | 是 | 物流公司名称 |
|
||||||
| logisticsId | string | 是 | 物流公司ID |
|
| logisticsOnlineSendJson | string | 是 | 拼多多订单同步json字符串 |
|
||||||
| orderSn | string | 是 | 拼多多订单号 |
|
|
||||||
| orderState | string | 是 | 订单状态 |
|
|
||||||
| waybillNo | string | 是 | 运单号 |
|
|
||||||
### 响应示例
|
### 响应示例
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -493,3 +489,13 @@ dll.PddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, reqJson)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 12.释放C字符串内存--FreeCString
|
||||||
|
### 请求信息
|
||||||
|
```gotemplate
|
||||||
|
dll.FreeCString(str)
|
||||||
|
```
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--|--|--|----------|
|
||||||
|
| str | string | 是 | 需要释放的字符串 |
|
||||||
|
|||||||
BIN
pdd/dll/pdd.dll
BIN
pdd/dll/pdd.dll
Binary file not shown.
@ -102,7 +102,7 @@ extern __declspec(dllexport) char* PddErpOrderSync(char* clientId, char* clientS
|
|||||||
|
|
||||||
// PddOrderSynchronization 拼多多订单同步
|
// PddOrderSynchronization 拼多多订单同步
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* PddOrderSynchronization(char* clientId, char* clientSecret, char* accessToken, char* logisticsCompany, char* logisticsId, char* orderSn, char* orderState, char* waybillNo);
|
extern __declspec(dllexport) char* PddOrderSynchronization(char* clientId, char* clientSecret, char* accessToken, char* logisticsCompany, char* logisticsOnlineSendJson);
|
||||||
|
|
||||||
// PddGoodsImgUpload 商品图片上传接口
|
// PddGoodsImgUpload 商品图片上传接口
|
||||||
//
|
//
|
||||||
@ -116,7 +116,11 @@ extern __declspec(dllexport) char* PddGoodsAdd(char* clientId, char* clientSecre
|
|||||||
//
|
//
|
||||||
extern __declspec(dllexport) char* SelfPddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* filePath, char* goodsAddJson);
|
extern __declspec(dllexport) char* SelfPddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* filePath, char* goodsAddJson);
|
||||||
|
|
||||||
// 释放C字符串内存
|
// PddOpenDecryptMaskBatch 批量数据解密脱敏接口
|
||||||
|
//
|
||||||
|
extern __declspec(dllexport) char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson);
|
||||||
|
|
||||||
|
// FreeCString 释放C字符串内存
|
||||||
//
|
//
|
||||||
extern __declspec(dllexport) void FreeCString(char* str);
|
extern __declspec(dllexport) void FreeCString(char* str);
|
||||||
|
|
||||||
|
|||||||
193
pdd/pdd.go
193
pdd/pdd.go
@ -35,7 +35,12 @@ type ErrorWrapper struct {
|
|||||||
ErrorResponse ErrorResponse `json:"error_response"` // 错误响应
|
ErrorResponse ErrorResponse `json:"error_response"` // 错误响应
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateSign 生成签名
|
/*
|
||||||
|
* 生成签名
|
||||||
|
* param params[map[string]interface{}] 生成签名需要的map
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* return 签名
|
||||||
|
*/
|
||||||
func generateSign(params map[string]interface{}, clientSecret string) string {
|
func generateSign(params map[string]interface{}, clientSecret string) string {
|
||||||
// 获取所有键并排序
|
// 获取所有键并排序
|
||||||
keys := make([]string, 0, len(params))
|
keys := make([]string, 0, len(params))
|
||||||
@ -72,7 +77,16 @@ func generateSign(params map[string]interface{}, clientSecret string) string {
|
|||||||
return strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
|
return strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 类目预测
|
/*
|
||||||
|
* 类目预测
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param outerCatId[string] 外部类目ID
|
||||||
|
* param outerCatName[string] 外部类目名称
|
||||||
|
* param outerGoodsName[string] 外部商品名称
|
||||||
|
* return 类目预测响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
func pddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||||||
// API地址
|
// API地址
|
||||||
@ -134,7 +148,12 @@ func pddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
|||||||
return string(responseJSON), nil
|
return string(responseJSON), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快递公司查看
|
/*
|
||||||
|
* 快递公司查看
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* return 快递公司查看响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddLogisticsCompaniesGet(clientId, clientSecret string) (string, error) {
|
func pddLogisticsCompaniesGet(clientId, clientSecret string) (string, error) {
|
||||||
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
|
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
|
||||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
@ -195,7 +214,17 @@ func pddLogisticsCompaniesGet(clientId, clientSecret string) (string, error) {
|
|||||||
return string(responseJSON), nil
|
return string(responseJSON), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// erp打单信息同步
|
/*
|
||||||
|
* erp打单信息同步
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param logisticsId[string] 物流公司编码
|
||||||
|
* param orderSn[string] 订单号
|
||||||
|
* param orderState[string] 订单状态:1-已打单
|
||||||
|
* param waybillNo[string] 运单号
|
||||||
|
* return 快递公司查看响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
func pddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
||||||
orderSn, orderState, waybillNo string) (string, error) {
|
orderSn, orderState, waybillNo string) (string, error) {
|
||||||
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
|
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
|
||||||
@ -266,6 +295,78 @@ func pddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
|||||||
return string(responseJSON), nil
|
return string(responseJSON), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 订单发货通知接口
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param logisticsOnlineSendJson[string] 订单发货通知json字符串
|
||||||
|
* return 订单发货通知接口响应结构体,错误信息
|
||||||
|
*/
|
||||||
|
func pddLogisticsOnlineSend(clientId, clientSecret, accessToken string, logisticsOnlineSendJson string) (string, error) {
|
||||||
|
url := fmt.Sprint("https://gw-api.pinduoduo.com/api/router")
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
// 生成签名
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"type": "pdd.logistics.online.send",
|
||||||
|
"data_type": "JSON",
|
||||||
|
"client_id": clientId,
|
||||||
|
"access_token": accessToken,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
}
|
||||||
|
// 将JSON参数合并到params中
|
||||||
|
toParams, err := addStructToParams(logisticsOnlineSendJson, params)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
sign := generateSign(toParams, clientSecret)
|
||||||
|
|
||||||
|
if sign == "" {
|
||||||
|
return "", fmt.Errorf("生成 sign 签名错误!")
|
||||||
|
}
|
||||||
|
params["sign"] = sign
|
||||||
|
|
||||||
|
request := gorequest.New()
|
||||||
|
resp, body, errs := request.Get(url).
|
||||||
|
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
|
||||||
|
Set("Accept", "application/json, text/plain, */*").
|
||||||
|
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
|
||||||
|
Timeout(30 * time.Second).
|
||||||
|
Send(params).
|
||||||
|
End()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return "", fmt.Errorf("请求失败: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("HTTP状态码异常: %d, 响应: %s", resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
var response map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||||||
|
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异常处理
|
||||||
|
if response["error_response"] != nil {
|
||||||
|
var errorWrapper ErrorWrapper
|
||||||
|
// 解析响应
|
||||||
|
if err := json.Unmarshal([]byte(body), &errorWrapper); err != nil {
|
||||||
|
return "", fmt.Errorf("解析 errorWrapper 失败: %v, 响应内容: %s", err, body)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("请求失败: %v, 错误码: %d", errorWrapper.ErrorResponse.ErrorMsg, errorWrapper.ErrorResponse.ErrorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换成json字符串
|
||||||
|
responseJSON, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||||||
|
}
|
||||||
|
return string(responseJSON), nil
|
||||||
|
}
|
||||||
|
|
||||||
// LogisticsCompany 物流公司信息结构体
|
// LogisticsCompany 物流公司信息结构体
|
||||||
type LogisticsCompany struct {
|
type LogisticsCompany struct {
|
||||||
Available int `json:"available"` // 是否可用
|
Available int `json:"available"` // 是否可用
|
||||||
@ -284,9 +385,23 @@ type LogisticsResponse struct {
|
|||||||
LogisticsCompaniesGetResponse LogisticsCompaniesGetResponse `json:"logistics_companies_get_response"` // 物流公司响应
|
LogisticsCompaniesGetResponse LogisticsCompaniesGetResponse `json:"logistics_companies_get_response"` // 物流公司响应
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼多多订单同步(组合接口:先查物流公司,再同步订单)
|
/*
|
||||||
func pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsId,
|
* 拼多多订单同步(组合接口:先查物流公司,再同步订单)
|
||||||
orderSn, orderState, waybillNo string) (string, error) {
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param logisticsCompany[string] 外部类目ID
|
||||||
|
* param logisticsOnlineSendJson[string] 拼多多订单同步json字符串
|
||||||
|
* return 类目预测响应结构体,错误信息
|
||||||
|
*/
|
||||||
|
func pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson string) (string, error) {
|
||||||
|
|
||||||
|
var logisticsOnlineSendData map[string]interface{}
|
||||||
|
err := json.Unmarshal([]byte(logisticsOnlineSendJson), &logisticsOnlineSendData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("logisticsOnlineSendJson JSON解析失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 获取物流公司列表
|
// 1. 获取物流公司列表
|
||||||
logisticsCompaniesGet, err := pddLogisticsCompaniesGet(clientId, clientSecret)
|
logisticsCompaniesGet, err := pddLogisticsCompaniesGet(clientId, clientSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -300,20 +415,31 @@ func pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompa
|
|||||||
// 3. 根据物流公司名称查找对应的物流公司ID
|
// 3. 根据物流公司名称查找对应的物流公司ID
|
||||||
for _, lc := range response.LogisticsCompaniesGetResponse.LogisticsCompanies {
|
for _, lc := range response.LogisticsCompaniesGetResponse.LogisticsCompanies {
|
||||||
if lc.LogisticsCompany == logisticsCompany {
|
if lc.LogisticsCompany == logisticsCompany {
|
||||||
logisticsId = fmt.Sprintf("%d", lc.ID)
|
logisticsOnlineSendData["logistics_id"] = fmt.Sprintf("%d", lc.ID)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 4. 执行ERP订单同步
|
fmt.Println(logisticsOnlineSendData["logistics_id"])
|
||||||
erpOrderSync, err := pddErpOrderSync(clientId, clientSecret, accessToken,
|
|
||||||
logisticsId, orderSn, orderState, waybillNo)
|
logisticsOnlineSendStr, err := json.Marshal(logisticsOnlineSendData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("logisticsOnlineSendData 序列化失败: %v", err)
|
||||||
|
}
|
||||||
|
logisticsOnlineSend, err := pddLogisticsOnlineSend(clientId, clientSecret, accessToken, string(logisticsOnlineSendStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return erpOrderSync, nil
|
return logisticsOnlineSend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品图片上传接口
|
/*
|
||||||
|
* 商品图片上传接口
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param filePath[string] 商品图片文件流
|
||||||
|
* return 商品图片上传响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddGoodsImgUpload(clientId, clientSecret, accessToken string, filePath string) (string, error) {
|
func pddGoodsImgUpload(clientId, clientSecret, accessToken string, filePath string) (string, error) {
|
||||||
url := fmt.Sprint("https://gw-upload.pinduoduo.com/api/upload") // 上传专用地址
|
url := fmt.Sprint("https://gw-upload.pinduoduo.com/api/upload") // 上传专用地址
|
||||||
|
|
||||||
@ -562,7 +688,14 @@ type SkuProperty struct {
|
|||||||
Vid int64 `json:"vid"` // 属性值id
|
Vid int64 `json:"vid"` // 属性值id
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品新增接口
|
/*
|
||||||
|
* 商品新增接口
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param goodsAddJson[string] 新增商品信息json字符串
|
||||||
|
* return 商品新增接口响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddGoodsAdd(clientId, clientSecret, accessToken string, goodsAddJson string) (string, error) {
|
func pddGoodsAdd(clientId, clientSecret, accessToken string, goodsAddJson string) (string, error) {
|
||||||
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
|
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
|
||||||
|
|
||||||
@ -633,7 +766,14 @@ func pddGoodsAdd(clientId, clientSecret, accessToken string, goodsAddJson string
|
|||||||
return string(responseJSON), nil
|
return string(responseJSON), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 联合拼多多图片上传的商品新增(组合接口)
|
/*
|
||||||
|
* 联合拼多多图片上传的商品新增(组合接口)
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param goodsAddJson[string] 新增商品信息json字符串
|
||||||
|
* return 商品新增接口响应结构体,错误信息
|
||||||
|
*/
|
||||||
func selfPddGoodsAdd(clientId, clientSecret, accessToken string, filePath string, goodsAddJson string) (string, error) {
|
func selfPddGoodsAdd(clientId, clientSecret, accessToken string, filePath string, goodsAddJson string) (string, error) {
|
||||||
// 1. 上传商品图片
|
// 1. 上传商品图片
|
||||||
upload, err := pddGoodsImgUpload(clientId, clientSecret, accessToken, filePath)
|
upload, err := pddGoodsImgUpload(clientId, clientSecret, accessToken, filePath)
|
||||||
@ -660,7 +800,14 @@ func selfPddGoodsAdd(clientId, clientSecret, accessToken string, filePath string
|
|||||||
return add, nil
|
return add, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量数据解密脱敏接口
|
/*
|
||||||
|
* 批量数据解密脱敏接口
|
||||||
|
* param clientId[string] 客户端ID
|
||||||
|
* param clientSecret[string] 客户端密钥
|
||||||
|
* param accessToken[string] 访问Token
|
||||||
|
* param reqJson[string] 批量数据解密脱敏接口json字符串
|
||||||
|
* return 批量数据解密脱敏接口响应结构体,错误信息
|
||||||
|
*/
|
||||||
func pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken string, reqJson string) (string, error) {
|
func pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken string, reqJson string) (string, error) {
|
||||||
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
|
url := fmt.Sprint("http://gw-api.pinduoduo.com/api/router")
|
||||||
// 当前时间戳
|
// 当前时间戳
|
||||||
@ -796,17 +943,13 @@ func PddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
|||||||
// PddOrderSynchronization 拼多多订单同步
|
// PddOrderSynchronization 拼多多订单同步
|
||||||
//
|
//
|
||||||
//export PddOrderSynchronization
|
//export PddOrderSynchronization
|
||||||
func PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsId,
|
func PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson *C.char) *C.char {
|
||||||
orderSn, orderState, waybillNo *C.char) *C.char {
|
|
||||||
goClientId := C.GoString(clientId)
|
goClientId := C.GoString(clientId)
|
||||||
goClientSecret := C.GoString(clientSecret)
|
goClientSecret := C.GoString(clientSecret)
|
||||||
goAccessToken := C.GoString(accessToken)
|
goAccessToken := C.GoString(accessToken)
|
||||||
goLogisticsCompany := C.GoString(logisticsCompany)
|
goLogisticsCompany := C.GoString(logisticsCompany)
|
||||||
goLogisticsId := C.GoString(logisticsId)
|
goLogisticsOnlineSendJson := C.GoString(logisticsOnlineSendJson)
|
||||||
goOrderSn := C.GoString(orderSn)
|
info, err := pddOrderSynchronization(goClientId, goClientSecret, goAccessToken, goLogisticsCompany, goLogisticsOnlineSendJson)
|
||||||
goOrderState := C.GoString(orderState)
|
|
||||||
goWaybillNo := C.GoString(waybillNo)
|
|
||||||
info, err := pddOrderSynchronization(goClientId, goClientSecret, goAccessToken, goLogisticsCompany, goLogisticsId, goOrderSn, goOrderState, goWaybillNo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return C.CString(err.Error())
|
return C.CString(err.Error())
|
||||||
}
|
}
|
||||||
@ -882,5 +1025,5 @@ func FreeCString(str *C.char) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// main函数
|
// main函数
|
||||||
func main() {
|
//func main() {
|
||||||
}
|
//}
|
||||||
|
|||||||
@ -135,9 +135,9 @@ func (m *pddDLL) PddErpOrderSync(clientId, clientSecret, accessToken, logisticsI
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
clientId := "203c5a7ba8bd4b8488d5e26f93052642"
|
//clientId := "203c5a7ba8bd4b8488d5e26f93052642"
|
||||||
clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
|
//clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
|
||||||
accessToken := "bd96218bb2a146779701506dc1e5e5c478692539"
|
//accessToken := "bd96218bb2a146779701506dc1e5e5c478692539"
|
||||||
//outerCatId := "15543"
|
//outerCatId := "15543"
|
||||||
//outerCatName := "书籍/杂志/报纸"
|
//outerCatName := "书籍/杂志/报纸"
|
||||||
//outerGoodsName := "书籍医家金鉴 妇产科学卷"
|
//outerGoodsName := "书籍医家金鉴 妇产科学卷"
|
||||||
@ -210,7 +210,7 @@ func main() {
|
|||||||
|
|
||||||
// 脱敏
|
// 脱敏
|
||||||
|
|
||||||
jsonStr := `[{"data_tag":"251229-272441044622514","encrypted_data":"~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"}]`
|
//jsonStr := `[{"data_tag":"251229-272441044622514","encrypted_data":"~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"}]`
|
||||||
|
|
||||||
//var records []DataList
|
//var records []DataList
|
||||||
//err := json.Unmarshal([]byte(jsonStr), &records)
|
//err := json.Unmarshal([]byte(jsonStr), &records)
|
||||||
@ -218,12 +218,25 @@ func main() {
|
|||||||
// log.Fatal("解析JSON失败:", err)
|
// log.Fatal("解析JSON失败:", err)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
batch, err := pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, jsonStr)
|
//batch, err := pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, jsonStr)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
fmt.Println(err)
|
// fmt.Println(err)
|
||||||
}
|
//}
|
||||||
fmt.Println(batch)
|
//fmt.Println(batch)
|
||||||
|
//clientId := "203c5a7ba8bd4b8488d5e26f93052642"
|
||||||
|
//clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
|
||||||
|
//accessToken := "5b1e9506827049a7a9335302e917d2b896a3d6c7"
|
||||||
|
//logisticsCompany := "韵达快递"
|
||||||
|
//logisticsId := ""
|
||||||
|
//orderSn := "260107-652497405582514"
|
||||||
|
//orderState := "1"
|
||||||
|
//waybillNo := "312944253800986"
|
||||||
|
//
|
||||||
|
//synchronization, err := pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsId, orderSn, orderState, waybillNo)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
//}
|
||||||
|
//fmt.Println(string(synchronization))
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataList struct {
|
type DataList struct {
|
||||||
|
|||||||
@ -50,7 +50,7 @@ type ProxyManager struct {
|
|||||||
proxyType string `json:"proxy_type"` // 代理类型 CALF_ELEPHANT_PROXY/TAIL_PROXY
|
proxyType string `json:"proxy_type"` // 代理类型 CALF_ELEPHANT_PROXY/TAIL_PROXY
|
||||||
}
|
}
|
||||||
|
|
||||||
// 代理健康状态结构体,用于跟踪代理的健康状况
|
// ProxyHealth 代理健康状态结构体,用于跟踪代理的健康状况
|
||||||
type ProxyHealth struct {
|
type ProxyHealth struct {
|
||||||
SuccessCount int // 成功次数
|
SuccessCount int // 成功次数
|
||||||
FailCount int // 失败次数
|
FailCount int // 失败次数
|
||||||
@ -65,7 +65,14 @@ func init() {
|
|||||||
globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取代理URL,代理类型管理器,根据代理类型构建不同的代理URL
|
/*
|
||||||
|
* 获取代理URL,代理类型管理器,根据代理类型构建不同的代理URL
|
||||||
|
* param proxyType[string] 代理类型
|
||||||
|
* param username[string] 代理用户名
|
||||||
|
* param password[string] 代理密码
|
||||||
|
* param machineCode[string] 机器码
|
||||||
|
* return 代理服务器IP,错误信息
|
||||||
|
*/
|
||||||
func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
|
func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
|
||||||
switch proxyType {
|
switch proxyType {
|
||||||
case CalfElephantProxyType:
|
case CalfElephantProxyType:
|
||||||
@ -80,7 +87,12 @@ func proxyTypeManager(proxyType, username, password, machineCode string) (string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建小象代理URL
|
/*
|
||||||
|
* 构建小象代理URL
|
||||||
|
* param username[string] 代理用户名
|
||||||
|
* param password[string] 代理密码
|
||||||
|
* return 代理服务器IP,错误信息
|
||||||
|
*/
|
||||||
func buildCalfElephantProxyURL(username, password string) (string, error) {
|
func buildCalfElephantProxyURL(username, password string) (string, error) {
|
||||||
// 随机选择一个代理服务器
|
// 随机选择一个代理服务器
|
||||||
server := randomServer()
|
server := randomServer()
|
||||||
@ -103,7 +115,13 @@ func buildCalfElephantProxyURL(username, password string) (string, error) {
|
|||||||
return proxyURL, nil
|
return proxyURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试下一个小象代理服务器
|
/*
|
||||||
|
* 尝试下一个小象代理服务器
|
||||||
|
* param username[string] 代理用户名
|
||||||
|
* param password[string] 代理密码
|
||||||
|
* param failedServer[string] 代理服务器
|
||||||
|
* return 代理服务器IP,错误信息
|
||||||
|
*/
|
||||||
func tryNextCalfElephantProxy(username, password, failedServer string) (string, error) {
|
func tryNextCalfElephantProxy(username, password, failedServer string) (string, error) {
|
||||||
// 创建服务器副本并排除失败的服务器
|
// 创建服务器副本并排除失败的服务器
|
||||||
availableServers := make([]string, 0)
|
availableServers := make([]string, 0)
|
||||||
@ -137,7 +155,11 @@ func tryNextCalfElephantProxy(username, password, failedServer string) (string,
|
|||||||
return "", fmt.Errorf("所有可用的小象代理服务器都检测失败")
|
return "", fmt.Errorf("所有可用的小象代理服务器都检测失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建内置代理URL
|
/*
|
||||||
|
* 构建内置代理URL
|
||||||
|
* param machineCode[string] 机器码
|
||||||
|
* return 代理服务器IP,错误信息
|
||||||
|
*/
|
||||||
func buildTailProxyURL(machineCode string) (string, error) {
|
func buildTailProxyURL(machineCode string) (string, error) {
|
||||||
// 获取代理列表
|
// 获取代理列表
|
||||||
proxies, err := getProxies(machineCode)
|
proxies, err := getProxies(machineCode)
|
||||||
@ -163,7 +185,11 @@ func buildTailProxyURL(machineCode string) (string, error) {
|
|||||||
return findWorkingTailProxy(proxies)
|
return findWorkingTailProxy(proxies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤健康代理,返回当前健康的代理列表
|
/*
|
||||||
|
* 过滤健康代理,返回当前健康的代理列表
|
||||||
|
* param proxies[[]string] 内置代理服务器数组
|
||||||
|
* return 内置代理服务器数组,错误信息
|
||||||
|
*/
|
||||||
func filterHealthyProxies(proxies []string) []string {
|
func filterHealthyProxies(proxies []string) []string {
|
||||||
// 获取读锁,允许多个goroutine同时读取
|
// 获取读锁,允许多个goroutine同时读取
|
||||||
proxyHealthMutex.RLock()
|
proxyHealthMutex.RLock()
|
||||||
@ -181,7 +207,11 @@ func filterHealthyProxies(proxies []string) []string {
|
|||||||
return healthy
|
return healthy
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找可用的尾巴代理
|
/*
|
||||||
|
* 查找可用的尾巴代理
|
||||||
|
* param proxies[[]string] 内置代理服务器数组
|
||||||
|
* return 可用的代理服务器IP,错误信息
|
||||||
|
*/
|
||||||
func findWorkingTailProxy(proxies []string) (string, error) {
|
func findWorkingTailProxy(proxies []string) (string, error) {
|
||||||
// 打乱代理顺序
|
// 打乱代理顺序
|
||||||
shuffledProxies := shuffleServers(proxies)
|
shuffledProxies := shuffleServers(proxies)
|
||||||
@ -238,7 +268,11 @@ func findWorkingTailProxy(proxies []string) (string, error) {
|
|||||||
return "", fmt.Errorf("所有尾巴代理都不可用")
|
return "", fmt.Errorf("所有尾巴代理都不可用")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测代理健康状态,通过访问测试网站验证代理可用性
|
/*
|
||||||
|
* 检测代理健康状态,通过访问测试网站验证代理可用性
|
||||||
|
* param proxyURL[string] 代理服务器
|
||||||
|
* return 错误信息
|
||||||
|
*/
|
||||||
func checkProxyHealth(proxyURL string) error {
|
func checkProxyHealth(proxyURL string) error {
|
||||||
start := time.Now() // 记录开始时间,用于计算响应时间
|
start := time.Now() // 记录开始时间,用于计算响应时间
|
||||||
// 创建HTTP请求,设置代理和超时
|
// 创建HTTP请求,设置代理和超时
|
||||||
@ -263,7 +297,12 @@ func checkProxyHealth(proxyURL string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新代理健康状态
|
/*
|
||||||
|
* 更新代理健康状态
|
||||||
|
* param proxyURL[string] 代理服务器
|
||||||
|
* param success[bool] 是否成功
|
||||||
|
* param responseTime[time.Duration] 响应时间
|
||||||
|
*/
|
||||||
func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration) {
|
func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration) {
|
||||||
// 获取写锁,确保更新操作的互斥性
|
// 获取写锁,确保更新操作的互斥性
|
||||||
proxyHealthMutex.Lock()
|
proxyHealthMutex.Lock()
|
||||||
@ -295,7 +334,11 @@ func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取代理主机名
|
/*
|
||||||
|
* 获取代理主机名
|
||||||
|
* param proxyURL[string] 代理服务器
|
||||||
|
* return 代理服务器IP
|
||||||
|
*/
|
||||||
func getProxyHost(proxyURL string) string {
|
func getProxyHost(proxyURL string) string {
|
||||||
// 去除协议前缀
|
// 去除协议前缀
|
||||||
if strings.HasPrefix(proxyURL, "http://") {
|
if strings.HasPrefix(proxyURL, "http://") {
|
||||||
@ -338,7 +381,11 @@ func shuffleServers(servers []string) []string {
|
|||||||
return shuffled
|
return shuffled
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查卡密是否过期
|
/*
|
||||||
|
* 检查卡密是否过期
|
||||||
|
* param tailCardSecret[string] 卡密
|
||||||
|
* return 是否过期,错误信息
|
||||||
|
*/
|
||||||
func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
|
func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
|
||||||
// 获取机器码信息
|
// 获取机器码信息
|
||||||
code, err := getMachineCode(tailCardSecret)
|
code, err := getMachineCode(tailCardSecret)
|
||||||
@ -372,7 +419,11 @@ type getMachineCodeResp struct {
|
|||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询机器码
|
/*
|
||||||
|
* 查询机器码
|
||||||
|
* param tailCardSecret[string] 卡密
|
||||||
|
* return 响应结构体,错误信息
|
||||||
|
*/
|
||||||
func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
|
func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
|
||||||
url := "http://114.66.2.223:7842/api/proxies/ip_show"
|
url := "http://114.66.2.223:7842/api/proxies/ip_show"
|
||||||
// 构建请求数据
|
// 构建请求数据
|
||||||
@ -408,7 +459,12 @@ func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 充值卡密
|
/*
|
||||||
|
* 充值卡密
|
||||||
|
* param tailCardSecret[string] 卡密
|
||||||
|
* param machineCode[string] 机器码
|
||||||
|
* return 机器码,错误信息
|
||||||
|
*/
|
||||||
func rechargeCard(tailCardSecret, machineCode string) (string, error) {
|
func rechargeCard(tailCardSecret, machineCode string) (string, error) {
|
||||||
url := "http://114.66.2.223:7842/api/proxies/ip_recharge"
|
url := "http://114.66.2.223:7842/api/proxies/ip_recharge"
|
||||||
// 构建请求数据
|
// 构建请求数据
|
||||||
@ -443,7 +499,11 @@ func rechargeCard(tailCardSecret, machineCode string) (string, error) {
|
|||||||
return resp.Data.MachineCode, nil
|
return resp.Data.MachineCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取代理服务器列表
|
/*
|
||||||
|
* 获取代理服务器列表
|
||||||
|
* param machineCode[string] 机器码
|
||||||
|
* return 代理服务器组,错误信息
|
||||||
|
*/
|
||||||
func getProxies(machineCode string) ([]string, error) {
|
func getProxies(machineCode string) ([]string, error) {
|
||||||
log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
|
log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
|
||||||
// 生成签名
|
// 生成签名
|
||||||
|
|||||||
146
so/kongfz.h
146
so/kongfz.h
@ -1,146 +0,0 @@
|
|||||||
/* 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* OutGetUserMsg(char* token);
|
|
||||||
|
|
||||||
// 获取商品模版(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* itemId, char* proxy);
|
|
||||||
|
|
||||||
// 获取商品列表-已登的店铺(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsListMsgFromSelfShop(char* token, char* proxy, char* itemSn, char* priceMin, char* priceMax, int startCreateTime, int endCreateTime, char* requestType, int isItemSnEqual, int page, int size);
|
|
||||||
|
|
||||||
// 删除商品-已登的店铺(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
|
|
||||||
|
|
||||||
// 新增商品(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData);
|
|
||||||
|
|
||||||
// 获取图片URL(官图和拍图)带有店铺过滤
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* isbn, int shopId, char* proxy, int isLiveImage, int isReturnMsg);
|
|
||||||
|
|
||||||
// 获取商品图片(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* isbn, char* proxy, int isLiveImage, int isReturnMsg);
|
|
||||||
|
|
||||||
// 获取商品列表通过店铺ID(带有Out的都非官方标准接口)
|
|
||||||
//
|
|
||||||
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int retPrice, 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
|
|
||||||
BIN
so/kongfz.so
BIN
so/kongfz.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
1237
zjdydll.go
1237
zjdydll.go
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user