更新修改功能

This commit is contained in:
Cai1Cai1 2026-02-27 11:46:40 +08:00
parent 909313e2b5
commit fa1b388c8d
60 changed files with 14105 additions and 1566 deletions

271
config/config.go Normal file
View File

@ -0,0 +1,271 @@
package main
/*
#include <stdlib.h>
*/
import "C"
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"unsafe"
"gopkg.in/ini.v1"
"gopkg.in/yaml.v3"
)
// ConfigReader 配置文件读取器接口
type ConfigReader interface {
ReadConfig(data []byte) (string, error)
}
// JSONConfigReader JSON配置文件读取器
type JSONConfigReader struct{}
func (r *JSONConfigReader) ReadConfig(data []byte) (string, error) {
var result map[string]interface{}
if err := json.Unmarshal(data, &result); err != nil {
if strings.Contains(err.Error(), "cannot unmarshal array into Go value of type map[string]interface {}") {
// 尝试解析为数组
var arrayResult []map[string]interface{}
if arrayErr := json.Unmarshal(data, &arrayResult); arrayErr != nil {
return "", fmt.Errorf("JSON解析错误: %v", err)
}
arrayResultStr, err := json.Marshal(arrayResult)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(arrayResultStr), nil
}
return "", fmt.Errorf("JSON解析错误: %v", err)
}
resultStr, err := json.Marshal(result)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(resultStr), nil
}
// YAMLConfigReader YAML配置文件读取器
type YAMLConfigReader struct{}
func (r *YAMLConfigReader) ReadConfig(data []byte) (string, error) {
var result map[string]interface{}
if err := yaml.Unmarshal(data, &result); err != nil {
return "", fmt.Errorf("YAML解析错误: %v", err)
}
resultStr, err := json.Marshal(result)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(resultStr), nil
}
// INIConfigReader INI配置文件读取器
type INIConfigReader struct{}
func (r *INIConfigReader) ReadConfig(data []byte) (string, error) {
cfg, err := ini.Load(data)
if err != nil {
return "", fmt.Errorf("INI解析错误: %v", err)
}
result := make(map[string]interface{})
// 将INI结构转换为map
for _, section := range cfg.Sections() {
sectionName := section.Name()
if sectionName == "DEFAULT" {
sectionName = "default"
}
sectionMap := make(map[string]interface{})
for _, key := range section.Keys() {
sectionMap[key.Name()] = key.Value()
}
// 如果是默认节,直接合并到顶层
if sectionName == "default" {
for k, v := range sectionMap {
result[k] = v
}
} else {
result[sectionName] = sectionMap
}
}
resultStr, err := json.Marshal(result)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
return string(resultStr), nil
}
// ConfigProcessor 配置处理器
type ConfigProcessor struct {
readers map[string]ConfigReader
}
// NewConfigProcessor 创建配置处理器
func NewConfigProcessor() *ConfigProcessor {
return &ConfigProcessor{
readers: map[string]ConfigReader{
"json": &JSONConfigReader{},
"yaml": &YAMLConfigReader{},
"yml": &YAMLConfigReader{},
"ini": &INIConfigReader{},
"conf": &INIConfigReader{},
},
}
}
// GetFileExtension 获取文件扩展名
func (p *ConfigProcessor) GetFileExtension(fileName string) string {
ext := strings.ToLower(filepath.Ext(fileName))
if ext != "" {
return ext[1:] // 去掉点号
}
return ""
}
// GetReader 根据文件扩展名获取对应的读取器
func (p *ConfigProcessor) GetReader(fileName string) (ConfigReader, error) {
ext := p.GetFileExtension(fileName)
if ext == "" {
return nil, fmt.Errorf("无法识别文件类型: %s", fileName)
}
reader, exists := p.readers[ext]
if !exists {
return nil, fmt.Errorf("不支持的文件类型: %s", ext)
}
return reader, nil
}
// ReadConfigFile 读取配置文件
// 参数: filePath文件路径 fileName文件名
func (p *ConfigProcessor) readConfigFile(filePath, fileName string) (string, error) {
// 构建完整文件路径
fullPath := p.buildFullPath(filePath, fileName)
// 读取文件内容
data, err := p.readFileContent(fullPath)
if err != nil {
return "", err
}
reader, err := p.GetReader(fileName)
if err != nil {
return "", err
}
return reader.ReadConfig(data)
}
// buildFullPath 构建完整文件路径
func (p *ConfigProcessor) buildFullPath(filePath, fileName string) string {
// 如果文件路径为空,使用当前目录
if filePath == "" {
return fileName
}
// 检查是否为HTTP/HTTPS URL
if strings.HasPrefix(filePath, "http://") || strings.HasPrefix(filePath, "https://") {
// 确保URL以斜杠结尾
if !strings.HasSuffix(filePath, "/") {
filePath += "/"
}
return filePath + fileName
}
// 本地文件路径
return filepath.Join(filePath, fileName)
}
// readFileContent 读取文件内容
func (p *ConfigProcessor) readFileContent(fullPath string) ([]byte, error) {
// 检查是否为HTTP/HTTPS URL
if strings.HasPrefix(fullPath, "http://") || strings.HasPrefix(fullPath, "https://") {
return p.readFromHTTP(fullPath)
}
// 本地文件
return p.readFromLocal(fullPath)
}
// readFromLocal 从本地文件读取内容
func (p *ConfigProcessor) readFromLocal(filePath string) ([]byte, error) {
// 检查文件是否存在
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, fmt.Errorf("文件不存在: %s", filePath)
}
return os.ReadFile(filePath)
}
// readFromHTTP 从HTTP URL读取内容
func (p *ConfigProcessor) readFromHTTP(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("HTTP请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP请求失败状态码: %d", resp.StatusCode)
}
return io.ReadAll(resp.Body)
}
// PrettyPrint 美化打印配置内容
func PrettyPrint(config map[string]interface{}, indent string) {
for key, value := range config {
switch v := value.(type) {
case map[string]interface{}:
fmt.Printf("%s%s:\n", indent, key)
PrettyPrint(v, indent+" ")
default:
fmt.Printf("%s%s: %v\n", indent, key, v)
}
}
}
// ReadConfigFile 读取配置文件
//
//export ReadConfigFile
func ReadConfigFile(filePath, fileName *C.char) *C.char {
filePathStr := C.GoString(filePath)
fileNameStr := C.GoString(fileName)
info, err := NewConfigProcessor().readConfigFile(filePathStr, fileNameStr)
if err != nil {
return C.CString(err.Error())
}
return C.CString(info)
}
// FreeCString 释放C字符串内存
//
//export FreeCString
func FreeCString(str *C.char) {
C.free(unsafe.Pointer(str))
}
// CSV_VERSION 版本号
const (
CSV_VERSION = "v1"
)
// 获取版本信息
//
//export GetVersion
func GetVersion() *C.char {
return C.CString(CSV_VERSION)
}
// 主函数
func main() {
}

24
config/config.json Normal file
View File

@ -0,0 +1,24 @@
{
"app": {
"name": "My Application",
"version": "1.0.0",
"debug": false
},
"server": {
"host": "localhost",
"port": 8080,
"timeout": 30
},
"database": {
"host": "localhost",
"port": 5432,
"name": "mydb",
"user": "admin",
"password": "secret"
},
"features": {
"enable_logging": true,
"max_file_size": 1048576,
"allowed_extensions": ["jpg", "png", "pdf"]
}
}

27
config/configDll.go Normal file
View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
"os"
)
func main() {
// 解析命令行参数
filePath := "https://newverifyprice.buzhiyushu.cn"
fileName := "verify_price_config.yaml"
// 创建配置处理器
processor := NewConfigProcessor()
// 读取配置文件
config, err := processor.readConfigFile(filePath, fileName)
if err != nil {
fmt.Printf("读取配置文件失败: %v\n", err)
os.Exit(1)
}
fmt.Println(config)
// 输出配置内容
fmt.Printf("成功读取配置文件: %s\n", fileName)
}

BIN
config/dll/config.dll Normal file

Binary file not shown.

105
config/dll/config.h Normal file
View File

@ -0,0 +1,105 @@
/* 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 "config.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
// ReadConfigFile 读取配置文件
//
extern __declspec(dllexport) char* ReadConfigFile(char* filePath, char* fileName);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus
}
#endif

BIN
csv/csv.dll Normal file

Binary file not shown.

View File

@ -493,7 +493,7 @@ func (mgr *CSVManager) createEmptyCSVHandle(filename string, delimiter rune, has
} }
// 以读写模式打开文件 // 以读写模式打开文件
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644) file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0755)
if err != nil { if err != nil {
return -1, fmt.Errorf("打开文件失败: %w", err) return -1, fmt.Errorf("打开文件失败: %w", err)
} }
@ -766,7 +766,7 @@ func (mgr *CSVManager) openFile(handle *CSVHandle) error {
// 正常打开文件 // 正常打开文件
func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error { func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error {
// 以读写模式打开文件 // 以读写模式打开文件
file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0644) file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0755)
if err != nil { if err != nil {
return err return err
} }
@ -794,7 +794,7 @@ func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error {
// 使用内存映射打开大文件 // 使用内存映射打开大文件
func (mgr *CSVManager) openFileWithMMap(handle *CSVHandle, fileSize int64) error { func (mgr *CSVManager) openFileWithMMap(handle *CSVHandle, fileSize int64) error {
// 对于大文件,使用只读模式打开,写入需要特殊处理 // 对于大文件,使用只读模式打开,写入需要特殊处理
file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0644) file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0755)
if err != nil { if err != nil {
return err return err
} }
@ -1023,6 +1023,83 @@ func (mgr *CSVManager) WriteRows(handleID int64, rows [][]string) (int64, error)
return handle.TotalRows, nil return handle.TotalRows, nil
} }
// WriteRowsNum 批量写入多行数据到CSV文件
// 返回每行数据存储的行号数组从1开始
func (mgr *CSVManager) WriteRowsNum(handleID int64, rows [][]string) ([]int64, error) {
// 获取句柄
handle, err := mgr.getHandle(handleID)
if err != nil {
return nil, fmt.Errorf("WriteRows 获取句柄失败: %w", err)
}
defer mgr.releaseHandle(handleID)
// 检查句柄状态
if !handle.beginOperation() {
return nil, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
}
defer handle.endOperation()
handle.mu.Lock()
defer handle.mu.Unlock()
if !handle.IsOpen {
return nil, fmt.Errorf("文件未打开")
}
// 检查是否有表头
if handle.HasHeader && len(handle.Header) > 0 {
// 验证每行的列数
for i, row := range rows {
if len(row) != len(handle.Header) {
return nil, fmt.Errorf("第%d行列数(%d)与表头列数(%d)不匹配", i+1, len(row), len(handle.Header))
}
}
}
// 移动到文件末尾
if _, err := handle.File.Seek(0, 2); err != nil {
return nil, fmt.Errorf("移动到文件末尾失败: %w", err)
}
// 计算起始行号 - 关键修正部分
var startRow int64
// 先获取当前文件的实际行数(包括表头)
currentLineCount := handle.TotalRows
if handle.HasHeader && currentLineCount == 0 {
// 如果有表头但还没有数据行表头算第1行数据从第2行开始
startRow = 2
} else if handle.HasHeader {
// 有表头且已有数据行表头是第1行TotalRows不包括表头
// 所以下一行应该是 TotalRows + 2
startRow = currentLineCount + 2
} else {
// 没有表头,直接累加
startRow = currentLineCount + 1
}
// 创建行号数组
rowNumbers := make([]int64, len(rows))
for i := 0; i < len(rows); i++ {
rowNumbers[i] = startRow + int64(i)
}
// 批量写入行数据
for _, row := range rows {
if err := handle.CSVWriter.Write(row); err != nil {
return nil, fmt.Errorf("写入行失败: %w", err)
}
}
// 更新行数统计
handle.TotalRows += int64(len(rows))
handle.cachedRowCount = handle.TotalRows
handle.rowCountCached = true
return rowNumbers, nil
}
// logWriteRows 记录行数据到日志文件 // logWriteRows 记录行数据到日志文件
func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error { func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error {
// 获取句柄信息(但不锁定,因为我们只是读取元数据) // 获取句柄信息(但不锁定,因为我们只是读取元数据)
@ -1077,6 +1154,61 @@ func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error {
return nil return nil
} }
// logWriteRows 记录行数据到日志文件
func (mgr *CSVManager) logWriteRowsNum(handleID int64, rows [][]string, rowsNum []int64) error {
// 获取句柄信息(但不锁定,因为我们只是读取元数据)
handle, err := mgr.getHandle(handleID)
if err != nil {
return fmt.Errorf("获取句柄信息失败: %w", err)
}
defer mgr.releaseHandle(handleID)
// 创建日志目录
logDir := filepath.Join(filepath.Dir("csv"), "logs")
if err := os.MkdirAll(logDir, 0755); err != nil {
return fmt.Errorf("创建日志目录失败: %w", err)
}
// 生成日志文件名基于CSV文件名和时间
baseName := filepath.Base("csv")
logFileName := fmt.Sprintf("%s_%s_write.log",
baseName[:len(baseName)-len(filepath.Ext(baseName))],
time.Now().Format("20060102"))
logFilePath := filepath.Join(logDir, logFileName)
// 打开或创建日志文件
logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("打开日志文件失败: %w", err)
}
defer logFile.Close()
// 创建日志条目
logEntry := map[string]interface{}{
"timestamp": time.Now().Format("2006-01-02 15:04:05.000"),
"handle_id": handleID,
"filename": "csv",
"operation": "write_rows",
"row_count": len(rows),
"total_rows": handle.TotalRows + int64(len(rows)), // 预计的总行数
"data": rows,
"rowsNum": rowsNum,
}
// 序列化为JSON
logData, err := json.Marshal(logEntry)
if err != nil {
return fmt.Errorf("序列化日志数据失败: %w", err)
}
// 写入日志每行一个JSON对象
if _, err := logFile.Write(append(logData, '\n')); err != nil {
return fmt.Errorf("写入日志文件失败: %w", err)
}
return nil
}
// Flush 将缓冲区数据写入文件 // Flush 将缓冲区数据写入文件
func (mgr *CSVManager) Flush(handleID int64) error { func (mgr *CSVManager) Flush(handleID int64) error {
// 获取句柄 // 获取句柄
@ -1135,6 +1267,24 @@ func (mgr *CSVManager) AppendRows(handleID int64, rows [][]string) (int64, error
return totalRows, nil return totalRows, nil
} }
// AppendRows 批量追加行数据自动Flush
func (mgr *CSVManager) AppendRowsNum(handleID int64, rows [][]string) ([]int64, error) {
rowsNum, err := mgr.WriteRowsNum(handleID, rows)
if err != nil {
return nil, err
}
err = mgr.Flush(handleID)
if err != nil {
return nil, err
}
// 首先记录日志
if err := mgr.logWriteRowsNum(handleID, rows, rowsNum); err != nil {
// 日志记录失败不影响主流程,但可以打印警告
fmt.Printf("警告:记录日志失败: %v\n", err)
}
return rowsNum, nil
}
// GetHeader 获取CSV文件表头 // GetHeader 获取CSV文件表头
func (mgr *CSVManager) GetHeader(handleID int64) ([]string, error) { func (mgr *CSVManager) GetHeader(handleID int64) ([]string, error) {
// 获取句柄 // 获取句柄
@ -1469,23 +1619,36 @@ func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error)
return nil, fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, totalRows) return nil, fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, totalRows)
} }
// 保存当前文件位置 //// 保存当前文件位置
currentPos, err := handle.File.Seek(0, 1) // 当前位置 //currentPos, err := handle.File.Seek(0, 1) // 当前位置
//if err != nil {
// return nil, fmt.Errorf("获取当前文件位置失败: %w", err)
//}
//// 重置到文件开始
//if _, err := handle.File.Seek(0, 0); err != nil {
// return nil, fmt.Errorf("重置文件指针失败: %w", err)
//}
// 创建文件副本用于读取避免影响其他goroutine
file, err := os.Open(handle.Filename)
if err != nil { if err != nil {
return nil, fmt.Errorf("获取当前文件位置失败: %w", err) return nil, fmt.Errorf("打开文件失败: %w", err)
} }
defer file.Close()
// 重置到文件开始 // 创建CSV阅读器
if _, err := handle.File.Seek(0, 0); err != nil { reader := csv.NewReader(file)
return nil, fmt.Errorf("重置文件指针失败: %w", err)
}
// 重置CSV阅读器
reader := csv.NewReader(bufio.NewReader(handle.File))
reader.Comma = handle.Delimiter reader.Comma = handle.Delimiter
reader.LazyQuotes = true reader.LazyQuotes = true
reader.TrimLeadingSpace = true reader.TrimLeadingSpace = true
//// 重置CSV阅读器
//reader := csv.NewReader(bufio.NewReader(handle.File))
//reader.Comma = handle.Delimiter
//reader.LazyQuotes = true
//reader.TrimLeadingSpace = true
// 定位到指定行 // 定位到指定行
var currentRow int64 = 0 var currentRow int64 = 0
var targetRow []string var targetRow []string
@ -1496,7 +1659,7 @@ func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error)
break break
} }
// 恢复文件位置 // 恢复文件位置
handle.File.Seek(currentPos, 0) //handle.File.Seek(currentPos, 0)
return nil, fmt.Errorf("读取行失败: %w", err) return nil, fmt.Errorf("读取行失败: %w", err)
} }
@ -1517,10 +1680,10 @@ func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error)
currentRow++ currentRow++
} }
// 恢复文件位置 //// 恢复文件位置
if _, err := handle.File.Seek(currentPos, 0); err != nil { //if _, err := handle.File.Seek(currentPos, 0); err != nil {
return nil, fmt.Errorf("恢复文件位置失败: %w", err) // return nil, fmt.Errorf("恢复文件位置失败: %w", err)
} //}
if targetRow == nil { if targetRow == nil {
return nil, fmt.Errorf("未找到行号 %d", rowNumber) return nil, fmt.Errorf("未找到行号 %d", rowNumber)
@ -1944,6 +2107,46 @@ func WriteRows(handleID C.int, rowsData *C.char) *C.char {
return C.CString(string(csvResponseStr)) return C.CString(string(csvResponseStr))
} }
// WriteRowsNum 写入/覆盖行数据
//
//export WriteRowsNum
func WriteRowsNum(handleID C.int, rowsData *C.char) *C.char {
var csvResponse CSVResponse
goHandleID := int64(handleID)
goRowsData := C.GoString(rowsData)
// json解析
var data [][]string
err := json.Unmarshal([]byte(goRowsData), &data)
if err != nil {
csvResponse = CSVResponse{
Success: false,
Message: fmt.Sprintf("rowsData JSON解析失败: %v", err),
}
csvResponseStr, _ := json.Marshal(csvResponse)
return C.CString(string(csvResponseStr))
}
// 写入数据
rowsNum, err := GetManager().AppendRowsNum(goHandleID, data)
response := struct {
RowsNum []int64 `json:"rowsNum"`
}{
RowsNum: rowsNum,
}
if err != nil {
csvResponse = CSVResponse{
Success: false,
Message: err.Error(),
}
} else {
csvResponse = CSVResponse{
Success: true,
Data: response,
}
}
csvResponseStr, _ := json.Marshal(csvResponse)
return C.CString(string(csvResponseStr))
}
// UpdateRow C导出函数 - 修改指定行数据 // UpdateRow C导出函数 - 修改指定行数据
// //
//export UpdateRow //export UpdateRow
@ -2085,5 +2288,5 @@ func FreeCString(str *C.char) {
} }
// 主函数 // 主函数
func main() { //func main() {
} //}

133
csv/csv.h Normal file
View File

@ -0,0 +1,133 @@
/* 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
// OpenCSVFile 打开CSV文件
//
extern __declspec(dllexport) char* OpenCSVFile(char* filename, char delimiter, int hasHeader);
// WriteHeader 写入表头
//
extern __declspec(dllexport) char* WriteHeader(int handleID, char* header);
// WriteRows 写入/覆盖行数据
//
extern __declspec(dllexport) char* WriteRows(int handleID, char* rowsData);
// WriteRowsNum 写入/覆盖行数据
//
extern __declspec(dllexport) char* WriteRowsNum(int handleID, char* rowsData);
// UpdateRow C导出函数 - 修改指定行数据
//
extern __declspec(dllexport) char* UpdateRow(int handleID, int rowNumber, char* rowData);
// GetRow C导出函数 - 获取指定行数据
//
extern __declspec(dllexport) char* GetRow(int handleID, int rowNumber);
// MergeCSVFilesSimple 合并两个csv文件
//
extern __declspec(dllexport) char* MergeCSVFilesSimple(int srcHandleID, int dstHandleID, int appendMode);
// CloseHandles 关闭指定句柄(使用优雅关闭)
//
extern __declspec(dllexport) char* CloseHandles(int handleID);
// CloseAllHandles 关闭所有句柄
//
extern __declspec(dllexport) char* CloseAllHandles(void);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

View File

@ -1,10 +1,8 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"github.com/redis/go-redis/v9" "sync"
"log"
) )
// //
@ -584,260 +582,493 @@ import (
//} //}
// 主函数 - 测试代码 // 主函数 - 测试代码
//func main() {
// fmt.Println("=== CSV句柄管理器测试 ===") //7984
//
// filename := "csv/2006557053525397505_2013842964755726337_20260121_1.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. 获取修改后的行数据")
//
// for i := 0; i < 10000; i++ {
// row1, err := GetManager().GetRow(handleID, int64(i))
// if err != nil {
// fmt.Printf("获取第 %d 行失败: %v\n", i, err)
// } else {
// fmt.Printf("第 %d 行数据: %v\n", i, row1)
// }
// }
// //获取第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测试完成")
//}
func main() { func main() {
fmt.Println("=== CSV句柄管理器测试 ===") //7984 filename := "csv/taskLog.csv"
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) handleID, err := GetManager().OpenCSVFile(filename, ',', true)
if err != nil { if err != nil {
fmt.Printf("打开文件失败: %v\n", err) fmt.Println(err.Error())
} }
fmt.Println(handleID)
fmt.Printf("2. 打开文件成功句柄ID: %d\n", handleID) headers := []string{"ID", "序号", "姓名", "年龄"}
err = GetManager().WriteHeader(handleID, headers)
// 测试写入表头
header := []string{"ID", "data"}
err = GetManager().WriteHeader(handleID, header)
if err != nil { if err != nil {
fmt.Printf("写入表头失败: %v\n", err) fmt.Println(err.Error())
} else {
fmt.Println("写入表头成功")
} }
// 获取列表所有元素 //rowss, i, err := GetManager().AppendRowss(handleID, rows)
listValues, err := rdb.LRange(ctx, key, 0, -1).Result() //if err != nil {
if err != nil { // fmt.Println(err.Error())
log.Fatal("获取列表失败:", err) //}
//fmt.Println(rowss)
//fmt.Println(i)
var wg sync.WaitGroup
results := make([][]int64, 100)
for i := 0; i < 100; i++ {
rows := [][]string{
{fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), fmt.Sprintf("张三%d", i), fmt.Sprintf("3%d", i)},
}
wg.Add(1)
go func(idx int) {
defer wg.Done()
rowsss, _ := GetManager().AppendRowsNum(handleID, rows)
fmt.Println(rows, ":", rowsss)
results[idx] = rowsss
}(i)
} }
fmt.Printf("列表包含 %d 个元素:\n", len(listValues)) wg.Wait()
for i, value := range listValues {
fmt.Printf(" [%d] %s\n", i, value) //rowsss, err := GetManager().AppendRowsss(handleID, rows)
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 { //if err != nil {
// fmt.Printf("创建目标文件失败: %v", err) // fmt.Println(err.Error())
//} //}
//// 合并 //fmt.Println(rowsss)
//totalRows, err := GetManager().MergeCSVFilesSimple(handleID, dstHandle, true)
//if err != nil {
// fmt.Printf("合并文件失败: %v", err)
//}
//fmt.Println(totalRows)
//for i := 0; i < 10000; i++ { GetManager().CloseHandle(handleID)
// // 打开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) // WriteRowss 批量写入多行数据到CSV文件
//if err != nil { // 返回写入的起始行号和写入的行数,错误时返回(-1, -1, error)
// fmt.Printf("写入表头失败: %v\n", err) func (mgr *CSVManager) WriteRowss(handleID int64, rows [][]string) (int64, int64, error) {
//} else { // 首先记录日志
// fmt.Println("写入表头成功") if err := mgr.logWriteRows(handleID, rows); err != nil {
//} // 日志记录失败不影响主流程,但可以打印警告
fmt.Printf("警告:记录日志失败: %v\n", err)
//// 写入测试数据 }
//rowsData := [][]string{
// {"1", "张三", "25", "zhangsan@example.com"}, // 获取句柄
// {"2", "李四", "30", "lisi@example.com"}, handle, err := mgr.getHandle(handleID)
// {"3", "王五", "28", "wangwu@example.com"}, if err != nil {
// {"4", "赵六", "35", "zhaoliu@example.com"}, return -1, -1, fmt.Errorf("WriteRows 获取句柄失败: %w", err)
//} }
// defer mgr.releaseHandle(handleID)
//totalRows, err := GetManager().WriteRows(handleID, rowsData)
//if err != nil { // 检查句柄状态
// fmt.Printf("批量写入数据失败: %v\n", err) if !handle.beginOperation() {
//} else { return -1, -1, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
// fmt.Printf("批量写入数据成功,行数: %d\n", totalRows) }
//} defer handle.endOperation()
//
//// 获取写入后的总行数 handle.mu.Lock()
//totalRows, err = GetManager().GetTotalRows(handleID) defer handle.mu.Unlock()
//if err != nil {
// fmt.Printf("获取总行数失败: %v\n", err) if !handle.IsOpen {
//} else { return -1, -1, fmt.Errorf("文件未打开")
// fmt.Printf("写入后总行数: %d\n", totalRows) }
//}
// 检查是否有表头
//// ============ 测试修改行功能 ============ if handle.HasHeader && len(handle.Header) > 0 {
// // 验证每行的列数
//fmt.Println("\n3. 测试修改行功能") for i, row := range rows {
// if len(row) != len(handle.Header) {
//// 修改第1行数据索引0 return -1, -1, fmt.Errorf("第%d行列数(%d)与表头列数(%d)不匹配", i+1, len(row), len(handle.Header))
//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行数据成功") if _, err := handle.File.Seek(0, 2); err != nil {
//} return -1, -1, fmt.Errorf("移动到文件末尾失败: %w", err)
// }
//// 修改第3行数据索引2
//newRow3 := []string{"3", "王五(已修改)", "29", "wangwu_updated@example.com"} // 记录写入前的行数作为起始行号
//err = GetManager().UpdateRow(handleID, 2, newRow3) // 注意行号从1开始如果是首次写入且没有表头TotalRows=0起始行号就是1
//if err != nil { startRow := handle.TotalRows + 1
// fmt.Printf("修改第3行数据失败: %v\n", err) if handle.HasHeader && startRow == 1 {
//} else { // 如果有表头数据从第2行开始
// fmt.Println("修改第3行数据成功") startRow = 2
//} }
//
//// 获取修改后的行数据 // 批量写入行数据
//fmt.Println("\n4. 获取修改后的行数据") for _, row := range rows {
if err := handle.CSVWriter.Write(row); err != nil {
//获取第1行 return -1, -1, fmt.Errorf("写入行失败: %w", err)
//row1, err := GetManager().GetRow(handleID, 10) }
//if err != nil { }
// fmt.Printf("获取第1行失败: %v\n", err)
//} else { // 更新行数统计
// fmt.Printf("第1行数据: %v\n", row1) rowsWritten := int64(len(rows))
//} handle.TotalRows += rowsWritten
handle.cachedRowCount = handle.TotalRows
//// 获取第3行 handle.rowCountCached = true
//row3, err := GetManager().GetRow(handleID, 2)
//if err != nil { return startRow, rowsWritten, nil
// fmt.Printf("获取第3行失败: %v\n", err) }
//} else {
// fmt.Printf("第3行数据: %v\n", row3) // AppendRows 批量追加行数据自动Flush
//} func (mgr *CSVManager) AppendRowss(handleID int64, rows [][]string) (int64, int64, error) {
totalRows, rowss, err := mgr.WriteRowss(handleID, rows)
//// ============ 测试批量修改功能 ============ if err != nil {
// return -1, -1, err
//fmt.Println("\n5. 测试批量修改功能") }
// err = mgr.Flush(handleID)
//updates := map[int64][]string{ if err != nil {
// 1: {"2", "李四(批量修改)", "31", "lisi_batch@example.com"}, return -1, -1, err
// 3: {"4", "赵六(批量修改)", "36", "zhaoliu_batch@example.com"}, }
//} return totalRows, rowss, nil
// }
//updatedCount, err := GetManager().UpdateRows(handleID, updates)
//if err != nil { // AppendRows 批量追加行数据自动Flush
// fmt.Printf("批量修改失败: %v\n", err) func (mgr *CSVManager) AppendRowsss(handleID int64, rows [][]string) ([]int64, error) {
//} else { rowsss, err := mgr.WriteRowsss(handleID, rows)
// fmt.Printf("批量修改成功,修改了%d行\n", updatedCount)
//} if err != nil {
// return nil, err
//// 验证批量修改结果 }
//row2, err := GetManager().GetRow(handleID, 1) err = mgr.Flush(handleID)
//if err != nil { if err != nil {
// fmt.Printf("获取第2行失败: %v\n", err) return nil, err
//} else { }
// fmt.Printf("第2行数据批量修改后: %v\n", row2) return rowsss, nil
//} }
//
//row4, err := GetManager().GetRow(handleID, 3) // WriteRows 批量写入多行数据到CSV文件
//if err != nil { // 返回每行数据存储的行号数组从1开始
// fmt.Printf("获取第4行失败: %v\n", err) func (mgr *CSVManager) WriteRowsss(handleID int64, rows [][]string) ([]int64, error) {
//} else { // 首先记录日志
// fmt.Printf("第4行数据批量修改后: %v\n", row4) if err := mgr.logWriteRows(handleID, rows); err != nil {
//} // 日志记录失败不影响主流程,但可以打印警告
// fmt.Printf("警告:记录日志失败: %v\n", err)
//// 获取最终总行数 }
//finalTotalRows, err := GetManager().GetTotalRows(handleID)
//if err != nil { // 获取句柄
// fmt.Printf("获取最终总行数失败: %v\n", err) handle, err := mgr.getHandle(handleID)
//} else { if err != nil {
// fmt.Printf("最终总行数: %d\n", finalTotalRows) return nil, fmt.Errorf("WriteRows 获取句柄失败: %w", err)
//} }
defer mgr.releaseHandle(handleID)
// 清理
//GetManager().closeAllHandles() // 检查句柄状态
fmt.Println("\n测试完成") if !handle.beginOperation() {
return nil, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
}
defer handle.endOperation()
handle.mu.Lock()
defer handle.mu.Unlock()
if !handle.IsOpen {
return nil, fmt.Errorf("文件未打开")
}
// 检查是否有表头
if handle.HasHeader && len(handle.Header) > 0 {
// 验证每行的列数
for i, row := range rows {
if len(row) != len(handle.Header) {
return nil, fmt.Errorf("第%d行列数(%d)与表头列数(%d)不匹配", i+1, len(row), len(handle.Header))
}
}
}
// 移动到文件末尾
if _, err := handle.File.Seek(0, 2); err != nil {
return nil, fmt.Errorf("移动到文件末尾失败: %w", err)
}
// 计算起始行号 - 关键修正部分
var startRow int64
// 先获取当前文件的实际行数(包括表头)
currentLineCount := handle.TotalRows
if handle.HasHeader && currentLineCount == 0 {
// 如果有表头但还没有数据行表头算第1行数据从第2行开始
startRow = 2
} else if handle.HasHeader {
// 有表头且已有数据行表头是第1行TotalRows不包括表头
// 所以下一行应该是 TotalRows + 2
startRow = currentLineCount + 2
} else {
// 没有表头,直接累加
startRow = currentLineCount + 1
}
// 创建行号数组
rowNumbers := make([]int64, len(rows))
for i := 0; i < len(rows); i++ {
rowNumbers[i] = startRow + int64(i)
}
// 批量写入行数据
for _, row := range rows {
if err := handle.CSVWriter.Write(row); err != nil {
return nil, fmt.Errorf("写入行失败: %w", err)
}
}
// 更新行数统计
handle.TotalRows += int64(len(rows))
handle.cachedRowCount = handle.TotalRows
handle.rowCountCached = true
return rowNumbers, nil
} }

Binary file not shown.

View File

@ -134,6 +134,10 @@ extern __declspec(dllexport) int GetError(char* buffer, int bufferSize);
// //
extern __declspec(dllexport) void FreeCString(char* str); extern __declspec(dllexport) void FreeCString(char* str);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,6 +1,5 @@
package main package main
//
///* ///*
//#include <stdlib.h> //#include <stdlib.h>
//*/ //*/
@ -1256,39 +1255,18 @@ package main
// C.free(unsafe.Pointer(str)) // C.free(unsafe.Pointer(str))
//} //}
// //
//// CSV_VERSION 版本号
//const (
// CSV_VERSION = "v1"
//)
//
//// 获取版本信息
////
////export GetVersion
//func GetVersion() *C.char {
// return C.CString(CSV_VERSION)
//}
//
//// 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
//}

101
csv/taskLog.csv Normal file
View File

@ -0,0 +1,101 @@
ID,序号,姓名,年龄
0,0,张三0,30
1,1,张三1,31
12,12,张三12,312
3,3,张三3,33
22,22,张三22,322
15,15,张三15,315
75,75,张三75,375
91,91,张三91,391
20,20,张三20,320
90,90,张三90,390
21,21,张三21,321
23,23,张三23,323
24,24,张三24,324
25,25,张三25,325
26,26,张三26,326
27,27,张三27,327
28,28,张三28,328
29,29,张三29,329
30,30,张三30,330
6,6,张三6,36
64,64,张三64,364
9,9,张三9,39
2,2,张三2,32
33,33,张三33,333
17,17,张三17,317
16,16,张三16,316
18,18,张三18,318
4,4,张三4,34
7,7,张三7,37
13,13,张三13,313
45,45,张三45,345
63,63,张三63,363
43,43,张三43,343
8,8,张三8,38
14,14,张三14,314
40,40,张三40,340
10,10,张三10,310
19,19,张三19,319
11,11,张三11,311
5,5,张三5,35
31,31,张三31,331
34,34,张三34,334
35,35,张三35,335
36,36,张三36,336
37,37,张三37,337
41,41,张三41,341
38,38,张三38,338
32,32,张三32,332
53,53,张三53,353
42,42,张三42,342
49,49,张三49,349
50,50,张三50,350
47,47,张三47,347
51,51,张三51,351
48,48,张三48,348
46,46,张三46,346
52,52,张三52,352
57,57,张三57,357
54,54,张三54,354
59,59,张三59,359
55,55,张三55,355
58,58,张三58,358
56,56,张三56,356
44,44,张三44,344
65,65,张三65,365
61,61,张三61,361
39,39,张三39,339
62,62,张三62,362
68,68,张三68,368
69,69,张三69,369
67,67,张三67,367
60,60,张三60,360
70,70,张三70,370
71,71,张三71,371
66,66,张三66,366
72,72,张三72,372
74,74,张三74,374
78,78,张三78,378
76,76,张三76,376
73,73,张三73,373
79,79,张三79,379
77,77,张三77,377
83,83,张三83,383
82,82,张三82,382
86,86,张三86,386
87,87,张三87,387
81,81,张三81,381
88,88,张三88,388
84,84,张三84,384
80,80,张三80,380
85,85,张三85,385
89,89,张三89,389
92,92,张三92,392
94,94,张三94,394
96,96,张三96,396
99,99,张三99,399
97,97,张三97,397
98,98,张三98,398
93,93,张三93,393
95,95,张三95,395
1 ID 序号 姓名 年龄
2 0 0 张三0 30
3 1 1 张三1 31
4 12 12 张三12 312
5 3 3 张三3 33
6 22 22 张三22 322
7 15 15 张三15 315
8 75 75 张三75 375
9 91 91 张三91 391
10 20 20 张三20 320
11 90 90 张三90 390
12 21 21 张三21 321
13 23 23 张三23 323
14 24 24 张三24 324
15 25 25 张三25 325
16 26 26 张三26 326
17 27 27 张三27 327
18 28 28 张三28 328
19 29 29 张三29 329
20 30 30 张三30 330
21 6 6 张三6 36
22 64 64 张三64 364
23 9 9 张三9 39
24 2 2 张三2 32
25 33 33 张三33 333
26 17 17 张三17 317
27 16 16 张三16 316
28 18 18 张三18 318
29 4 4 张三4 34
30 7 7 张三7 37
31 13 13 张三13 313
32 45 45 张三45 345
33 63 63 张三63 363
34 43 43 张三43 343
35 8 8 张三8 38
36 14 14 张三14 314
37 40 40 张三40 340
38 10 10 张三10 310
39 19 19 张三19 319
40 11 11 张三11 311
41 5 5 张三5 35
42 31 31 张三31 331
43 34 34 张三34 334
44 35 35 张三35 335
45 36 36 张三36 336
46 37 37 张三37 337
47 41 41 张三41 341
48 38 38 张三38 338
49 32 32 张三32 332
50 53 53 张三53 353
51 42 42 张三42 342
52 49 49 张三49 349
53 50 50 张三50 350
54 47 47 张三47 347
55 51 51 张三51 351
56 48 48 张三48 348
57 46 46 张三46 346
58 52 52 张三52 352
59 57 57 张三57 357
60 54 54 张三54 354
61 59 59 张三59 359
62 55 55 张三55 355
63 58 58 张三58 358
64 56 56 张三56 356
65 44 44 张三44 344
66 65 65 张三65 365
67 61 61 张三61 361
68 39 39 张三39 339
69 62 62 张三62 362
70 68 68 张三68 368
71 69 69 张三69 369
72 67 67 张三67 367
73 60 60 张三60 360
74 70 70 张三70 370
75 71 71 张三71 371
76 66 66 张三66 366
77 72 72 张三72 372
78 74 74 张三74 374
79 78 78 张三78 378
80 76 76 张三76 376
81 73 73 张三73 373
82 79 79 张三79 379
83 77 77 张三77 377
84 83 83 张三83 383
85 82 82 张三82 382
86 86 86 张三86 386
87 87 87 张三87 387
88 81 81 张三81 381
89 88 88 张三88 388
90 84 84 张三84 384
91 80 80 张三80 380
92 85 85 张三85 385
93 89 89 张三89 389
94 92 92 张三92 392
95 94 94 张三94 394
96 96 96 张三96 396
97 99 99 张三99 399
98 97 97 张三97 397
99 98 98 张三98 398
100 93 93 张三93 393
101 95 95 张三95 395

BIN
erp/dll/erp.dll Normal file

Binary file not shown.

101
erp/dll/erp.h Normal file
View File

@ -0,0 +1,101 @@
/* 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 "erp.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
// InterfaceForward 接口转发
//
extern __declspec(dllexport) char* InterfaceForward(char* requestMethod, char* erpUrl, char* requestJson);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

Binary file not shown.

View File

@ -0,0 +1,125 @@
/* 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 "expressDeliveryOrder.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
// ZtoOpenCreateOrder 中通快递--创建订单接口
//
extern __declspec(dllexport) char* ZtoOpenCreateOrder(char* requestJSON, char* appKey, char* appSecret);
// JtOrderAddOrder 极兔快递--创建订单接口
//
extern __declspec(dllexport) char* JtOrderAddOrder(char* requestJSON, char* apiAccount, char* privateKey);
// EmsAmpApiOpen 邮政快递--订单接入接口
//
extern __declspec(dllexport) char* EmsAmpApiOpen(char* requestJSON, char* secretKey);
// StoOmsExpressOrderCreate 申通快递--订单接入接口
//
extern __declspec(dllexport) char* StoOmsExpressOrderCreate(char* requestJSON, char* fromAppkey, char* secretKey, char* fromCode);
// YdCreateBmOrder 韵达快递--电子面单下单
//
extern __declspec(dllexport) char* YdCreateBmOrder(char* requestJSON, char* appKey, char* appSecret);
// YdBmGetPdfInfo 韵达快递--电子面单打印
//
extern __declspec(dllexport) char* YdBmGetPdfInfo(char* requestJSON, char* appKey, char* appSecret);
// IntegrationOrderCreate 整合所有快递--订单接口
//
extern __declspec(dllexport) char* IntegrationOrderCreate(char* orderType, char* requestJSON, char* key, char* secret, char* fromCode);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
package main
import "fmt"
func main() {
//ztoJSON := `{"partnerType":"2","orderType":"1","partnerOrderCode":"商家自主定义","accountInfo":{"accountId":"test","accountPassword":"","type":1,"customerId":"GPG1576724269"},"billCode":"","senderInfo":{"senderId":"","senderName":"张三","senderPhone":"010-22226789","senderMobile":"13900000000","senderProvince":"上海","senderCity":"上海市","senderDistrict":"青浦区","senderAddress":"华志路"},"receiveInfo":{"receiverName":"Jone Star","receiverPhone":"021-87654321","receiverMobile":"13500000000","receiverProvince":"上海","receiverCity":"上海市","receiverDistrict":"闵行区","receiverAddress":"申贵路1500号"},"orderVasList":[{"vasType":"COD","vasAmount":100000,"vasPrice":0,"vasDetail":"","accountNo":""}],"hallCode":"S2044","siteCode":"02100","siteName":"上海","summaryInfo":{"size":"","quantity":3,"price":30,"freight":20,"premium":10,"startTime":"2020-12-10 12:00:00","endTime":"2020-12-10 12:00:00"},"remark":"小吉下单","orderItems":[{"name":"","category":"","material":"","size":"","weight":0,"unitprice":0,"quantity":0,"remark":""}],"cabinet":{"address":"","specification":0,"code":""}}`
//order, err := ztoOpenCreateOrder(ztoJSON, "2e2858ad0c0006150011b", "5f935e459b9fc50db8220714adcb2c2e")
//if err != nil {
// fmt.Printf(err.Error())
//}
//fmt.Println(order)
jtJSON := `{"customerCode":"J0086474299","digest":"qonqb4O1eNr6VCWS07Ieeg==","network":null,"txlogisticId":"TEST20220704210006","expressType":"EZ","orderType":"1","serviceType":"01","deliveryType":"06","payType":"PP_PM","sender":{"name":"小九","company":null,"postCode":null,"mailBox":null,"mobile":"15546168286","phone":"","countryCode":"CHN","prov":"上海","city":"上海市","area":"青浦区","town":null,"street":null,"address":"庆丰三路28号"},"receiver":{"name":"田丽","company":null,"postCode":null,"mailBox":null,"mobile":"13766245825","phone":"","countryCode":"CHN","prov":"上海","city":"上海市","area":"嘉定区","town":null,"street":null,"address":"站前西路永利酒店斜对面童装店"},"sendStartTime":"2022-07-04 09:00:00","sendEndTime":"2022-07-04 18:00:00","goodsType":"bm000006","length":0,"width":0,"height":0,"weight":"0.02","totalQuantity":0,"itemsValue":null,"priceCurrency":null,"offerFee":null,"remark":null,"items":[{"itemType":null,"itemName":"衣帽鞋服","chineseName":null,"englishName":null,"number":1,"itemValue":null,"priceCurrency":"RMB","desc":null,"itemUrl":null}],"customsInfo":null,"postSiteCode":null,"postSiteName":null,"postSiteAddress":null,"realName":null, "extendInfo": { "isFourLevelAddress": "1" }}`
order, err := jtOrderAddOrder(jtJSON, "795641885325495168", "d8c262832bff4b8a9dd6e06147024861")
if err != nil {
fmt.Printf(err.Error())
}
fmt.Println(order)
}

View File

@ -1,4 +1,4 @@
package main package gin
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"

27
go.mod
View File

@ -3,6 +3,7 @@ module kfzgw-info
go 1.25 go 1.25
require ( require (
gioui.org v0.9.0
github.com/PuerkitoBio/goquery v1.10.3 github.com/PuerkitoBio/goquery v1.10.3
github.com/boombuler/barcode v1.1.0 github.com/boombuler/barcode v1.1.0
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
@ -14,21 +15,24 @@ require (
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/tjfoc/gmsm v1.4.1
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.26.0
golang.org/x/sys v0.37.0 golang.org/x/sys v0.37.0
gopkg.in/ini.v1 v1.67.1
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.44.3
) )
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
gioui.org/shader v1.0.8 // indirect
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/cespare/xxhash/v2 v2.3.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/dustin/go-humanize v1.0.1 // 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
@ -38,19 +42,24 @@ require (
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/go-text/typesetting v0.3.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/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/moul/http2curl v1.0.0 // indirect github.com/moul/http2curl v1.0.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect github.com/quic-go/quic-go v0.54.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/smartystreets/goconvey v1.8.1 // indirect github.com/smartystreets/goconvey v1.8.1 // indirect
@ -65,11 +74,17 @@ require (
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.43.0 // indirect golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.28.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.46.0 // indirect golang.org/x/net v0.46.0 // indirect
golang.org/x/sync v0.17.0 // indirect golang.org/x/sync v0.17.0 // indirect
golang.org/x/text v0.30.0 // indirect golang.org/x/text v0.30.0 // indirect
golang.org/x/tools v0.37.0 // indirect golang.org/x/tools v0.38.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.36.9 // indirect google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
modernc.org/libc v1.67.6 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
) )

156
go.sum
View File

@ -1,36 +1,46 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gioui.org v0.9.0 h1:4u7XZwnb5kzQW91Nz/vR0wKD6LdW9CaVF96r3rfy4kc=
gioui.org v0.9.0/go.mod h1:CjNig0wAhLt9WZxOPAusgFD8x8IRvqt26LdDBa3Jvao=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
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 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
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=
github.com/elastic/elastic-transport-go/v8 v8.7.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= github.com/elastic/elastic-transport-go/v8 v8.7.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
github.com/elastic/go-elasticsearch/v8 v8.19.0 h1:VmfBLNRORY7RZL+9hTxBD97ehl9H8Nxf2QigDh6HuMU= github.com/elastic/go-elasticsearch/v8 v8.19.0 h1:VmfBLNRORY7RZL+9hTxBD97ehl9H8Nxf2QigDh6HuMU=
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= 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/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=
@ -54,24 +64,53 @@ 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/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
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 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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=
@ -84,27 +123,33 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= 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/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
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/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=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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=
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
@ -114,14 +159,18 @@ github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4= github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
@ -146,6 +195,8 @@ go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
@ -153,17 +204,31 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@ -174,6 +239,9 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -183,7 +251,10 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -218,19 +289,72 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

BIN
image/123.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

866
kfztp.go
View File

@ -1,866 +0,0 @@
package main
//import (
// "crypto/md5"
// "encoding/json"
// "fmt"
// "log"
// "math/rand"
// "net/http"
// "net/url"
// "regexp"
// "strconv"
// "strings"
// "sync"
// "time"
//
// "github.com/parnurzeal/gorequest"
//)
//
//// 获取商品模版
//func outGetGoodsTplMsgLinux(token, itemId, proxy string) (map[string]interface{}, error) {
// if token == "" {
// return nil, fmt.Errorf("请先登录获取Token")
// }
//
// url := fmt.Sprintf("https://seller.kongfz.com/pc/itemInfo/getTplFields?itemId=%s&isClone=1&v=%d",
// itemId, time.Now().Unix())
//
// log.Printf("请求URL: %s", url)
//
// // 创建HTTP客户端
// request := gorequest.New()
//
// // 设置代理如果有提供代理URL
// if proxy != "" {
// request.Proxy(proxy)
// log.Printf("使用代理: %s", proxy)
// }
//
// resp, body, errs := request.Get(url).
// Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
// Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
// Set("Accept", "application/json, text/plain, */*").
// Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
// Set("Referer", "https://seller.kongfz.com/").
// Set("Origin", "https://seller.kongfz.com").
// Timeout(30 * time.Second).
// End()
//
// if len(errs) > 0 {
// return nil, fmt.Errorf("请求失败: %v", errs)
// }
//
// // 检查HTTP状态码
// if resp.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("HTTP错误: %d - %s", resp.StatusCode, resp.Status)
// }
//
// var data map[string]interface{}
// if err := json.Unmarshal([]byte(body), &data); err != nil {
// return nil, fmt.Errorf("解析JSON失败: %w", err)
// }
//
// // 检查API响应状态
// if val, ok := data["status"].(float64); ok && val == 1 {
// return data, nil
// }
//
// return nil, fmt.Errorf("API返回错误: %+v", data)
//}
//
//func main() {
// fmt.Println("=== 孔夫子商品模板API服务 ===")
//
// http.HandleFunc("/api/goodsTemplate", getGoodsTemplateHandler)
// http.HandleFunc("/api/outGetImageByIsbn", getOutGetImageByIsbnHandler)
//
// port := "8989"
// server := &http.Server{
// Addr: ":" + port,
// Handler: nil,
// }
// // 启动服务器
// if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
// fmt.Printf("服务器启动失败: %v\n", err)
// }
//}
//
//// 获取商品模板接口
//func getGoodsTemplateHandler(w http.ResponseWriter, r *http.Request) {
// // 设置响应头
// w.Header().Set("Content-Type", "application/json")
//
// // 只处理GET请求
// if r.Method != http.MethodGet {
// w.WriteHeader(http.StatusMethodNotAllowed)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": "只支持GET请求",
// })
// return
// }
//
// // 获取查询参数
// token := r.URL.Query().Get("token")
// itemId := r.URL.Query().Get("itemId")
// proxy := r.URL.Query().Get("proxy")
//
// // 验证必需参数
// if token == "" || itemId == "" {
// w.WriteHeader(http.StatusBadRequest)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": "缺少必需参数: token 和 itemid",
// "usage": map[string]string{
// "token": "登录Token (PHPSESSID)",
// "itemid": "商品ID",
// "proxy": "代理地址 (可选)",
// },
// "example": "GET /api/goods-template?token=abc123&itemid=123456&proxy=http://proxy:8080",
// })
// return
// }
//
// log.Printf("收到请求 - 商品ID: %s", itemId)
//
// // 调用函数获取商品模板
// result, err := outGetGoodsTplMsgLinux(token, itemId, proxy)
// if err != nil {
// log.Printf("获取商品模板失败: %v", err)
// w.WriteHeader(http.StatusInternalServerError)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": err.Error(),
// })
// return
// }
//
// // 返回成功响应
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": true,
// "data": result,
// })
//}
//
//func getOutGetImageByIsbnHandler(w http.ResponseWriter, r *http.Request) {
// // 设置响应头
// w.Header().Set("Content-Type", "application/json")
//
// // 只处理GET请求
// if r.Method != http.MethodGet {
// w.WriteHeader(http.StatusMethodNotAllowed)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": "只支持GET请求",
// })
// return
// }
//
// // 获取查询参数
// token := r.URL.Query().Get("token")
// isbn := r.URL.Query().Get("isbn")
// proxyType := r.URL.Query().Get("proxyType")
// username := r.URL.Query().Get("username")
// password := r.URL.Query().Get("password")
// machineCode := r.URL.Query().Get("machineCode")
// //proxy := r.URL.Query().Get("proxy")
//
// // 验证必需参数
// if token == "" || isbn == "" {
// w.WriteHeader(http.StatusBadRequest)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": "缺少必需参数: token 和 isbn",
// "usage": map[string]string{
// "token": "登录Token (PHPSESSID)",
// "isbn": "isbn",
// "proxy": "代理地址 (可选)",
// },
// "example": "GET /api/goods-template?token=abc123&itemid=123456&proxy=http://proxy:8080",
// })
// return
// }
//
// manager, err2 := proxyTypeManager(proxyType, username, password, machineCode)
// if err2 != nil {
// return
// }
// log.Printf("收到请求 - 商品ID: %s", isbn)
//
// // 调用函数获取商品模板
// result, err := OutGetImageByIsbns(token, isbn, manager, 0, 0)
// if err != nil {
// log.Printf("获取商品模板失败: %v", err)
// w.WriteHeader(http.StatusInternalServerError)
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": false,
// "error": err.Error(),
// })
// return
// }
// // 返回成功响应
// json.NewEncoder(w).Encode(map[string]interface{}{
// "success": true,
// "data": result,
// })
//}
//
//// 条目详情结构体
//type BookInfo struct {
// BookName string `json:"book_name"` // 书名
// Author string `json:"author"` // 作者
// Publisher string `json:"publisher"` // 出版社
// ISBN string `json:"isbn"` // ISBN
// PublicationTime int64 `json:"publication_time"` // 出版时间
// Edition string `json:"edition"` // 版次
// PrintTime string `json:"print_time"` // 印刷时间
// FixPrice string `json:"fix_price"` // 定价
// BindingLayout string `json:"binding_layout"` // 装帧
// Format string `json:"format"` // 开本
// Paper string `json:"paper"` // 纸张
// Pages string `json:"pages"` // 页数
// Wordage string `json:"wordage"` // 字数
// Languages string `json:"languages"` // 语种
// Era string `json:"era"` // 年代
// EngravingMethod string `json:"engraving_method"` // 刻印方式
// Dimensions string `json:"dimensions"` // 尺寸
// VolumeNumber string `json:"volume_number"` // 册数
// BookPic string `json:"book_pic"` // 图书封面图(官图)
// BookPicS string `json:"book_pic_s"` // 图书封面图(实拍图)
// SellingPrice string `json:"selling_price"` // 售价
// Condition string `json:"condition"` // 品相
// ExpressDeliveryFee string `json:"express_delivery_fee"` // 快递费
// Editor string `json:"editor"` // 编辑
// Category string `json:"category"` // 分类
// BuyCount string `json:"buy_count"` // 买过
// SellCount string `json:"sell_count"` // 在卖
// Content string `json:"content"` // 内容
// Mid int64 `json:"mid"` // 商家id
// ItemId int64 `json:"item_id"` // 商品id
// ShopId int64 `json:"shop_id"` // 店铺id
// DetailUrl string `json:"detail_url"` // 商品详情url
//}
//
//// 获取图片URL(官图和拍图)
//func OutGetImageByIsbns(token string, isbn string, proxy string, isLiveImage int, isReturnMsg int) (*BookInfo, error) {
// fmt.Println("[DEBUG] 使用的ISBN: ", isbn)
// // isLiveImage 1实拍图 0官图 isReturnMsg 0商品信息
// bookInfo := &BookInfo{}
// //if isLiveImage == 0 {
// // 孔网官图请求
// gtUrl := fmt.Sprintf("%s?keyword=%s", "https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list", isbn)
// // 创建HTTP客户端
// requestGt := gorequest.New()
// // 设置代理(如果有提供代理URL)
// if proxy != "" {
// requestGt.Proxy(proxy)
// }
// // 发送请求
// respGt, bodyGt, errsGt := requestGt.Get(gtUrl).
// Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
// Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
// Set("Accept", "application/json, text/plain, */*").
// Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
// Set("Referer", "https://item.kongfz.com/").
// Timeout(30 * time.Second).
// End()
// if len(errsGt) > 0 {
// // 检查是否是代理相关错误
// var isProxyError bool
// var errorDetails []string
// for _, e := range errsGt {
// errorStr := e.Error()
// errorDetails = append(errorDetails, errorStr)
// if strings.Contains(errorStr, "Proxy Authentication Required") ||
// strings.Contains(errorStr, "connectex: A connection attempt failed") ||
// strings.Contains(errorStr, "connectex: No connection could be made") ||
// strings.Contains(errorStr, "proxyconnect tcp") ||
// strings.Contains(errorStr, "timeout") ||
// strings.Contains(errorStr, "connection refused") {
// isProxyError = true
// }
// }
// log.Printf("[ERROR] 请求错误详情: %v", errorDetails)
// if isProxyError {
// // 处理代理失败
// return nil, fmt.Errorf("代理连接失败")
// }
// return nil, fmt.Errorf("查询请求失败: %v", errsGt)
// }
// //检查HTTP状态码
// if respGt.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("HTTP错误: %s", respGt.Status)
// }
// // 解析响应
// var apiGtResp struct {
// Status int `json:"status"`
// ErrType string `json:"errType"`
// Message string `json:"message"`
// SystemTime int64 `json:"systemTime"`
// Data struct {
// ItemResponse struct {
// Total int `json:"total"`
// List []struct {
// BookName string `json:"bookName"`
// Mid int64 `json:"mid"`
// ImgUrlEntity struct {
// BigImgUrl string `json:"bigImgUrl"`
// } `json:"imgUrlEntity"`
// Isbn string `json:"isbn"`
// BookShowInfo []string `json:"bookShowInfo"`
// } `json:"list"`
// } `json:"itemResponse"`
// } `json:"data"`
// }
// if err := json.Unmarshal([]byte(bodyGt), &apiGtResp); err != nil {
// return nil, fmt.Errorf("解析JSON失败: %w", err)
// }
// if apiGtResp.ErrType == "102" {
// return nil, fmt.Errorf("错误信息: %w状态码: %s", apiGtResp.Message, apiGtResp.ErrType)
// }
// // 如果找到条目返回图片URL
// if apiGtResp.Data.ItemResponse.Total > 0 && len(apiGtResp.Data.ItemResponse.List) > 0 {
// list := apiGtResp.Data.ItemResponse.List[0]
// bookShowInfo := list.BookShowInfo
// bookInfo.BookName = list.BookName
// bookInfo.BookPic = list.ImgUrlEntity.BigImgUrl
// bookInfo.ISBN = list.Isbn
// // 根据长度安全填充字段
// if isReturnMsg == 0 {
// if len(bookShowInfo) > 0 {
// bookInfo.Author = bookShowInfo[0]
// }
// if len(bookShowInfo) > 1 {
// bookInfo.Publisher = bookShowInfo[1]
// }
// if len(bookShowInfo) > 2 {
// bookInfo.PublicationTime = validateDateFormat(bookShowInfo[2])
// }
// if len(bookShowInfo) > 3 {
// bookInfo.BindingLayout = bookShowInfo[3]
// }
// if len(bookShowInfo) > 4 {
// bookInfo.FixPrice = bookShowInfo[4]
// } else {
// log.Printf("[WARN] BookShowInfo 长度不足 (仅 %d 项): %v", len(bookShowInfo), bookShowInfo)
// }
// }
// }
// //return bookInfo, nil
//
// //}
// //if isLiveImage == 1 {
//
// size := 10
// // 实拍图
// sptUrl := fmt.Sprintf("%s?dataType=0&keyword=%s&page=1&size=%d&sortType=7&actionPath=quality,sortType&quality=85~&quaSelect=2&userArea=13003000000",
// "https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list", isbn, size)
// //创建HTTP客户端
// requestSpt := gorequest.New()
// //设置代理(如果有提供代理URL)
// if proxy != "" {
// requestSpt.Proxy(proxy)
// }
// // 发送请求
// respSpt, bodySpt, errsSpt := requestSpt.Get(sptUrl).
// Proxy(proxy).
// Set("Cookie", token).
// Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
// Set("Accept", "application/json, text/plain, */*").
// Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
// Set("Referer", "https://item.kongfz.com/").
// Timeout(30 * time.Second).
// End()
// // 错误处理
// if len(errsSpt) > 0 {
// return nil, fmt.Errorf("请求失败: %v", errsSpt)
// }
// // 检查HTTP状态码
// if respSpt.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("HTTP错误: %s", respSpt.Status)
// }
// // 解析响应
// var apiSptResp struct {
// Status int `json:"status"`
// ErrType string `json:"errType"`
// Message string `json:"message"`
// SystemTime int64 `json:"systemTime"`
// Data struct {
// ItemResponse struct {
// Total int `json:"total"`
// List []struct {
// ItemId int64 `json:"itemId"`
// Title string `json:"title"`
// ImgUrl string `json:"imgUrl"`
// ImgBigUrl string `json:"imgBigUrl"`
// Author string `json:"author"`
// PubDateText string `json:"pubDateText"`
// Isbn string `json:"isbn"`
// Press string `json:"press"`
// ShopId int64 `json:"shopId"`
// TplRecords []struct {
// Key string `json:"key"`
// Value string `json:"value"`
// } `json:"tplRecords"`
// } `json:"list"`
// } `json:"itemResponse"`
// } `json:"data"`
// }
// // 解析JSON
// if err := json.Unmarshal([]byte(bodySpt), &apiSptResp); err != nil {
// return nil, fmt.Errorf("解析JSON失败: %w", err)
// }
// if apiSptResp.ErrType == "102" {
// return nil, fmt.Errorf("错误信息: %w状态码: %s", apiSptResp.Message, apiSptResp.ErrType)
// }
// if apiSptResp.Data.ItemResponse.Total > 0 && len(apiSptResp.Data.ItemResponse.List) > 0 {
// // 确定其实索引
// var startIndex int
// if size >= apiSptResp.Data.ItemResponse.Total {
// startIndex = apiSptResp.Data.ItemResponse.Total - 1
// } else {
// startIndex = size - 1
// }
// for attempt := 0; attempt < 3; attempt++ {
// currentIndex := startIndex - attempt
// // 检查索引是否有效
// if currentIndex < 0 || currentIndex >= len(apiSptResp.Data.ItemResponse.List) {
// log.Printf("[DEBUG] 索引 %d 超出范围,跳过", currentIndex)
// continue
// }
// randomNum := rand.Intn(currentIndex + 1)
// item := apiSptResp.Data.ItemResponse.List[randomNum]
// // 检查图片URL是否存在
// if item.ImgBigUrl == "" {
// log.Printf("[DEBUG] 索引 %d 的图片URL为空跳过", randomNum)
// continue
// }
// // 响应信息
// bookInfo.BookPicS = item.ImgUrl
// bookInfo.ISBN = item.Isbn
// if bookInfo.BookName == "" {
// bookInfo.BookName = item.Title
// if isReturnMsg == 0 {
// // 安全地获取TplRecords中的值
// if len(item.TplRecords) > 0 {
// bookInfo.Author = item.TplRecords[0].Value
// }
// if len(item.TplRecords) > 1 {
// bookInfo.Publisher = item.TplRecords[1].Value
// }
// if len(item.TplRecords) > 2 {
// bookInfo.PublicationTime = validateDateFormat(item.TplRecords[2].Value)
// }
// if len(item.TplRecords) > 3 {
// bookInfo.BindingLayout = item.TplRecords[3].Value
// }
// }
// }
// return bookInfo, nil
// }
// //}
// }
// return nil, fmt.Errorf("查询失败,没有数据!")
//}
//
//// 验证日期格式
//func validateDateFormat(dateStr string) int64 {
// // 去除前后空格
// dateStr = strings.TrimSpace(dateStr)
//
// // 替换各种分隔符为统一的分隔符"-"
// dateStr = regexp.MustCompile(`[/_\\.,\s]+`).ReplaceAllString(dateStr, "-")
//
// // 处理纯年份格式 (4位数字)
// if regexp.MustCompile(`^\d{4}$`).MatchString(dateStr) {
// dateStr += "-01-01"
// }
//
// // 处理年月格式 (4位数字-1或2位数字)
// if matches := regexp.MustCompile(`^(\d{4})-(\d{1,2})$`).FindStringSubmatch(dateStr); len(matches) == 3 {
// year := matches[1]
// month := matches[2]
// if len(month) == 1 {
// month = "0" + month
// }
// if monthNum, _ := strconv.Atoi(month); monthNum >= 1 && monthNum <= 12 {
// dateStr = year + "-" + month + "-01"
// } else {
// return 0
// }
// }
//
// // 处理年月日格式 (4位数字-1或2位数字-1或2位数字)
// if matches := regexp.MustCompile(`^(\d{4})-(\d{1,2})-(\d{1,2})`).FindStringSubmatch(dateStr); len(matches) >= 4 {
// year := matches[1]
// month := matches[2]
// day := matches[3]
//
// // 标准化月份和日期为两位数
// if len(month) == 1 {
// month = "0" + month
// }
// if len(day) == 1 {
// day = "0" + day
// }
//
// dateStr = year + "-" + month + "-" + day
// }
//
// // 加载上海时区
// loc, err := time.LoadLocation("Asia/Shanghai")
// if err != nil {
// // 如果加载时区失败使用UTC+8作为后备方案
// loc = time.FixedZone("CST", 8*60*60)
// }
//
// // 尝试解析为标准日期格式,并指定上海时区
// parsedTime, err := time.ParseInLocation("2006-01-02", dateStr, loc)
// if err != nil {
// return 0
// }
//
// // 返回秒级时间戳UTC时间但解析时已经考虑了时区偏移
// return parsedTime.Unix()
//}
//
//// 代理类型常量
//const (
// CalfElephantProxyType = "CALF_ELEPHANT_PROXY"
// TailProxyType = "TAIL_PROXY"
//)
//
//// 代理服务器列表
//var (
// servers = []string{
// "http-dynamic.xiaoxiangdaili.com",
// "http-dynamic-S02.xiaoxiangdaili.com",
// "http-dynamic-S03.xiaoxiangdaili.com",
// "http-dynamic-S04.xiaoxiangdaili.com",
// }
// randMutex sync.Mutex
// globalRand *rand.Rand
//
// // 代理健康状态管理
// proxyHealthMaps = make(map[string]*ProxyHealth)
// proxyHealthMutex sync.RWMutex
//)
//
//// ProxyManager 代理管理器结构体
//type ProxyManager struct {
// servers []string `json:"servers"` // 代理服务器列表
// username string `json:"username"` // 代理账号
// password string `json:"password"` // 代理密码
// tailCardSecret string `json:"tail_card_secret"` // 尾巴代理卡密
// proxyType string `json:"proxy_type"` // 代理类型 CALF_ELEPHANT_PROXY/TAIL_PROXY
//}
//
//// 代理健康状态
//type ProxyHealth struct {
// SuccessCount int // 成功次数
// FailCount int // 失败次数
// LastCheck time.Time // 最后检查时间
// ResponseTime time.Duration // 响应时间
// IsHealthy bool // 是否健康
//}
//
//func init() {
// // 创建全局的随机数生成器
// globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
//}
//
//// 获取代理URL
//func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
// switch proxyType {
// case CalfElephantProxyType:
// return buildCalfElephantProxyURL(username, password)
// case TailProxyType:
// return buildTailProxyURL(machineCode)
// default:
// return "", fmt.Errorf("不支持的代理类型: %s", proxyType)
// }
//}
//
//// 构建小象代理URL
//func buildCalfElephantProxyURL(username, password string) (string, error) {
// server := randomServer()
// proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
// url.QueryEscape(username),
// url.QueryEscape(password),
// server,
// 10030)
//
// // 检测代理可用性
// if err := checkProxyHealth(proxyURL); err != nil {
// log.Printf("[WARN] 代理 %s 检测失败: %v", server, err)
// // 尝试下一个代理服务器
// return tryNextCalfElephantProxy(username, password, server)
// }
//
// log.Printf("[INFO] 使用小象代理: %s", server)
// return proxyURL, nil
//}
//
//// 尝试下一个小象代理服务器
//func tryNextCalfElephantProxy(username, password, failedServer string) (string, error) {
// // 创建服务器副本并排除失败的服务器
// availableServers := make([]string, 0)
// for _, server := range servers {
// if server != failedServer {
// availableServers = append(availableServers, server)
// }
// }
//
// if len(availableServers) == 0 {
// return "", fmt.Errorf("所有小象代理服务器都不可用")
// }
//
// // 随机尝试可用服务器
// for _, server := range shuffleServers(availableServers) {
// proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
// url.QueryEscape(username),
// url.QueryEscape(password),
// server,
// 10030)
//
// if err := checkProxyHealth(proxyURL); err == nil {
// log.Printf("[INFO] 切换到可用代理: %s", server)
// return proxyURL, nil
// }
// log.Printf("[WARN] 代理 %s 检测失败", server)
// }
//
// return "", fmt.Errorf("所有可用的小象代理服务器都检测失败")
//}
//
//// 构建内置代理URL
//func buildTailProxyURL(machineCode string) (string, error) {
// proxies, err := getProxies(machineCode)
// if err != nil {
// return "", err
// }
// if len(proxies) == 0 {
// return "", fmt.Errorf("未获取到有效代理")
// }
//
// // 过滤并选择健康的代理
// healthyProxies := filterHealthyProxies(proxies)
// if len(healthyProxies) > 0 {
// proxyURL := "http://" + randomElement(healthyProxies)
// log.Printf("[INFO] 使用健康尾巴代理: %s", proxyURL)
// return proxyURL, nil
// }
//
// // 如果没有健康代理,检测所有代理
// log.Printf("[INFO] 未找到健康代理,开始检测代理可用性...")
// return findWorkingTailProxy(proxies)
//}
//
//// 获取代理服务器列表
//func getProxies(machineCode string) ([]string, error) {
// log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
// // 生成签名
// sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode))))
//
// GetProxiesUrl := fmt.Sprintf("http://114.66.2.223:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999",
// machineCode, sign)
//
// req := gorequest.New().Get(GetProxiesUrl).Timeout(20 * time.Second)
// _, body, errs := req.End()
// if len(errs) > 0 {
// return nil, fmt.Errorf("获取代理失败: %v", errs)
// }
//
// // 检查是否为JSON错误响应
// if strings.HasPrefix(strings.TrimSpace(body), "{") && strings.HasSuffix(strings.TrimSpace(body), "}") {
// // 尝试解析为JSON错误响应
// var errorResp struct {
// Code int `json:"code"`
// Message string `json:"message"`
// }
// if err := json.Unmarshal([]byte(body), &errorResp); err == nil {
// return nil, fmt.Errorf("获取代理失败: %s (错误码: %d)", errorResp.Message, errorResp.Code)
// }
// }
//
// // 解析响应
// lines := strings.Split(strings.TrimSpace(body), "\n")
// var proxies []string
// for _, line := range lines {
// line = strings.TrimSpace(line)
// if line != "" && !strings.HasPrefix(line, "{") {
// proxies = append(proxies, line)
// }
// }
// if len(proxies) == 0 {
// return nil, fmt.Errorf("未获取到有效代理")
// }
//
// log.Printf("[INFO] 获取到 %d 个代理", len(proxies))
// return proxies, nil
//}
//
//// 过滤健康代理
//func filterHealthyProxies(proxies []string) []string {
// proxyHealthMutex.RLock()
// defer proxyHealthMutex.RUnlock()
//
// var healthy []string
// for _, proxy := range proxies {
// if health, exists := proxyHealthMaps[proxy]; exists && health.IsHealthy {
// // 检查是否在最近检查过5分钟内
// if time.Since(health.LastCheck) < 5*time.Minute {
// healthy = append(healthy, proxy)
// }
// }
// }
// return healthy
//}
//
//// 查找可用的尾巴代理
//func findWorkingTailProxy(proxies []string) (string, error) {
// // 打乱代理顺序
// shuffledProxies := shuffleServers(proxies)
//
// // 并发检测代理
// type proxyResult struct {
// proxy string
// err error
// }
//
// ch := make(chan proxyResult, len(shuffledProxies))
// var wg sync.WaitGroup
//
// // 限制并发数
// sem := make(chan struct{}, 5)
//
// for _, proxy := range shuffledProxies {
// wg.Add(1)
// go func(p string) {
// defer wg.Done()
// sem <- struct{}{}
// defer func() { <-sem }()
//
// proxyURL := "http://" + p
// err := checkProxyHealth(proxyURL)
// ch <- proxyResult{proxy: p, err: err}
// }(proxy)
// }
//
// wg.Wait()
// close(ch)
//
// // 收集结果
// var workingProxies []string
// for result := range ch {
// if result.err == nil {
// workingProxies = append(workingProxies, result.proxy)
// // 更新健康状态
// updateProxyHealth(result.proxy, true, 0)
// } else {
// updateProxyHealth(result.proxy, false, 0)
// }
// }
//
// if len(workingProxies) > 0 {
// selected := randomElement(workingProxies)
// log.Printf("[INFO] 找到可用代理: %s (共 %d 个可用)", selected, len(workingProxies))
// return "http://" + selected, nil
// }
//
// return "", fmt.Errorf("所有尾巴代理都不可用")
//}
//
//// 检测代理健康状态
//func checkProxyHealth(proxyURL string) error {
// start := time.Now()
//
// req := gorequest.New().Proxy(proxyURL).Timeout(10 * time.Second)
// resp, _, errs := req.Get("https://shop.kongfz.com/").End()
//
// responseTime := time.Since(start)
//
// if len(errs) > 0 {
// updateProxyHealth(proxyURL, false, responseTime)
// return fmt.Errorf("代理连接失败: %v", errs)
// }
//
// if resp.StatusCode != 200 {
// updateProxyHealth(proxyURL, false, responseTime)
// return fmt.Errorf("代理响应状态码错误: %d", resp.StatusCode)
// }
//
// updateProxyHealth(proxyURL, true, responseTime)
// log.Printf("[DEBUG] 代理 %s 检测成功, 响应时间: %v", getProxyHost(proxyURL), responseTime)
// return nil
//}
//
//// 更新代理健康状态
//func updateProxyHealth(proxyURL string, success bool, responseTime time.Duration) {
// proxyHealthMutex.Lock()
// defer proxyHealthMutex.Unlock()
//
// host := getProxyHost(proxyURL)
// if _, exists := proxyHealthMaps[host]; !exists {
// proxyHealthMaps[host] = &ProxyHealth{}
// }
//
// health := proxyHealthMaps[host]
// health.LastCheck = time.Now()
//
// if success {
// health.SuccessCount++
// health.FailCount = 0
// health.ResponseTime = responseTime
// health.IsHealthy = true
// } else {
// health.FailCount++
// health.SuccessCount = 0
// // 连续失败3次标记为不健康
// if health.FailCount >= 3 {
// health.IsHealthy = false
// }
// }
//}
//
//// 获取代理主机名
//func getProxyHost(proxyURL string) string {
// if strings.HasPrefix(proxyURL, "http://") {
// proxyURL = proxyURL[7:]
// }
// // 去除认证信息
// if atIndex := strings.Index(proxyURL, "@"); atIndex != -1 {
// proxyURL = proxyURL[atIndex+1:]
// }
// // 去除端口
// if colonIndex := strings.Index(proxyURL, ":"); colonIndex != -1 {
// proxyURL = proxyURL[:colonIndex]
// }
// return proxyURL
//}
//
//// 随机代理服务器
//func randomServer() string {
// return randomElement(servers)
//}
//
//// 线程安全的随机元素选择
//func randomElement(slice []string) string {
// randMutex.Lock()
// defer randMutex.Unlock()
// return slice[globalRand.Intn(len(slice))]
//}
//
//// 打乱服务器顺序
//func shuffleServers(servers []string) []string {
// randMutex.Lock()
// defer randMutex.Unlock()
//
// shuffled := make([]string, len(servers))
// copy(shuffled, servers)
// globalRand.Shuffle(len(shuffled), func(i, j int) {
// shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
// })
// return shuffled
//}

Binary file not shown.

View File

@ -172,6 +172,10 @@ extern __declspec(dllexport) char* Initialize(char* configJSON);
// //
extern __declspec(dllexport) void FreeCString(char* str); extern __declspec(dllexport) void FreeCString(char* str);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -84,7 +84,9 @@ type BookInfo struct {
Category string `json:"category"` // 分类 Category string `json:"category"` // 分类
BuyCount string `json:"buy_count"` // 买过 BuyCount string `json:"buy_count"` // 买过
SellCount string `json:"sell_count"` // 在卖 SellCount string `json:"sell_count"` // 在卖
Content string `json:"content"` // 内容 Content string `json:"content"` // 内容简介
AuthorIntroduction string `json:"author_introduction"` // 作者简介
Catalogue string `json:"catalogue"` // 目录
Mid int64 `json:"mid"` // 商家id Mid int64 `json:"mid"` // 商家id
ItemId int64 `json:"item_id"` // 商品id ItemId int64 `json:"item_id"` // 商品id
ShopId int64 `json:"shop_id"` // 店铺id ShopId int64 `json:"shop_id"` // 店铺id
@ -1285,6 +1287,7 @@ func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int,
bookInfo.BookName = list.BookName bookInfo.BookName = list.BookName
bookInfo.BookPic = list.ImgUrlEntity.BigImgUrl bookInfo.BookPic = list.ImgUrlEntity.BigImgUrl
bookInfo.ISBN = list.Isbn bookInfo.ISBN = list.Isbn
bookInfo.Mid = list.Mid
// 根据长度安全填充字段 // 根据长度安全填充字段
if isReturnMsg == 0 { if isReturnMsg == 0 {
if len(bookShowInfo) > 0 { if len(bookShowInfo) > 0 {
@ -1322,8 +1325,7 @@ func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int,
} }
// 发送请求 // 发送请求
respSpt, bodySpt, errsSpt := requestSpt.Get(sptUrl). respSpt, bodySpt, errsSpt := requestSpt.Get(sptUrl).
Proxy(proxy). Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
Set("Cookie", token).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"). Set("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", "application/json, text/plain, */*").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"). Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
@ -1425,6 +1427,265 @@ func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int,
return nil, fmt.Errorf("查询失败,没有数据!") return nil, fmt.Errorf("查询失败,没有数据!")
} }
// 爬取孔网 https://item.kongfz.com/book/42291389.html 网址商品信息
func outGetMidDetail(token string, mid string) (*BookInfo, error) {
detailUrl := fmt.Sprintf("https://item.kongfz.com/book/%s.html", mid)
// 创建HTTP客户端设置超时时间
client := &http.Client{
Timeout: 120 * time.Second,
}
// 创建HTTP请求
req, err := http.NewRequest("GET", detailUrl, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
// 设置请求头,模拟浏览器访问
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token))
// 发送请求
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
var bookInfo BookInfo
// 创建goquery文档
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return nil, fmt.Errorf("解析HTML失败: %w", err)
}
// 1. 提取书名
bookInfo.BookName = strings.TrimSpace(doc.Find("title").Text())
// 2. 提取右侧详细信息区域
rightDiv := doc.Find("div.detail-con-right")
if rightDiv.Length() == 0 {
return nil, fmt.Errorf("未找到详细信息区域")
}
// 左侧信息框
leftInfoBox := rightDiv.Find("div.info-con-box-left")
// 提取作者
leftInfoBox.Find(".zuozhe").Each(func(i int, s *goquery.Selection) {
authorText := s.Find(".text-value").Text()
// 清理作者信息
authorText = strings.ReplaceAll(authorText, "\n", "")
authorText = strings.ReplaceAll(authorText, " ", "")
authorText = strings.TrimSpace(authorText)
bookInfo.Author = authorText
})
// 提取出版社
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "出版社") {
bookInfo.Publisher = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取出版时间(转换为时间戳)
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "出版时间") {
timeStr := strings.TrimSpace(s.Find("span.text-value").Text())
// 尝试解析时间
t, err := time.Parse("2006-01", timeStr)
if err != nil {
// 如果月份解析失败,尝试只解析年份
t, err = time.Parse("2006", timeStr)
if err == nil {
bookInfo.PublicationTime = t.Unix()
}
} else {
bookInfo.PublicationTime = t.Unix()
}
}
})
// 提取版次
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "版次") {
bookInfo.Edition = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取ISBN
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "ISBN") {
bookInfo.ISBN = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取定价
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "定价") {
bookInfo.FixPrice = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取装帧
leftInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "装帧") {
bookInfo.BindingLayout = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 右侧信息框
rightInfoBox := rightDiv.Find("div.info-con-box").Last()
// 提取开本
rightInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "开本") {
bookInfo.Format = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取纸张
rightInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "纸张") {
bookInfo.Paper = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取页数
rightInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "页数") {
bookInfo.Pages = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取语种
rightInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "正文语种") {
bookInfo.Languages = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取分类
rightInfoBox.Find(".item").Each(func(i int, s *goquery.Selection) {
key := s.Find(".text-key").Text()
if strings.Contains(key, "分类") {
bookInfo.Category = strings.TrimSpace(s.Find("span.text-value").Text())
}
})
// 提取买过人数
rightInfoBox.Find(".sale-qty").Each(func(i int, s *goquery.Selection) {
text := s.Text()
// 使用正则提取数字
re := regexp.MustCompile(`(\d+)`)
matches := re.FindStringSubmatch(text)
if len(matches) > 1 {
bookInfo.BuyCount = matches[1]
}
})
// 3. 提取底部详细信息(内容简介、作者简介、目录)
bottomDiv := rightDiv.Find("div.detail-con-right-bottom")
// 提取内容简介
bottomDiv.Find(".jianjie").Each(func(i int, s *goquery.Selection) {
h5 := s.Find("h5").Text()
if strings.Contains(h5, "内容简介") {
content := s.Contents().FilterFunction(func(i int, s *goquery.Selection) bool {
return goquery.NodeName(s) == "#text"
}).Text()
// 清理文本
content = strings.ReplaceAll(content, "\n", " ")
content = strings.ReplaceAll(content, "&nbsp;", " ")
content = strings.TrimSpace(content)
bookInfo.Content = content
}
})
// 提取作者简介
bottomDiv.Find(".jianjie").Each(func(i int, s *goquery.Selection) {
h5 := s.Find("h5").Text()
if strings.Contains(h5, "作者简介") {
intro := s.Contents().FilterFunction(func(i int, s *goquery.Selection) bool {
return goquery.NodeName(s) == "#text"
}).Text()
intro = strings.ReplaceAll(intro, "\n", " ")
intro = strings.ReplaceAll(intro, "&nbsp;", " ")
intro = strings.TrimSpace(intro)
bookInfo.AuthorIntroduction = intro
}
})
// 提取目录
bottomDiv.Find(".jianjie").Each(func(i int, s *goquery.Selection) {
h5 := s.Find("h5").Text()
if strings.Contains(h5, "目录") {
catalog := s.Contents().FilterFunction(func(i int, s *goquery.Selection) bool {
return goquery.NodeName(s) == "#text"
}).Text()
catalog = strings.ReplaceAll(catalog, "\n", " ")
catalog = strings.ReplaceAll(catalog, "&nbsp;", " ")
catalog = strings.TrimSpace(catalog)
bookInfo.Catalogue = catalog
}
})
// 4. 提取图书封面图
doc.Find("div.detail-con-left").Find("img").Each(func(i int, s *goquery.Selection) {
if src, exists := s.Attr("src"); exists {
if strings.Contains(src, "kongfz") {
bookInfo.BookPic = src
}
}
})
// 5. 提取商品信息(售价、品相等)
doc.Find("div.store-list-item").First().Each(func(i int, s *goquery.Selection) {
// 提取售价
priceText := s.Find(".item-price").Text()
re := regexp.MustCompile(`¥(\d+\.?\d*)`)
matches := re.FindStringSubmatch(priceText)
if len(matches) > 1 {
bookInfo.SellingPrice = matches[1]
}
// 提取品相
qualityText := s.Find(".item-quality").Text()
if qualityText != "" {
bookInfo.Condition = strings.TrimSpace(qualityText)
}
})
// 6. 尝试提取店铺ID、商品ID等
// 从URL中提取mid
if midInt, err := strconv.ParseInt(mid, 10, 64); err == nil {
bookInfo.Mid = midInt
}
return &bookInfo, nil
}
/* /*
* 获取商品列表通过店铺ID * 获取商品列表通过店铺ID
* param shopId[int] 店铺ID * param shopId[int] 店铺ID
@ -1440,7 +1701,7 @@ func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int,
* Error 无效的排序类型: %s可选值: sort, putDate, newItem, price * Error 无效的排序类型: %s可选值: sort, putDate, newItem, price
* Error 无效的排序类型: %s可选值: desc, asc * Error 无效的排序类型: %s可选值: desc, asc
*/ */
func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage int, sortType string, func outGetGoodsListMsgByShopId(token string, shopId int, proxy string, retPrice int, isImage int, sortType string,
sort string, priceMin float32, priceMax float32, sort string, priceMin float32, priceMax float32,
pageNum, returnNum int) (books []BookInfo, goodsNum string, pNum string, err error) { pageNum, returnNum int) (books []BookInfo, goodsNum string, pNum string, err error) {
// 判断店铺ID // 判断店铺ID
@ -1488,7 +1749,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
return nil, "", "", fmt.Errorf("无效的排序类型: %s可选值: desc, asc", sort) return nil, "", "", fmt.Errorf("无效的排序类型: %s可选值: desc, asc", sort)
} }
} }
var url string var kfzUrl string
var pMin int var pMin int
pMin = 0 pMin = 0
var pMax int var pMax int
@ -1496,35 +1757,111 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
// 判断价格下限设置默认值0 // 判断价格下限设置默认值0
if priceMin == 0 && priceMax == 0 { if priceMin == 0 && priceMax == 0 {
// 调用的url // 调用的url
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%d", kfzUrl = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%d",
shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, pMax) shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, pMax)
} else if priceMin == 0 { } else if priceMin == 0 {
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%.2f", kfzUrl = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%.2f",
shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, priceMax) shopId, isImageStr, returnNum, pageNum, sortType, sort, pMin, priceMax)
} else if priceMax == 0 { } else if priceMax == 0 {
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%d", kfzUrl = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%d",
shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, pMax) shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, pMax)
} else { } else {
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%.2f", kfzUrl = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%.2f_%.2f",
shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, priceMax) shopId, isImageStr, returnNum, pageNum, sortType, sort, priceMin, priceMax)
} }
// 发送请求
response, err := fetchResponse(url, proxy) // 发送请求(移除了重试机制)
if err != nil { var response *http.Response
return nil, "", "", err var errors []error
// 根据是否有代理发送请求
if proxy != "" {
response, _, errors = gorequest.New().
Get(kfzUrl).
Proxy(proxy).
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(120 * time.Second).
End()
} else {
fmt.Println("没使用代理")
response, _, errors = gorequest.New().
Get(kfzUrl).
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").
Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8").
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
Timeout(120 * time.Second).
End()
} }
// 检测请求是否错误
if len(errors) > 0 {
var proxyAuthFailed bool
var timeoutError bool
var connectionError bool
// 分析错误类型
for _, e := range errors {
errStr := e.Error()
if strings.Contains(errStr, "Proxy Authentication Required") {
proxyAuthFailed = true
}
if strings.Contains(errStr, "timeout") || strings.Contains(errStr, "i/o timeout") {
timeoutError = true
}
if strings.Contains(errStr, "connection") || strings.Contains(errStr, "connect") {
connectionError = true
}
}
// 根据错误类型返回相应的错误信息
if proxyAuthFailed {
fmt.Println("代理认证失败")
return nil, "", "", fmt.Errorf("代理认证失败")
}
if timeoutError {
fmt.Println("请求超时,超时网址")
return nil, "", "", fmt.Errorf("请求超时,超时网址:%s", kfzUrl)
}
if connectionError {
fmt.Println("网络连接错误,错误网址:")
return nil, "", "", fmt.Errorf("网络连接错误,错误网址:%s", kfzUrl)
}
fmt.Println("查询请求失败:", errors)
return nil, "", "", fmt.Errorf("查询请求失败: %v失败网址:%s", errors, kfzUrl)
}
// 检查响应是否为空
if response == nil {
fmt.Println("响应为空")
return nil, "", "", fmt.Errorf("响应为空")
}
// 检查HTTP状态码
if response.StatusCode != http.StatusOK {
fmt.Println("HTTP错误")
return nil, "", "", fmt.Errorf("HTTP错误: %s", response.Status)
}
// 读取响应体 // 读取响应体
body, err := io.ReadAll(response.Body) body, err := io.ReadAll(response.Body)
if err != nil { if err != nil {
return nil, "", "", err return nil, "", "", err
} }
// 解析HTML文档 // 解析HTML文档
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body))) doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return nil, "", "", err
}
// 全部商品数量 // 全部商品数量
num := doc.Find("div.crumbs-nav-main.clearfix").Find("span") num := doc.Find("div.crumbs-nav-main.clearfix").Find("span")
if match := regexp.MustCompile(`\d+`).FindString(num.Text()); match != "" { if match := regexp.MustCompile(`\d+`).FindString(num.Text()); match != "" {
goodsNum = match goodsNum = match
} }
// 商品页数 // 商品页数
pg := doc.Find("li.pull-right.page_num").Find("span") pg := doc.Find("li.pull-right.page_num").Find("span")
_, split, found := strings.Cut(strings.TrimSpace(pg.Text()), "/") _, split, found := strings.Cut(strings.TrimSpace(pg.Text()), "/")
@ -1533,6 +1870,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
} else { } else {
log.Printf("未找到页数!") log.Printf("未找到页数!")
} }
// 提取商品信息 // 提取商品信息
infoDiv := doc.Find("div.list-content") infoDiv := doc.Find("div.list-content")
var params ParamsInfo var params ParamsInfo
@ -1568,6 +1906,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
params.Area = "13003000000" params.Area = "13003000000"
dataItem, err := getGoodsListShippingFee(params, proxy) dataItem, err := getGoodsListShippingFee(params, proxy)
if err != nil { if err != nil {
fmt.Println("查询快递费失败")
return nil, "", "", err return nil, "", "", err
} }
// 将快递费信息填充到图书信息中 // 将快递费信息填充到图书信息中
@ -3132,10 +3471,45 @@ func OutGetImageByIsbn(token, proxy, isbn *C.char, isLiveImage C.int, isReturnMs
return C.CString(string(jsonData)) return C.CString(string(jsonData))
} }
// OutGetMidDetail 爬取孔网 https://item.kongfz.com/book/42291389.html 网址商品信息
//
//export OutGetMidDetail
func OutGetMidDetail(token, mid *C.char) *C.char {
goToken := C.GoString(token)
gomid := C.GoString(mid)
bookInfo, err := outGetMidDetail(goToken, gomid)
// 构建API响应
var apiResponse APIResponse
if err != nil {
apiResponse = APIResponse{
Success: false,
Message: err.Error(),
}
} else {
apiResponse = APIResponse{
Success: true,
Data: bookInfo,
}
}
// 转换为JSON字符串
jsonData, marshalErr := json.Marshal(apiResponse)
if marshalErr != nil {
// 如果JSON序列化失败返回错误信息
apiResponse = APIResponse{
Success: false,
Message: fmt.Sprintf("JSON序列化失败: %v", marshalErr),
}
errorJson, _ := json.Marshal(apiResponse)
return C.CString(string(errorJson))
}
return C.CString(string(jsonData))
}
// OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID // OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID
// //
//export OutGetGoodsListMsgByShopId //export OutGetGoodsListMsgByShopId
func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, retPrice C.int, isImage C.int, sortType *C.char, sort *C.char, priceMin C.float, priceMax C.float, pageNum, returnNum C.int) *C.char { func OutGetGoodsListMsgByShopId(token *C.char, shopId C.int, proxy *C.char, retPrice C.int, isImage C.int, sortType *C.char, sort *C.char, priceMin C.float, priceMax C.float, pageNum, returnNum C.int) *C.char {
goToken := C.GoString(token)
goShopId := int(shopId) goShopId := int(shopId)
goProxy := C.GoString(proxy) goProxy := C.GoString(proxy)
goRetPrice := int(retPrice) goRetPrice := int(retPrice)
@ -3146,7 +3520,7 @@ func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, retPrice C.int, isI
goPriceMax := float32(priceMax) goPriceMax := float32(priceMax)
goPageNum := int(pageNum) goPageNum := int(pageNum)
goReturnNum := int(returnNum) goReturnNum := int(returnNum)
books, num, pNum, err := outGetGoodsListMsgByShopId(goShopId, goProxy, goRetPrice, goIsImage, books, num, pNum, err := outGetGoodsListMsgByShopId(goToken, goShopId, goProxy, goRetPrice, goIsImage,
goSortType, goSort, goPriceMin, goPriceMax, goPageNum, goReturnNum) goSortType, goSort, goPriceMin, goPriceMax, goPageNum, goReturnNum)
// 构建统一格式的响应 // 构建统一格式的响应
bookInfo := struct { bookInfo := struct {
@ -3397,6 +3771,18 @@ func FreeCString(str *C.char) {
C.free(unsafe.Pointer(str)) C.free(unsafe.Pointer(str))
} }
// CSV_VERSION 版本号
const (
CSV_VERSION = "v3"
)
// 获取版本信息
//
//export GetVersion
func GetVersion() *C.char {
return C.CString(CSV_VERSION)
}
// 空main函数编译DLL时需要 // 空main函数编译DLL时需要
func main() { func main() {
} }

185
kongfz/kongfz.h Normal file
View File

@ -0,0 +1,185 @@
/* 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 "kongfz.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
// OutLogin 孔网登录
//
extern __declspec(dllexport) char* OutLogin(char* username, char* password);
// OutGetUserMsg 获取孔网用户信息
//
extern __declspec(dllexport) char* OutGetUserMsg(char* token);
// OutGetGoodsTplMsg 获取商品模版--已登的店铺
//
extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* proxy, char* itemId);
// OutGetGoodsListMsgFromSelfShop 获取商品列表-已登的店铺
//
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);
// OutAddGoods 新增商品-已登的店铺
//
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 删除商品-已登的店铺
//
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
// OutGetImageFilterShopId 获取孔网商品图片和信息(官图和拍图)-带有店铺过滤
//
extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* proxy, char* isbn, int shopId, int isLiveImage, int isReturnMsg);
// OutGetImageByIsbn 获取孔网商品图片和信息(官图和拍图)
//
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* proxy, char* isbn, int isLiveImage, int isReturnMsg);
// OutGetMidDetail 爬取孔网 https://item.kongfz.com/book/42291389.html 网址商品信息
//
extern __declspec(dllexport) char* OutGetMidDetail(char* token, char* mid);
// OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID
//
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(char* token, int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
// OutGetGoodsMsgByDetailUrl 获取商品信息通过商品详情链接
//
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// OutGetTopGoodsListMsg 获取销量榜商品列表
//
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
// KongfzDeliveryMethodList 获取配送方式列表
//
extern __declspec(dllexport) char* KongfzDeliveryMethodList(int appId, char* appSecret, char* accessToken);
// KongfzOrderDeliver 订单发货
//
extern __declspec(dllexport) char* KongfzOrderDeliver(int appId, char* appSecret, char* accessToken, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
// KongfzOrderSynchronization 孔网订单同步
//
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 初始化配置
//
extern __declspec(dllexport) char* Initialize(char* configJSON);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,109 +1,107 @@
package main package main
import "fmt" //func main() {
// //upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
func main() { // // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb",
//upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8", // // "D:/work/image/9787511220660.jpg",
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", // // "D:\\work\\project\\kfzgw-info\\kongfz")
// "D:/work/image/9787511220660.jpg", // //if err != nil {
// "D:\\work\\project\\kfzgw-info\\kongfz") // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(upload)
//} //
//fmt.Println(upload) // //var q OrderQueryParams
// //q.UserType = "buyer"
//var q OrderQueryParams // //marshal, err := json.Marshal(q)
//q.UserType = "buyer" // //if err != nil {
//marshal, err := json.Marshal(q) // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(string(marshal))
//} // //
//fmt.Println(string(marshal)) // //list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
// // // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal))
//list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8", // //if err != nil {
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal)) // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(list)
//} // //
//fmt.Println(list) // //key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "")
// // //if err != nil {
//key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "") // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(key)
//} // //
//fmt.Println(key) // //upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "",
// // // "D:/work/image/9787115460622.jpg")
//upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "", // //if err != nil {
// "D:/work/image/9787115460622.jpg") // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(upload)
//} //
//fmt.Println(upload) // //uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "",
// // "D:/work/image/9787511220660.jpg")
//uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "", // //if err != nil {
// "D:/work/image/9787511220660.jpg") // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(uploadd)
//} //
//fmt.Println(uploadd) // appId := 576
// appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8"
appId := 576 // accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92"
appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8" // shippingComName := "韵达快递"
accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92" // orderId := 275723461
shippingComName := "韵达快递" // shippingId := ""
orderId := 275723461 // shippingCom := ""
shippingId := "" // shipmentNum := "312944151258647"
shippingCom := "" // userDefined := ""
shipmentNum := "312944151258647" // moreShipmentNum := ""
userDefined := "" //
moreShipmentNum := "" // synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
// if err != nil {
synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum) // fmt.Println(err)
if err != nil { // }
fmt.Println(err) // fmt.Println(synchronization)
} //
fmt.Println(synchronization) // //data := ItemInfo{
// // ItemName: "至关重要的关系",
//data := ItemInfo{ // // ISBN: "9787550213869",
// ItemName: "至关重要的关系", // // Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译",
// ISBN: "9787550213869", // // Press: "北京联合出版公司",
// Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译", // // YearsGroup: "1",
// Press: "北京联合出版公司", // // PubDate: "2013-04",
// YearsGroup: "1", // // PubDateYear: "2013",
// PubDate: "2013-04", // // PubDateMonth: "4",
// PubDateYear: "2013", // // Edition: "1",
// PubDateMonth: "4", // // Binding: "2", // 2可能表示平装
// Edition: "1", // // Quality: "95",
// Binding: "2", // 2可能表示平装 // // Price: "1500",
// Quality: "95", // // Number: "2",
// Price: "1500", // // ItemSn: "a1",
// Number: "2", // // MouldId: "909963",
// ItemSn: "a1", // // DeliverTime: "48h",
// MouldId: "909963", // // IsDeliverTimeDefault: "48h",
// DeliverTime: "48h", // // CatId: "43000000000000000",
// IsDeliverTimeDefault: "48h", // // PageType: "add",
// CatId: "43000000000000000", // // OldCatId: "0000000000000000000",
// PageType: "add", // // Tpl: "13",
// OldCatId: "0000000000000000000", // // BearShipping: "buyer",
// Tpl: "13", // // WeightPiece: "1",
// BearShipping: "buyer", // // IsOnSale: "1",
// WeightPiece: "1", // // Weight: "0.5",
// IsOnSale: "1", // // IsUseMould: "1",
// Weight: "0.5", // // DeliverTimeGroup: "1",
// IsUseMould: "1", // //}
// DeliverTimeGroup: "1", // //marshal, err := json.Marshal(data)
//} // //if err != nil {
//marshal, err := json.Marshal(data) // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //
//} // //file, err := outAddGoodsAndFile("7feb7d192911b5440a4c83d81decd568e3cb4833", "", "D:/work/image/9787115460622.jpg", string(marshal))
// // //if err != nil {
//file, err := outAddGoodsAndFile("7feb7d192911b5440a4c83d81decd568e3cb4833", "", "D:/work/image/9787115460622.jpg", string(marshal)) // // fmt.Println(err)
//if err != nil { // //}
// fmt.Println(err) // //fmt.Println(string(file))
//} //}
//fmt.Println(string(file))
}
type ItemInfo struct { type ItemInfo struct {
ItemName string `json:"itemName"` ItemName string `json:"itemName"`

71
listener/listener.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
// 定义路由
http.HandleFunc("/", homeHandler)
http.HandleFunc("/api/data", dataHandler)
http.HandleFunc("/ws", websocketHandler)
// 中间件示例
wrappedHandler := loggingMiddleware(http.DefaultServeMux)
// 配置服务器
server := &http.Server{
Addr: ":8080",
Handler: wrappedHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
fmt.Println("HTTP Server listening on :8080")
// 启动服务器
if err := server.ListenAndServe(); err != nil {
log.Fatal("Server error:", err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
fmt.Fprintf(w, "Welcome to HTTP Server!\n")
}
func dataHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintf(w, `{"status": "ok", "message": "GET request received"}`)
case "POST":
fmt.Fprintf(w, `{"status": "ok", "message": "POST request received"}`)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func websocketHandler(w http.ResponseWriter, r *http.Request) {
// WebSocket处理逻辑
fmt.Fprintf(w, "WebSocket endpoint")
}
// 中间件:记录请求日志
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 调用下一个处理器
next.ServeHTTP(w, r)
// 记录日志
log.Printf("%s %s %s %v", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start))
})
}

BIN
logger/dll/logger.dll Normal file

Binary file not shown.

107
logger/dll/logger.h Normal file
View File

@ -0,0 +1,107 @@
/* 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 "logger.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* CreateLogger(char* configJSON);
extern __declspec(dllexport) char* CreateContextWithTaskType(char* loggerHandle, char* taskType);
extern __declspec(dllexport) void LogInfo(char* ctxHandle, char* message);
extern __declspec(dllexport) void LogError(char* ctxHandle, char* message);
extern __declspec(dllexport) void LogWarning(char* ctxHandle, char* message);
extern __declspec(dllexport) void LogSuccess(char* ctxHandle, char* message);
extern __declspec(dllexport) void FreeString(char* str);
extern __declspec(dllexport) char* CloseAllLoggers(void);
extern __declspec(dllexport) char* GetLogs(char* loggerHandle, char* configJSON);
extern __declspec(dllexport) char* GetLogFiles(char* loggerHandle);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus
}
#endif

1053
logger/logger.go Normal file

File diff suppressed because it is too large Load Diff

840
logger/loggerDll.go Normal file
View File

@ -0,0 +1,840 @@
package main
import "C"
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
// LoggerWrapper 封装DLL调用
type LoggerWrapper struct {
dllHandle windows.Handle
createLogger uintptr
createContext uintptr
logInfo uintptr
logError uintptr
logWarning uintptr
logSuccess uintptr
freeString uintptr
closeAllLoggers uintptr
// cui
getLogs uintptr
getLogFiles uintptr
}
// 全局变量来跟踪创建的logger数量
var (
createdLoggers []string
loggerCount int
)
func NewLoggerWrapper(dllPath string) (*LoggerWrapper, error) {
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
return nil, fmt.Errorf("DLL文件不存在: %s", dllPath)
}
dllHandle, err := windows.LoadLibrary(dllPath)
if err != nil {
return nil, fmt.Errorf("加载DLL失败: %v", err)
}
wrapper := &LoggerWrapper{
dllHandle: dllHandle,
}
if err := wrapper.loadFunctions(); err != nil {
windows.FreeLibrary(dllHandle)
return nil, err
}
return wrapper, nil
}
func (lw *LoggerWrapper) loadFunctions() error {
var err error
lw.createLogger, err = windows.GetProcAddress(lw.dllHandle, "CreateLogger")
if err != nil {
return err
}
lw.createContext, err = windows.GetProcAddress(lw.dllHandle, "CreateContextWithTaskType")
if err != nil {
return err
}
lw.logInfo, err = windows.GetProcAddress(lw.dllHandle, "LogInfo")
if err != nil {
return err
}
lw.logError, err = windows.GetProcAddress(lw.dllHandle, "LogError")
if err != nil {
return err
}
lw.logWarning, err = windows.GetProcAddress(lw.dllHandle, "LogWarning")
if err != nil {
return err
}
lw.logSuccess, err = windows.GetProcAddress(lw.dllHandle, "LogSuccess")
if err != nil {
return err
}
lw.freeString, err = windows.GetProcAddress(lw.dllHandle, "FreeString")
if err != nil {
return err
}
lw.closeAllLoggers, err = windows.GetProcAddress(lw.dllHandle, "CloseAllLoggers")
if err != nil {
return err
}
// cui
lw.getLogs, err = windows.GetProcAddress(lw.dllHandle, "GetLogs")
if err != nil {
return err
}
// cui
lw.getLogFiles, err = windows.GetProcAddress(lw.dllHandle, "GetLogFiles")
if err != nil {
return err
}
return nil
}
func (lw *LoggerWrapper) CreateLogger(configJSON string) (string, error) {
configCStr, err := syscall.BytePtrFromString(configJSON)
if err != nil {
return "", err
}
resultPtr, _, err := syscall.SyscallN(lw.createLogger, uintptr(unsafe.Pointer(configCStr)))
if err != syscall.Errno(0) {
return "", fmt.Errorf("CreateLogger调用失败: %v", err)
}
if resultPtr == 0 {
return "", fmt.Errorf("CreateLogger返回空指针")
}
result := readNullTerminatedString(resultPtr)
lw.FreeStringByPtr(resultPtr)
// 记录创建的logger
createdLoggers = append(createdLoggers, result)
loggerCount++
return result, nil
}
func (lw *LoggerWrapper) CreateContextWithTaskType(loggerHandle, taskType string) (string, error) {
loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle)
if err != nil {
return "", err
}
taskTypeCStr, err := syscall.BytePtrFromString(taskType)
if err != nil {
return "", err
}
resultPtr, _, err := syscall.SyscallN(
lw.createContext,
uintptr(unsafe.Pointer(loggerHandleCStr)),
uintptr(unsafe.Pointer(taskTypeCStr)),
)
if err != syscall.Errno(0) {
return "", fmt.Errorf("CreateContextWithTaskType调用失败: %v", err)
}
if resultPtr == 0 {
return "", fmt.Errorf("CreateContextWithTaskType返回空指针")
}
result := readNullTerminatedString(resultPtr)
lw.FreeStringByPtr(resultPtr)
return result, nil
}
func (lw *LoggerWrapper) LogInfo(ctxHandle, message string) error {
return lw.logMessage(ctxHandle, message, lw.logInfo)
}
func (lw *LoggerWrapper) LogError(ctxHandle, message string) error {
return lw.logMessage(ctxHandle, message, lw.logError)
}
func (lw *LoggerWrapper) LogWarning(ctxHandle, message string) error {
return lw.logMessage(ctxHandle, message, lw.logWarning)
}
func (lw *LoggerWrapper) LogSuccess(ctxHandle, message string) error {
return lw.logMessage(ctxHandle, message, lw.logSuccess)
}
func (lw *LoggerWrapper) logMessage(ctxHandle, message string, logFunc uintptr) error {
ctxHandleCStr, err := syscall.BytePtrFromString(ctxHandle)
if err != nil {
return err
}
messageCStr, err := syscall.BytePtrFromString(message)
if err != nil {
return err
}
_, _, err = syscall.SyscallN(
logFunc,
uintptr(unsafe.Pointer(ctxHandleCStr)),
uintptr(unsafe.Pointer(messageCStr)),
)
if err != syscall.Errno(0) {
return fmt.Errorf("日志记录失败: %v", err)
}
return nil
}
// cui
func (lw *LoggerWrapper) GetLogs(loggerHandle, configJSON string) (string, error) {
loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle)
if err != nil {
return "", err
}
configCStr, err := syscall.BytePtrFromString(configJSON)
if err != nil {
return "", err
}
resultPtr, _, err := syscall.SyscallN(
lw.getLogs,
uintptr(unsafe.Pointer(loggerHandleCStr)),
uintptr(unsafe.Pointer(configCStr)),
)
if err != syscall.Errno(0) {
return "", fmt.Errorf("GetLogs调用失败: %v", err)
}
if resultPtr == 0 {
return "", fmt.Errorf("GetLogs返回空指针")
}
result := ptrToString(resultPtr)
lw.FreeStringByPtr(resultPtr)
return result, nil
}
// cui
func (lw *LoggerWrapper) GetLogFiles(loggerHandle string) (string, error) {
loggerHandleCStr, err := syscall.BytePtrFromString(loggerHandle)
if err != nil {
return "", err
}
resultPtr, _, err := syscall.SyscallN(
lw.getLogFiles,
uintptr(unsafe.Pointer(loggerHandleCStr)),
)
if err != syscall.Errno(0) {
return "", fmt.Errorf("GetLogFiles调用失败: %v", err)
}
if resultPtr == 0 {
return "", fmt.Errorf("GetLogFiles返回空指针")
}
result := ptrToString(resultPtr)
lw.FreeStringByPtr(resultPtr)
return result, nil
}
func ptrToString(ptr uintptr) string {
if ptr == 0 {
return ""
}
// 先找到字符串长度
var length int
for {
b := *(*byte)(unsafe.Pointer(ptr + uintptr(length)))
if b == 0 {
break
}
length++
if length > 64*1024 { // 64KB限制
break
}
}
if length == 0 {
return ""
}
// 创建适当大小的切片
data := make([]byte, length)
for i := 0; i < length; i++ {
data[i] = *(*byte)(unsafe.Pointer(ptr + uintptr(i)))
}
return string(data)
}
func (lw *LoggerWrapper) FreeStringByPtr(ptr uintptr) {
if ptr != 0 {
syscall.SyscallN(lw.freeString, ptr)
}
}
func (lw *LoggerWrapper) CloseAllLoggers() (string, error) {
resultPtr, _, err := syscall.SyscallN(lw.closeAllLoggers)
if err != syscall.Errno(0) {
return "", fmt.Errorf("CloseAllLoggers调用失败: %v", err)
}
if resultPtr == 0 {
return "", fmt.Errorf("CloseAllLoggers返回空指针")
}
result := readNullTerminatedString(resultPtr)
lw.FreeStringByPtr(resultPtr)
return result, nil
}
func readNullTerminatedString(ptr uintptr) string {
if ptr == 0 {
return ""
}
var bytes []byte
for i := 0; i < 256; i++ {
b := *(*byte)(unsafe.Pointer(ptr + uintptr(i)))
if b == 0 {
break
}
bytes = append(bytes, b)
}
return string(bytes)
}
func (lw *LoggerWrapper) Close() error {
return windows.FreeLibrary(lw.dllHandle)
}
func checkError(result string) error {
if len(result) >= 6 && result[:6] == "错误:" {
return fmt.Errorf(result)
}
return nil
}
// 监控内存
func monitorResources() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("📊 内存使用: Alloc=%.2fMB, TotalAlloc=%.2fMB, Sys=%.2fMB, NumGC=%d, Goroutines=%d\n",
float64(m.Alloc)/1024/1024,
float64(m.TotalAlloc)/1024/1024,
float64(m.Sys)/1024/1024,
m.NumGC,
runtime.NumGoroutine())
}
// 测试日志读取功能 cui
func testLogReadingFunctions(wrapper *LoggerWrapper) {
fmt.Println("\n📖 ========== 开始日志读取功能测试 ==========")
// 创建测试logger
config := `{
"log_dir": "./test_logs_reading",
"level": 1,
"split_type": 1,
"max_size": 1048576,
"max_count": 5,
"enable_caller": true,
"default_task_type": "reading_test"
}`
loggerHandle, err := wrapper.CreateLogger(config)
if err != nil {
log.Printf("创建测试logger失败: %v", err)
return
}
fmt.Printf("✅ 创建测试logger: %s\n", loggerHandle)
// 创建不同任务类型的上下文
taskTypes := []string{"task_a", "task_b", "task_c"}
ctxHandles := make(map[string]string)
for _, taskType := range taskTypes {
ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, taskType)
if err != nil {
log.Printf("创建上下文失败: %v", err)
continue
}
ctxHandles[taskType] = ctxHandle
fmt.Printf("✅ 创建上下文: %s -> %s\n", taskType, ctxHandle)
}
// 记录测试日志
fmt.Println("\n📝 记录测试日志数据...")
messages := []struct {
level string
message string
task string
}{
{"INFO", "这是INFO级别的测试消息", "task_a"},
{"ERROR", "这是ERROR级别的错误消息", "task_b"},
{"WARNING", "这是WARNING级别的警告消息", "task_c"},
{"SUCCESS", "这是SUCCESS级别的成功消息", "task_a"},
{"INFO", "另一条INFO消息", "task_b"},
}
for i, msg := range messages {
var err error
switch msg.level {
case "INFO":
err = wrapper.LogInfo(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i))
case "ERROR":
err = wrapper.LogError(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i))
case "WARNING":
err = wrapper.LogWarning(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i))
case "SUCCESS":
err = wrapper.LogSuccess(ctxHandles[msg.task], fmt.Sprintf("%s - 序号: %d", msg.message, i))
}
if err != nil {
log.Printf("记录日志失败: %v", err)
} else {
fmt.Printf("✅ 记录%s日志: %s\n", msg.level, msg.message)
}
}
// 等待一下确保日志写入完成
time.Sleep(100 * time.Millisecond)
// 测试1: 获取日志文件列表
fmt.Println("\n📁 测试1: 获取日志文件列表")
filesResult, err := wrapper.GetLogFiles(loggerHandle)
if err != nil {
log.Printf("获取日志文件列表失败: %v", err)
} else {
fmt.Printf("✅ 获取日志文件列表成功:\n%s\n", formatJSON(filesResult))
}
// 测试2: 获取所有日志
fmt.Println("\n📄 测试2: 获取所有日志")
allLogsConfig := `{
"level": -1,
"max_entries": 50
}`
allLogsResult, err := wrapper.GetLogs(loggerHandle, allLogsConfig)
if err != nil {
log.Printf("获取所有日志失败: %v", err)
} else {
fmt.Printf("✅ 获取所有日志成功:\n%s\n", formatJSON(allLogsResult))
}
// 测试3: 按级别过滤日志
fmt.Println("\n🔍 测试3: 按级别过滤日志(INFO)")
infoLogsConfig := `{
"level": 1,
"max_entries": 10
}`
infoLogsResult, err := wrapper.GetLogs(loggerHandle, infoLogsConfig)
if err != nil {
log.Printf("获取INFO日志失败: %v", err)
} else {
fmt.Printf("✅ 获取INFO日志成功:\n%s\n", formatJSON(infoLogsResult))
}
// 测试4: 按任务类型过滤日志
fmt.Println("\n🔍 测试4: 按任务类型过滤日志(task_a)")
taskALogsConfig := `{
"level": -1,
"task_type": "task_a",
"max_entries": 10
}`
taskALogsResult, err := wrapper.GetLogs(loggerHandle, taskALogsConfig)
if err != nil {
log.Printf("获取task_a日志失败: %v", err)
} else {
fmt.Printf("✅ 获取task_a日志成功:\n%s\n", formatJSON(taskALogsResult))
}
// 测试5: 限制返回条目数
fmt.Println("\n🔍 测试5: 限制返回条目数(2条)")
limitedLogsConfig := `{
"level": -1,
"max_entries": 2
}`
limitedLogsResult, err := wrapper.GetLogs(loggerHandle, limitedLogsConfig)
if err != nil {
log.Printf("获取限制条目日志失败: %v", err)
} else {
fmt.Printf("✅ 获取限制条目日志成功:\n%s\n", formatJSON(limitedLogsResult))
}
// 测试6: 组合条件查询
fmt.Println("\n🔍 测试6: 组合条件查询(ERROR级别 + task_b)")
combinedConfig := `{
"level": 3,
"task_type": "task_b",
"max_entries": 10
}`
combinedResult, err := wrapper.GetLogs(loggerHandle, combinedConfig)
if err != nil {
log.Printf("组合条件查询失败: %v", err)
} else {
fmt.Printf("✅ 组合条件查询成功:\n%s\n", formatJSON(combinedResult))
}
// 测试7: 组合条件查询
fmt.Println("\n🔍 测试7: 组合条件查询(task_b)")
combinedTimeConfig := `{
"level": -1,
"task_type": "task_b",
"start_time": "2025-11-25 15:03:31",
"end_time": "2025-11-25 15:04:31",
"max_entries": 10
}`
combinedTimeResult, err := wrapper.GetLogs(loggerHandle, combinedTimeConfig)
if err != nil {
log.Printf("组合条件查询失败: %v", err)
} else {
fmt.Printf("✅ 组合条件查询成功:\n%s\n", formatJSON(combinedTimeResult))
}
// 解析并分析结果
fmt.Println("\n📊 日志读取功能测试分析:")
analyzeLogReadingResults(allLogsResult)
fmt.Println("✅ 日志读取功能测试完成")
}
// 分析日志读取结果 cui
func analyzeLogReadingResults(logsResult string) {
var result map[string]interface{}
if err := json.Unmarshal([]byte(logsResult), &result); err != nil {
log.Printf("解析日志结果失败: %v", err)
return
}
if errorMsg, exists := result["error"]; exists {
fmt.Printf("❌ 错误: %v\n", errorMsg)
return
}
count, _ := result["count"].(float64)
entries, _ := result["entries"].([]interface{})
fmt.Printf(" 总日志条目数: %.0f\n", count)
// 统计各级别日志数量
levelCount := make(map[string]int)
taskCount := make(map[string]int)
for _, entry := range entries {
if entryMap, ok := entry.(map[string]interface{}); ok {
level, _ := entryMap["level"].(string)
taskType, _ := entryMap["task_type"].(string)
levelCount[level]++
taskCount[taskType]++
}
}
fmt.Println(" 日志级别分布:")
for level, count := range levelCount {
fmt.Printf(" - %s: %d条\n", level, count)
}
fmt.Println(" 任务类型分布:")
for taskType, count := range taskCount {
fmt.Printf(" - %s: %d条\n", taskType, count)
}
}
// 格式化JSON输出 cui
func formatJSON(jsonStr string) string {
var result map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
return jsonStr // 返回原始字符串如果解析失败
}
formatted, err := json.MarshalIndent(result, " ", " ")
if err != nil {
return jsonStr
}
return string(formatted)
}
// 测试资源回收的核心函数
func testResourceCleanup(wrapper *LoggerWrapper) {
fmt.Println("🧪 开始资源回收验证测试")
// 测试配置
config := `{
"log_dir": "./resource_cleanup_test",
"level": 1,
"split_type": 1,
"max_size": 1048576,
"max_count": 3,
"enable_caller": false,
"default_task_type": "cleanup_test"
}`
// 阶段1: 创建多个logger并记录初始状态
fmt.Println("\n📝 阶段1: 创建测试logger")
activeLoggers := make([]string, 0)
idleLoggers := make([]string, 0)
// 创建6个logger3个活跃3个闲置
for i := 0; i < 6; i++ {
loggerHandle, err := wrapper.CreateLogger(config)
if err != nil {
log.Printf("创建logger %d 失败: %v", i, err)
continue
}
if i < 3 {
activeLoggers = append(activeLoggers, loggerHandle)
fmt.Printf("✅ 创建活跃logger %d: %s\n", i, loggerHandle)
} else {
idleLoggers = append(idleLoggers, loggerHandle)
fmt.Printf("✅ 创建闲置logger %d: %s\n", i, loggerHandle)
}
}
fmt.Printf("\n📊 创建统计: 活跃logger=%d, 闲置logger=%d, 总计=%d\n",
len(activeLoggers), len(idleLoggers), len(activeLoggers)+len(idleLoggers))
// 阶段2: 使用活跃logger让闲置logger保持不使用状态
fmt.Println("\n📝 阶段2: 使用活跃logger")
for i, loggerHandle := range activeLoggers {
ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, fmt.Sprintf("active_task_%d", i))
if err != nil {
log.Printf("创建上下文失败: %v", err)
continue
}
// 记录多条日志确保活跃状态
for j := 0; j < 5; j++ {
message := fmt.Sprintf("活跃logger %d - 消息 %d - 时间: %s", i, j, time.Now().Format("15:04:05"))
err = wrapper.LogInfo(ctxHandle, message)
if err != nil {
log.Printf("记录日志失败: %v", err)
}
}
fmt.Printf("✅ 活跃logger %d 已使用: %s\n", i, loggerHandle)
}
// 记录当前资源状态
fmt.Println("\n📊 资源使用前状态:")
monitorResources()
// 阶段3: 模拟时间流逝并验证资源回收
fmt.Println("\n⏰ 阶段3: 模拟时间流逝等待资源回收")
fmt.Println(" 自动清理机制: 每30分钟运行一次清理2小时未使用的资源")
fmt.Println(" 由于测试时间限制,我们将观察资源使用模式...")
// 多次采样资源使用情况来观察趋势
fmt.Println("\n📈 资源使用趋势观察:")
initialMemory := getMemoryUsage()
for i := 0; i < 3; i++ {
fmt.Printf("\n 采样 %d:\n", i+1)
monitorResources()
// 继续使用活跃logger以保持其活跃状态
for _, loggerHandle := range activeLoggers {
ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "keep_alive")
if err == nil {
wrapper.LogInfo(ctxHandle, fmt.Sprintf("保持活跃 - 采样 %d", i+1))
}
}
time.Sleep(2 * time.Second)
}
finalMemory := getMemoryUsage()
memoryChange := finalMemory - initialMemory
fmt.Printf("\n📊 内存变化: %.2fMB\n", memoryChange)
// 阶段4: 验证闲置logger的状态
fmt.Println("\n🔍 阶段4: 验证闲置logger状态")
stillWorkingCount := 0
for i, loggerHandle := range idleLoggers {
ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "test_after_idle")
if err != nil {
fmt.Printf("❌ 闲置logger %d 可能已被回收: %s -> %v\n", i, loggerHandle, err)
} else {
// 尝试记录日志
err = wrapper.LogInfo(ctxHandle, "测试闲置logger是否仍然工作")
if err != nil {
fmt.Printf("❌ 闲置logger %d 记录日志失败: %s -> %v\n", i, loggerHandle, err)
} else {
fmt.Printf("✅ 闲置logger %d 仍然工作: %s\n", i, loggerHandle)
stillWorkingCount++
}
}
}
// 阶段5: 验证活跃logger的状态
fmt.Println("\n🔍 阶段5: 验证活跃logger状态")
activeWorkingCount := 0
for i, loggerHandle := range activeLoggers {
ctxHandle, err := wrapper.CreateContextWithTaskType(loggerHandle, "test_active")
if err != nil {
fmt.Printf("❌ 活跃logger %d 异常: %s -> %v\n", i, loggerHandle, err)
} else {
err = wrapper.LogInfo(ctxHandle, "测试活跃logger是否正常工作")
if err != nil {
fmt.Printf("❌ 活跃logger %d 记录日志失败: %s -> %v\n", i, loggerHandle, err)
} else {
fmt.Printf("✅ 活跃logger %d 正常工作: %s\n", i, loggerHandle)
activeWorkingCount++
}
}
}
// 阶段6: 分析测试结果
fmt.Println("\n📋 阶段6: 资源回收测试结果分析")
fmt.Printf(" 初始创建: %d 个logger\n", len(activeLoggers)+len(idleLoggers))
fmt.Printf(" 活跃logger正常: %d/%d\n", activeWorkingCount, len(activeLoggers))
fmt.Printf(" 闲置logger仍然工作: %d/%d\n", stillWorkingCount, len(idleLoggers))
fmt.Printf(" 内存变化: %.2fMB\n", memoryChange)
// 资源回收效果评估
if stillWorkingCount < len(idleLoggers) {
fmt.Println("🎯 资源回收效果: ✅ 检测到可能的资源回收")
fmt.Println(" 说明: 部分闲置logger无法使用可能已被自动清理机制回收")
} else {
fmt.Println("🎯 资源回收效果: ⚠️ 未检测到明显的资源回收")
fmt.Println(" 说明: 所有logger仍然可用可能因为:")
fmt.Println(" - 测试时间不足以触发自动清理")
fmt.Println(" - 自动清理机制配置时间较长")
fmt.Println(" - 所有logger仍被认定为活跃状态")
}
// 阶段7: 显式清理
fmt.Println("\n🧹 阶段7: 显式清理所有资源")
result, err := wrapper.CloseAllLoggers()
if err != nil {
log.Printf("关闭所有logger失败: %v", err)
} else {
fmt.Printf("✅ %s\n", result)
}
// 最终资源状态
fmt.Println("\n📊 最终资源状态:")
monitorResources()
fmt.Println("🧪 资源回收验证测试完成")
}
func getMemoryUsage() float64 {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return float64(m.Alloc) / 1024 / 1024
}
// 文件系统资源检查
func checkFileSystemResources() {
fmt.Println("\n📁 文件系统资源检查:")
// 检查日志目录
dirs := []string{"./resource_cleanup_test", "./test_logs"}
for _, dir := range dirs {
if _, err := os.Stat(dir); !os.IsNotExist(err) {
files, _ := filepath.Glob(filepath.Join(dir, "*", "*.log"))
fmt.Printf(" 目录 %s: %d 个日志文件\n", dir, len(files))
// 显示文件详情
for _, file := range files {
info, err := os.Stat(file)
if err == nil {
fmt.Printf(" - %s (大小: %.2fKB, 修改时间: %s)\n",
filepath.Base(file),
float64(info.Size())/1024,
info.ModTime().Format("15:04:05"))
}
}
} else {
fmt.Printf(" 目录 %s: 不存在\n", dir)
}
}
}
func main() {
config := `{
"log_dir": "./test_logs_reading",
"level": 1,
"split_type": 1,
"max_size": 1048576,
"max_count": 5,
"enable_caller": true,
"default_task_type": "reading_test"
}`
var configs Config
err2 := json.Unmarshal([]byte(config), &configs)
if err2 != nil {
fmt.Println(err2)
}
logger, err := NewLogger(configs)
if err != nil {
fmt.Println(err)
}
fmt.Println(logger)
fmt.Println("🚀 开始资源回收验证测试")
// 确定DLL路径
dllPath := "logger.dll"
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
log.Fatalf("DLL文件不存在: %s", dllPath)
}
// 创建包装器
wrapper, err := NewLoggerWrapper(dllPath)
if err != nil {
log.Fatalf("创建日志包装器失败: %v", err)
}
defer wrapper.Close()
fmt.Println("✅ DLL加载成功")
// 初始资源状态
fmt.Println("\n📈 初始资源状态:")
monitorResources()
// 运行日志读取功能测试
testLogReadingFunctions(wrapper)
// 运行资源回收测试
testResourceCleanup(wrapper)
// 检查文件系统资源
checkFileSystemResources()
fmt.Println("\n🎉 资源回收验证完成!")
fmt.Println("\n💡 测试说明:")
fmt.Println(" - 本测试创建了活跃和闲置的logger来模拟真实使用场景")
fmt.Println(" - 通过观察闲置logger的可用性来验证自动回收机制")
fmt.Println(" - 内存使用趋势可以帮助识别资源泄漏")
fmt.Println(" - 实际自动清理需要较长时间(2小时未使用)")
}

1863
main.go

File diff suppressed because it is too large Load Diff

156
md/config.md Normal file
View File

@ -0,0 +1,156 @@
# config.dll 使用教程
## 1.创建DLL工具实例
### 加载DLL文件
```gotemplate
// ConfigDLL 配置文件读取DLL结构
type ConfigDLL struct {
dll *syscall.DLL
readConfigFile *syscall.Proc // 读取配置文件
getVersion *syscall.Proc // 获取版本信息
freeCString *syscall.Proc // 释放C字符串
}
// 初始化ConfigDLL
func InitConfigDLL() (*ConfigDLL, error) {
dllPath := filepath.Join("dll", "config.dll")
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
return nil, fmt.Errorf("config DLL 不存在: %s", dllPath)
}
if dll, err := syscall.LoadDLL(dllPath); err != nil {
return nil, fmt.Errorf("加载config DLL 失败: %s", err)
} else {
return &ConfigDLL{
dll: dll,
readConfigFile: dll.MustFindProc("ReadConfigFile"),
getVersion: dll.MustFindProc("GetVersion"),
freeCString: dll.MustFindProc("FreeCString"),
}, nil
}
}
dll, err := InitConfigDLL()
```
#### 获取C字符串
```gotemplate
// cStr 获取C字符串
func (m *ConfigDLL) 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
// 读取配置文件
func (m *ConfigDLL) ReadConfigFile(filePath, fileName string) (string, error) {
proc, err := m.dll.FindProc("ReadConfigFile")
if err != nil {
return "", fmt.Errorf("找不到函数 ReadConfigFile: %v", err)
}
filePathPtr, _ := syscall.BytePtrFromString(filePath)
fileNamePtr, _ := syscall.BytePtrFromString(fileName)
resultPtr, _, _ := proc.Call(
uintptr(unsafe.Pointer(filePathPtr)),
uintptr(unsafe.Pointer(fileNamePtr)),
)
result := m.cStr(resultPtr)
return result, nil
}
```
# 接口详情
## 读取配置文件接口--ReadConfigFile
### 请求信息
```gotemplate
dll.ReadConfigFile(filePath, fileName)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| filePath | string | 是 | 文件路径可以是本地路径或HTTP/HTTPS URL |
| fileName | string | 是 | 配置文件名 |
#### 功能说明
```text
此接口用于读取多种格式的配置文件,支持以下特性:
1.支持的文件格式:
JSON (.json)
YAML (.yaml, .yml)
INI (.ini, .conf)
2.支持的数据源:
本地文件路径
HTTP/HTTPS URL
3.配置解析:
JSON/YAML文件解析为Map结构
INI文件支持多节section解析
DEFAULT节的内容会合并到顶层
```
### 响应示例
```text
JSON字符串
{
"database": {
"host": "localhost",
"port": 3306,
"username": "admin"
},
"server": {
"port": 8080,
"timeout": 30
}
}
```
### 错误响应示例
```text
"error": "JSON解析错误: invalid character '}' looking for beginning of object key string"
```
## 释放C字符串内存--FreeCString
### 请求信息
```gotemplate
dll.FreeCString(str)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|------------|
| str | string | 是 | 需要释放的C字符串 |
### 响应示例
```text
```
## 获取版本信息接口--GetVersion
### 请求信息
```gotemplate
dll.GetVersion()
```
### 请求参数
### 响应示例
```text
v3
```
### 错误响应示例
```text
{
"error": "找不到函数 GetVersion"
}
```

739
md/expressDeliveryOrder.md Normal file
View File

@ -0,0 +1,739 @@
# expressDeliveryOrder.dll 使用教程
## 创建DLL工具实例
### 加载DLL文件
```gotemplate
// ExpressDeliveryOrderDLL 快递订单DLL结构
type ExpressDeliveryOrderDLL struct {
dll *syscall.DLL
ztoOpenCreateOrder *syscall.Proc // 中通创建订单
jtOrderAddOrder *syscall.Proc // 极兔创建订单
emsAmpApiOpen *syscall.Proc // 邮政订单接入
stoOmsExpressOrderCreate *syscall.Proc // 申通订单创建
integrationOrderCreate *syscall.Proc // 整合快递订单创建
freeCString *syscall.Proc // 释放C字符串
}
// 初始化ExpressDeliveryOrderDLL
func InitExpressDeliveryOrderDLL() (*ExpressDeliveryOrderDLL, error) {
dllPath := filepath.Join("dll", "expressDeliveryOrder.dll")
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
return nil, fmt.Errorf("expressDeliveryOrder DLL 不存在: %s", dllPath)
}
if dll, err := syscall.LoadDLL(dllPath); err != nil {
return nil, fmt.Errorf("加载expressDeliveryOrder DLL 失败: %s", err)
} else {
return &ExpressDeliveryOrderDLL{
dll: dll,
ztoOpenCreateOrder: dll.MustFindProc("ZtoOpenCreateOrder"),
jtOrderAddOrder: dll.MustFindProc("JtOrderAddOrder"),
emsAmpApiOpen: dll.MustFindProc("EmsAmpApiOpen"),
stoOmsExpressOrderCreate: dll.MustFindProc("StoOmsExpressOrderCreate"),
integrationOrderCreate: dll.MustFindProc("IntegrationOrderCreate"),
freeCString: dll.MustFindProc("FreeCString"),
}, nil
}
}
dll, err := InitExpressDeliveryOrderDLL()
```
### 获取C字符串
```gotemplate
// cStr 获取C字符串
func (m *ExpressDeliveryOrderDLL) 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
}
```
### 使用DLL函数示例
```gotemplate
// 调用中通创建订单接口
func (m *ExpressDeliveryOrderDLL) ZtoOpenCreateOrder(requestJSON, appKey, appSecret string) (string, error) {
proc, err := m.dll.FindProc("ZtoOpenCreateOrder")
if err != nil {
return "", fmt.Errorf("找不到函数 ZtoOpenCreateOrder: %v", err)
}
requestJSONPtr, _ := syscall.BytePtrFromString(requestJSON)
appKeyPtr, _ := syscall.BytePtrFromString(appKey)
appSecretPtr, _ := syscall.BytePtrFromString(appSecret)
resultPtr, _, _ := proc.Call(
uintptr(unsafe.Pointer(requestJSONPtr)),
uintptr(unsafe.Pointer(appKeyPtr)),
uintptr(unsafe.Pointer(appSecretPtr)),
)
result := m.cStr(resultPtr)
return result, nil
}
```
# 接口详情
## 中通快递创建订单接口--ZtoOpenCreateOrder
### 请求信息
```gotemplate
dll.ZtoOpenCreateOrder(requestJSON, appKey, appSecret)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| appKey | string | 是 | 中通APIkey |
| appSecret | string | 是 | 中通API应用密钥 |
#### requestJSON json字符串
API地址 https://open.zto.com/#/interfaces?resourceGroup=20&apiName=zto.open.createOrder
```json
{
"partnerType": "2",
"orderType": "1",
"partnerOrderCode": "商家自主定义",
"accountInfo": {
"accountId": "test",
"accountPassword": "",
"type": 1,
"customerId": "GPG1576724269"
},
"billCode": "",
"senderInfo": {
"senderId": "",
"senderName": "张三",
"senderPhone": "010-22226789",
"senderMobile": "13900000000",
"senderProvince": "上海",
"senderCity": "上海市",
"senderDistrict": "青浦区",
"senderAddress": "华志路"
},
"receiveInfo": {
"receiverName": "Jone Star",
"receiverPhone": "021-87654321",
"receiverMobile": "13500000000",
"receiverProvince": "上海",
"receiverCity": "上海市",
"receiverDistrict": "闵行区",
"receiverAddress": "申贵路1500号"
},
"orderVasList": [
{
"vasType": "COD",
"vasAmount": 100000,
"vasPrice": 0,
"vasDetail": "",
"accountNo": ""
}
],
"hallCode": "S2044",
"siteCode": "02100",
"siteName": "上海",
"summaryInfo": {
"size": "",
"quantity": 3,
"price": "30",
"freight": "20",
"premium": "10",
"startTime": "2020-12-10 12:00:00",
"endTime": "2020-12-10 12:00:00"
},
"remark": "小吉下单",
"orderItems": [
{
"name": "",
"category": "",
"material": "",
"size": "",
"weight": 0,
"unitprice": 0,
"quantity": 0,
"remark": ""
}
],
"cabinet": {
"address": "",
"specification": 0,
"code": ""
}
}
```
### 响应数据
```json
{"result":{"bigMarkInfo":{"bagAddr":"合肥","mark":"460- 38"},"siteCode":"02100","siteName":"上海","signBillInfo":{},"orderCode":"12123412434","billCode":"130005102254","partnerOrderCode":"43423424"},"message":"请求成功","status":true,"statusCode":"SYS000"}
```
### 异常示例
```json
{"result":null,"message":"电子面单账号或者集团客户编码不能为空","status":false,"statusCode":"DEF001"}
```
## 极兔快递创建订单接口--JtOrderAddOrder
### 请求信息
```gotemplate
dll.JtOrderAddOrder(requestJSON, apiAccount, privateKey)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| apiAccount | string | 是 | 极兔API账户标识 |
| privateKey | string | 是 | 极兔API私钥 |
#### requestJSON JSON字符串
API地址 https://open.bxexpress.com.cn/#/apiDoc/orderserve/create
```json
{
"customerCode": "J0086474299",
"digest": "Base64(Md5(客户编号+密文+privateKey))",
"network": "合作网点编码",
"txlogisticId": "客户订单号",
"expressType": "EZ",
"orderType": "2",
"serviceType": "01",
"deliveryType": "03",
"payType": "PP_PM",
"sender": {
"name": "寄件人姓名",
"company": "寄件公司",
"postCode": "寄件邮编",
"mailBox": "寄件邮箱",
"mobile": "寄件手机",
"phone": "寄件电话",
"countryCode": "CHN",
"prov": "寄件省份",
"city": "寄件城市",
"area": "寄件区域",
"town": "寄件乡镇",
"street": "寄件街道",
"address": "寄件详细地址"
},
"receiver": {
"name": "收件人姓名",
"company": "收件公司",
"postCode": "收件邮编",
"mailBox": "收件邮箱",
"mobile": "收件手机",
"phone": "收件电话",
"countryCode": "CHN",
"prov": "收件省份",
"city": "收件城市",
"area": "收件区域",
"town": "收件乡镇",
"street": "收件街道",
"address": "收件详细地址"
},
"sendStartTime": "yyyy-MM-dd HH:mm:ss",
"sendEndTime": "yyyy-MM-dd HH:mm:ss",
"goodsType": "bm000001",
"isRealName": true,
"isCustomsDeclaration": false,
"length": 10,
"width": 10,
"height": 10,
"weight": "0.02",
"totalQuantity": 1,
"itemsValue": "100.00",
"priceCurrency": "RMB",
"offerFee": "100.00",
"remark": "备注信息",
"items": [
{
"itemType": "bm000001",
"itemName": "物品名称",
"chineseName": "物品中文名称",
"englishName": "English Name",
"number": 1,
"itemValue": "100.00",
"priceCurrency": "RMB",
"desc": "物品描述",
"itemUrl": "https://example.com/item"
}
],
"customsInfo": {
"count": 1,
"unit": "个",
"sourceArea": "CHN",
"productRecordNo": "产品国检备案编号",
"goodPrepardNo": "商品海关备案号",
"taxNo": "商品行邮税号",
"hsCode": "海关编码",
"goodsCode": "商品编号",
"brand": "货物品牌",
"specifications": "规格型号",
"manufacturer": "生产厂家",
"cargoDeclaredValue": 100.00000,
"declaredValueDeclaredCurrency": "RMB",
"customerFreight": "50.00",
"iePort": "口岸代码",
"enterpriseCustomsCode": "海关注册编号"
},
"postSiteCode": "驿站编码",
"postSiteName": "驿站名称",
"postSiteAddress": "驿站地址",
"pickupExpressCodeCheckWay": 0,
"pickUpCode": "取件码",
"extendInfo": {
"isFourLevelAddress": "1"
}
}
```
### 响应参数
```json
{
"code":"1",
"msg":"success",
"data":{
"txlogisticId":"TEST20220704210006",
"billCode":"UT0000498364212",
"sortingCode":"382 300-64 010",
"sumFreight":"5.00",
"createOrderTime":"2022-07-04 12:00:53",
"lastCenterName":"华东转运中心B1"
}
}
```
## 邮政快递订单接入接口--EmsAmpApiOpen
### 请求信息
```gotemplate
dll.EmsAmpApiOpen(requestJSON, secretKey)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| secretKey | string | 是 | 邮政API密钥 |
#### requestJSON JSON字符串
API地址https://api.ems.com.cn/#/gnapijj 需要下载API文档里面的 订单接入 接口
```json
[
{
"ecommerceUserId": "12313165",
"deliveryPasswordFlag": "1",
"deliveryPassword": "",
"logisticsOrderNo": "12384565600",
"batchNo": "",
"waybillNo": "",
"oneBillFlag": "",
"submailNo": "",
"oneBillNum": "",
"oneBillFeeType": "",
"contentsAttribute": "1",
"bizProductNo": "1",
"bizProductId": "",
"teanIncrementFlag": "",
"weight": 0,
"volume": 0,
"length": 0,
"width": 0,
"height": 0,
"postageTotal": 12,
"remarks": "",
"insuranceFlag": "",
"insuranceAmount": "0",
"deliverType": "1",
"deliverPreDate": "",
"paymentMode": "",
"codFlag": "",
"codAmount": "0",
"valuableFlag": "1",
"receiverAgentIdType": "1",
"receiverIdNo": "",
"projectId": "",
"reservationReturnFlag": "1",
"reservationReturnTime": "1",
"returnRelationNo": "",
"receiptFlag": "",
"receiptWaybillNo": "",
"localCollectionFee": "1",
"sender": {
"name": "张三",
"postCode": "",
"phone": "",
"mobile": "15232805086",
"prov": "北京市",
"city": "北京市",
"county": "西城区",
"address": "永安路174号"
},
"receiver": {
"name": "张三",
"postCode": "",
"phone": "",
"mobile": "15232805086",
"prov": "北京市",
"city": "北京市",
"county": "西城区",
"address": "永安路174号"
},
"cargos": [
{
"cargoName": "测试商品",
"cargoCategory": "",
"cargoQuantity": "1",
"cargoValue": "1",
"cargoWeight": "1"
}
]
}
]
```
### 响应参数
```json
{
"logisticsOrderNo": "客户内部订单号",
"waybillNo": "物流运单号1,物流运单号2",
"routeCode": "四段码/分拣码",
"packageCode": "集包地编码",
"packageCodeName": "集包地名称",
"markDestinationCode": "大头笔编码",
"markDestinationName": "大头笔"
}
```
## 申通快递订单创建接口--StoOmsExpressOrderCreate
### 请求信息
```gotemplate
dll.StoOmsExpressOrderCreate(requestJSON, fromAppkey, secretKey, fromCode)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| fromAppkey | string | 是 | 请求发起方应用密钥 |
| secretKey | string | 是 | 签名密钥 |
| fromCode | string | 是 | 请求发起方应用资源编码|
#### requestJSON JSON字符串
API地址https://open.sto.cn/#/apiDocument
```json
{
"orderNo": "8885452262",
"orderSource": "****",
"billType": "00",
"orderType": "01",
"sender": {
"name": "测试名称",
"tel": "0558-45778586",
"mobile": "18775487548",
"postCode": "100001",
"country": "中国",
"province": "安徽",
"city": "合肥",
"area": "泸州",
"town": "测试镇",
"address": "XX街道XX小区XX楼888"
},
"receiver": {
"name": "测试名称",
"tel": "0556-45778586",
"mobile": "15575487548",
"postCode": "100001",
"country": "中国",
"province": "河北",
"city": "湖州",
"area": "江汉",
"town": "收件镇",
"address": "XX街道XX小区XX楼666",
"safeNo": "13466666632-0011"
},
"cargo": {
"battery": "10",
"goodsType": "大件",
"goodsName": "XX物",
"goodsCount": 10,
"spaceX": 10,
"spaceY": 10,
"spaceZ": 10,
"weight": 10,
"goodsAmount": "100",
"cargoItemList": [
{
"serialNumber": "8451234",
"referenceNumber": "88838783634",
"productId": "001",
"name": "小商品",
"qty": 10,
"unitPrice": 1,
"amount": 10,
"currency": "美元",
"weight": 10,
"remark": "无"
}
]
},
"customer": {
"siteCode": "666666",
"customerName": "666666000001",
"sitePwd": "***",
"monthCustomerCode": "9000000"
},
"internationalAnnex": {
"internationalProductType": "01",
"customsDeclaration": false,
"senderCountry": "中国",
"receiverCountry": "俄罗斯"
},
"waybillNo": "59635456632",
"assignAnnex": {
"takeCompanyCode": "862456565466",
"takeUserCode": "9000000007"
},
"codValue": "2000",
"freightCollectValue": "20",
"timelessType": "01",
"productType": "01",
"serviceTypeList": [
"***"
],
"extendFieldMap": {
"mapValue": "***"
},
"remark": "无备注",
"expressDirection": "01",
"createChannel": "01",
"regionType": "01",
"insuredAnnex": {
"insuredValue": "6.66",
"goodsValue": "6.66"
},
"expectValue": "10",
"payModel": "1"
}
```
### 响应数据
```json
{
"success": true,
"errorCode": "PARAM_INVALID/QUERY_ROOKIE_EXCEPTION/AddOrderFailed/SendMobileError/SendPhoneError/SERVER_EXCEPTION/TIME_OUT/PrintCodeRepeat",
"errorMsg": "waybillNo invalid:运单号不符合申通单号规则ROUTING_INFO_QUERY_NO_REACHABLE: 物流服务不支持派送;该账号剩余面单库存不足,请联系合作网点充值;",
"data": {
"orderNo": "88875485332",
"waybillNo": "47893154",
"bigWord": "877-342-213",
"packagePlace": "上海青浦测试集包地",
"sourceOrderId": "88875485332",
"safeNo": "95013740109424",
"newBlockCode": "新四段码"
}
}
```
## 韵达快递--电子面单下单接口--YdCreateBmOrder
### 请求信息
```gotemplate
dll.YdCreateBmOrder(requestJSON, appKey, appSecret)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| appKey | string | 是 | 请求发起方应用密钥 |
| appSecret | string | 是 | 签名密钥 |
#### requestJSON JSON字符串
API地址https://open.yundaex.com/api/apiDoc?apiCode=apiauth_doc 电子面单下单接口
```json
{
"appid": "999999",
"partner_id": "201700101001",
"secret": "123456789",
"orders": [
{
"collection_value": 126.5,
"cus_area1": "",
"cus_area2": "",
"isProtectPrivacy": "",
"items": [
{
"name": "衣服",
"number": 1,
"remark": "袜子"
}
],
"khddh": 2012121715001,
"node_id": "350",
"order_serial_no": 2012121715001,
"order_type": "common",
"platform_source": "",
"receiver": {
"address": "上海市,青浦区,盈港东路 6679 号",
"city": "上海市",
"company": "",
"county": "青浦区",
"mobile": "17601206977",
"name": "李四",
"province": "上海市"
},
"remark": "",
"sender": {
"address": "上海市,青浦区,盈港东路 7766 号",
"city": "上海市",
"company": "",
"county": "青浦区",
"mobile": "17601206977",
"name": "张三",
"province": "上海市"
},
"size": "0.12,0.23,0.11",
"special": 0,
"value": 126.5,
"weight": 0,
"multi_pack": {
"mulpck": "",
"total": 0,
"endmark": 0
},
"markingInfos": [
{
"type": "INSURED",
"markingValue": {
"value": 2100
}
},
{
"type": "DF",
"markingValue": {
"value": 15
}
},
{
"type": "COD",
"markingValue": {
"value": 15
}
},
{
"type": "RETURN",
"markingValue": {
"value": "1,2"
}
},
{
"type": "YXZ"
},
{
"type": "MUL",
"markingValue": {
"value": 10
}
},
{
"type": "CONTACT"
}
]
}
]
}
```
### 响应参数
```json
{
"code": "0000",
"message": "请求成功",
"result": true,
"data":[{
"order_serial_no": "0000019876576897",
"pdf_info": "[[{"order_id":"5160533654","order_serial_no":"test_191021002","partner_id":"2017001068","partner_orderid":"test_191021002","order_type":"common","mailno":"4060005469862","customer_id":"","sender_name":"\u738b\u5c0f\u864e","sender_company":"\u51ef\u5229","sender_area_ids":"","sender_area_names":"\u6c5f\u82cf\u7701\uff0c\u5f90\u5dde\u5e02\uff0c\u65b0\u6c82\u5e02","sender_address":"\u6e56\u4e1c\u8def999\u53f7","sender_postcode":"221435","sender_phone":"021-85926525","sender_mobile":"13761960078","sender_branch":"201700","receiver_name":"\u9646\u5927\u6709","receiver_company":"\u5343\u5343","receiver_area_ids":"310118","receiver_area_names":"\u4e0a\u6d77\u5e02,\u4e0a\u6d77\u5e02,\u9752\u6d66\u533a","receiver_address":"\u4e0a\u6d77\u5e02\u9752\u6d66\u533a\u76c8\u6e2f\u4e1c\u8def6633\u53f7","receiver_postcode":"201700","receiver_phone":"020-57720341","receiver_mobile":"13761960075","receiver_branch":"200230","weight":"11.00","remark":"","status":"rs10","time":"2019-10-21 11:16:26","position_no":"G096-00 20","position_zz":"0","options":"","send_num":"0","nb_ckh":"2001123","cus_area1":"\u8ba2\u5355\u53f7:test_191021002\n\u8ba2\u5355\u53f7\uff1a123 \n\u6279\u6b21\u53f7\uff1a456212","cus_area2":"","position":"300","receiver_flag":"1","package_wd":"J200000","callback_id":"","wave_no":"","node_id":"","ems_flag":"","cus_area3":"","trade_code":"","shi1":null,"sheng1":null,"shi2":"310100","sheng2":"310000","collection_value":"100.00","value":"20.00","zffs":"0","innerProvinceName":"\u7701\u5185\u4ef6","package_wdjc":"\u96c6\u5305\u5730\uff1a\u4e0a\u6d77\u5206\u62e8\u5305 ","sender_branch_jc":"\u9752\u6d66\u533aYD","bigpen_code":"G096-00","lattice_mouth_no":"20","mailno_barcode":"406000546986204240","tname":"mailtmp_s12","dispatch_code":"20","qrcode":"4060005469862\/300 G096-00 20","privacy_receiver_name":"\u9646**","privacy_receiver_phone":"020-****0341","privacy_receiver_mobile":"137****0075"},["0424",0]]]",
"mail_no": "5300010219368",
"status": "1",
"remark": null,
"msg": "订单创建成功",
"orderId":"9876576897"
}]
}
```
### 错误响应参数
```json
{
"result": false,
"code": "7777",
"message": "内部接口服务失败",
"sub_code":"s10",
"sub_msg": "参数校验不合法"
}
```
## 韵达快递--电子面单打印接口--YdBmGetPdfInfo
### 请求信息
```gotemplate
dll.YdBmGetPdfInfo(requestJSON, appKey, appSecret)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-------------|
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| appKey | string | 是 | 请求发起方应用密钥 |
| appSecret | string | 是 | 签名密钥 |
#### requestJSON JSON字符串
API地址https://open.yundaex.com/api/apiDoc?apiCode=apiauth_doc 电子面单打印接口
```json
{
"appid": "999999",
"partner_id": "201700101001",
"secret": "123456789",
"orders": [
{
"mailno": "312356465656666"
}
]
}
```
### 响应参数
```json
{
"code": "0000",
"data": [
{
"mailno": "312356465656666",
"pdfInfo": "base64info"
}
],
"message": "请求成功",
"result": true,
"sub_code": null,
"sub_msg": null
}
```
## 整合快递订单创建接口--IntegrationOrderCreate
### 请求信息
```gotemplate
dll.StoOmsExpressOrderCreate(requestJSON, fromAppkey, secretKey, fromCode)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|-------------|--|--|---------------------------------------------------------------------|
| orderType | string | 是 | 快递类型ZTO 中通/JT 极兔/EMS 邮政/STO 申通) |
| requestJSON | string | 是 | 创建订单参数JSON字符串 |
| key | string | 是 | key 对应ZTO中appKey,JT中apiAccount,STO中fromAppkeyEMS类型的时候可以不填其他类型必填 |
| secret | string | 是 | 签名密钥 对应ZTO中appSecret,JT中privateKey,EMS中secretKey,STO中secretKey |
| fromCode | string | 是 | 请求发起方应用资源编码,快递类型EMS类型时必填 |
### 请求参数
```text
可以看上面ZTO 中通/JT 极兔/EMS 邮政/STO 申通) 的订单接口请求参数
```
### 响应参数
```text
可以看上面ZTO 中通/JT 极兔/EMS 邮政/STO 申通) 的订单接口响应参数
```
## 释放C字符串内存--FreeCString
### 请求信息
```gotemplate
dll.FreeCString(str)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| str | string | 是 | 需要释放的字符串 |

322
md/logger.md Normal file
View File

@ -0,0 +1,322 @@
# logger.dll 使用教程
## 1. 创建DLL工具实例
### 加载DLL文件
```gotemplate
// LoggerDLL 日志工具DLL结构
type loggerDLL struct {
dll *syscall.DLL
createLogger *syscall.Proc // 创建日志器
createContextWithTaskType *syscall.Proc // 创建带任务类型的上下文
logInfo *syscall.Proc // 记录信息日志
logError *syscall.Proc // 记录错误日志
logWarning *syscall.Proc // 记录警告日志
logSuccess *syscall.Proc // 记录成功日志
getLogs *syscall.Proc // 获取日志条目
getLogFiles *syscall.Proc // 获取日志文件列表
getVersion *syscall.Proc // 获取版本信息
closeAllLoggers *syscall.Proc // 关闭所有日志器
freeString *syscall.Proc // 释放C字符串
}
// 初始化loggerDLL
func InitLoggerDLL() (*loggerDLL, error) {
dllPath := filepath.Join("dll", "logger.dll")
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
return nil, fmt.Errorf("logger DLL 不存在: %s", dllPath)
}
if dll, err := syscall.LoadDLL(dllPath); err != nil {
return nil, fmt.Errorf("加载logger DLL 失败: %s", err)
} else {
return &loggerDLL{
dll: dll,
createLogger: dll.MustFindProc("CreateLogger"),
createContextWithTaskType: dll.MustFindProc("CreateContextWithTaskType"),
logInfo: dll.MustFindProc("LogInfo"),
logError: dll.MustFindProc("LogError"),
logWarning: dll.MustFindProc("LogWarning"),
logSuccess: dll.MustFindProc("LogSuccess"),
getLogs: dll.MustFindProc("GetLogs"),
getLogFiles: dll.MustFindProc("GetLogFiles"),
getVersion: dll.MustFindProc("GetVersion"),
closeAllLoggers: dll.MustFindProc("CloseAllLoggers"),
freeString: dll.MustFindProc("FreeString"),
}, nil
}
}
dll, err := InitLoggerDLL()
```
### 获取C字符串
```gotemplate
// cStr 获取C字符串
func (m *loggerDLL) 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.freeString != nil {
m.freeString.Call(p)
}
return s
}
```
# 接口详情
## 创建日志器--CreateLogger
### 请求信息
```gotemplate
dll.CreateLogger(configJSON)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| configJSON | string | 是 | 配置信息JSON字符串 |
#### 配置JSON结构
```json
{
"log_dir": "/path/to/logs",
"split_type": 0,
"rotate_type": 0,
"max_size": 104857600,
"max_count": 30,
"level": 1,
"enable_caller": true,
"default_task_type": "main"
}
```
#### 参数说明:
```text
log_dir: 日志目录路径
split_type: 分片方式0=按月1=按天2=按小时3=按分钟4=按秒)
rotate_type: 轮转方式0=按大小1=按数量)
max_size: 最大文件大小字节仅在rotate_type=0时有效
max_count: 最大文件数量仅在rotate_type=1时有效
level: 日志级别0=SUCCESS1=INFO2=WARNING3=ERROR
enable_caller: 是否启用调用者信息
default_task_type: 默认任务类型
```
### 响应示例
```json
"错误: 创建日志目录失败: permission denied"
```
## 创建带任务类型的上下文--CreateContextWithTaskType
### 请求信息
```gotemplate
dll.CreateContextWithTaskType(loggerHandle, taskType)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| loggerHandle | string | 是 | 日志器句柄 |
| taskType | string | 是 | 任务类型 |
### 响应示例
```json
"ctx_1645497600000000000"
```
#### 错误响应示例
```json
"错误: 无效的logger句柄"
```
## 记录信息日志--LogInfo
### 请求信息
```gotemplate
dll.LogInfo(ctxHandle, message)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| ctxHandle | string | 是 | 上下文句柄 |
| message | string | 是 | 日志消息 |
### 响应示例
```text
无返回值,日志将写入到对应的日志文件中。
```
## 记录错误日志--LogError
### 请求信息
```gotemplate
dll.LogError(ctxHandle, message)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| ctxHandle | string | 是 | 上下文句柄 |
| message | string | 是 | 日志消息 |
### 响应示例
```text
无返回值,日志将写入到对应的日志文件中。
```
## 记录警告日志--LogWarning
### 请求信息
```gotemplate
dll.LogWarning(ctxHandle, message)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| ctxHandle | string | 是 | 上下文句柄 |
| message | string | 是 | 日志消息 |
### 响应示例
```text
无返回值,日志将写入到对应的日志文件中。
```
## 记录成功日志--LogSuccess
### 请求信息
```gotemplate
dll.LogSuccess(ctxHandle, message)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|--------------|
| ctxHandle | string | 是 | 上下文句柄 |
| message | string | 是 | 日志消息 |
### 响应示例
```text
无返回值,日志将写入到对应的日志文件中。
```
## 获取日志条目--GetLogs
### 请求信息
```gotemplate
dll.GetLogs(loggerHandle, configJSON)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------|
| loggerHandle | string | 是 | 日志器句柄 |
| configJSON | string | 是 | 查询配置JSON |
#### 查询配置JSON结构
```json
{
"level": 1,
"task_type": "main",
"start_time": "2024-01-01 00:00:00",
"end_time": "2024-01-31 23:59:59",
"max_entries": 1000
}
```
#### 参数说明:
```text
level: 日志级别(-1表示所有级别
task_type: 任务类型(空字符串表示所有任务类型)
start_time: 开始时间(格式: 2006-01-02 15:04:05
end_time: 结束时间(格式: 2006-01-02 15:04:05
max_entries: 最大返回条目数0表示使用默认值1000
```
### 响应示例
```json
{
"count": 125,
"entries": [
{
"timestamp": "2024-01-15 10:30:45.123",
"level": "INFO",
"task_type": "main",
"caller": "logger.go:256",
"message": "系统启动完成"
},
{
"timestamp": "2024-01-15 10:31:15.456",
"level": "ERROR",
"task_type": "backup",
"caller": "backup.go:89",
"message": "备份文件失败: 磁盘空间不足"
}
]
}
```
### 错误响应示例
```json
{"error": "无效的logger句柄"}
```
## 获取日志文件列表--GetLogFiles
### 请求信息
```gotemplate
dll.GetLogFiles(loggerHandle)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------|
| loggerHandle | string | 是 | 日志器句柄 |
### 响应示例
```json
{
"count": 8,
"files": [
{
"level": "INFO",
"task_type": "main",
"file_name": "INFO-main-2024-01.log",
"file_size": 1048576,
"mod_time": "2024-01-15 10:30:45"
},
{
"level": "ERROR",
"task_type": "backup",
"file_name": "ERROR-backup-2024-01.log",
"file_size": 51200,
"mod_time": "2024-01-15 10:31:15"
}
]
}
```
### 错误响应示例
```json
{"error": "无效的logger句柄"}
```
## 获取版本信息--GetVersion
### 请求信息
```gotemplate
dll.GetVersion()
```
### 请求参数
```text
无参数
```
### 响应示例
```json
"v1"
```
## 关闭所有日志器--CloseAllLoggers
### 请求信息
```gotemplate
dll.CloseAllLoggers()
```
### 请求参数
```text
无参数
```
### 响应示例
```json
"成功关闭所有logger"
```
### 错误响应示例
```json
"关闭了5个logger其中1个出错最后错误: close file error"
```
## 释放C字符串内存--FreeString
### 请求信息
```gotemplate
dll.FreeString(str)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------|
| str | string | 是 | 需要释放的字符串 |

362
md/pdd.md
View File

@ -490,6 +490,368 @@ dll.PddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, reqJson)
} }
``` ```
## 生成商家自定义的规格--PddGoodsSpecIdGet
### 请求信息
```gotemplate
dll.PddGoodsSpecIdGet(clientId, clientSecret, accessToken, parentSpecId, specName)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| parentSpecId | string | 是 | 拼多多标准规格ID |
| specName | string | 是 | 商家编辑的规格值,如颜色规格下设置白色属性 |
### 响应参数
```json
{
"goods_spec_id_get_response": {
"parent_spec_id": 0,
"spec_id": 0,
"spec_name": "str"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 修改商品SKU价格--PddGoodsSkuPriceUpdate
### 请求信息
```gotemplate
dll.PddGoodsSkuPriceUpdate(clientId, clientSecret, accessToken, request)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------------------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| request | string | 是 | 价格更新请求JSON字符串 |
#### 请求JSON结构
```json
{
"goods_id": "必填商品id类型为LONG",
"ignore_edit_warn": "非必填是否获取商品发布警告信息默认为忽略类型为BOOLEAN",
"market_price": "非必填参考价单位分类型为LONG",
"market_price_in_yuan": "非必填参考价单位元类型为STRING",
"sku_price_list": [
{
"group_price": "非必填拼团购买价格单位分类型为LONG",
"is_onsale": "非必填sku上架状态0-已下架1-上架中类型为INTEGER",
"single_price": "非必填单独购买价格单位分类型为LONG",
"sku_id": "必填sku标识类型为LONG"
}
],
"sync_goods_operate": "非必填提交后上架状态0:上架,1:保持原样类型为INTEGER",
"two_pieces_discount": "非必填满2件折扣可选范围0-1000表示取消95表示95折设置需先查询规则接口获取实际可填范围类型为INTEGER"
}
```
### 响应参数
```json
{
"goods_update_sku_price_response": {
"goods_commit_id": 0,
"is_success": true
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 商品库存更新接口--PddGoodsQuantityUpdate
### 请求信息
```gotemplate
dll.PddGoodsQuantityUpdate(clientId, clientSecret, accessToken, request)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|---------------------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| request | string | 是 | 库存更新请求JSON字符串 |
#### 请求JSON结构 request 字符串
```json
{
"force_update": "非必填是否强制更新仅update_type=1(全量更新)时有效默认值falseforce_update=false时quantity不能小于预扣库存force_update=true时代表强制更新当quantity<预扣库存时不报错直接将quantity清0类型为BOOLEAN",
"goods_id": "必填商品id类型为LONG",
"outer_id": "非必填sku商家编码类型为STRING",
"quantity": "必填库存修改值。当全量更新库存时quantity必须为大于等于0的正整数当增量更新库存时quantity为整数可小于等于0。若增量更新时传入的库存为负数则负数与实际库存之和不能小于0。比如当前实际库存为1传入增量更新quantity=-1库存改为0类型为LONG",
"sku_id": "非必填sku_id和outer_id必填一个类型为LONG",
"update_type": "非必填库存更新方式可选。1为全量更新2为增量更新。如果不填默认为全量更新类型为INTEGER"
}
```
### 响应参数
```json
{
"goods_quantity_update_response": {
"is_success": false
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 获取商品信息接口 -- OutPddAuthGetCommitDetailt
### 请求信息
```gotemplate
dll.OutPddAuthGetCommitDetailt(goodsCommitId, goodsId, accessToken)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| goodsCommitId | string | 是 | 商品提交ID |
| goodsId | string | 是 | 商品ID |
| accessToken | string | 是 | 授权令牌 |
### 响应参数
```json
```
## 获取商品详情信息接口 -- OutPddAuthGetGoodsDetail
### 请求信息
```gotemplate
dll.OutPddAuthGetGoodsDetail(goodsId, accessToken)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| goodsId | string | 是 | 商品ID |
| accessToken | string | 是 | 授权令牌 |
### 响应参数
```json
{
"bad_fruit_claim": 0,
"buy_limit": 999999,
"carousel_gallery_list": [
"https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
"https://img.pddpic.com/open-gw/2025-06-30/4539f740-331b-4687-aa00-5c96855de6cd.jpeg",
"https://img.pddpic.com/open-gw/2025-06-30/b0e89e39-c97b-475d-9be2-f1909e30acb5.jpeg"
],
"cat_id": 15678,
"cost_template_id": 655688447565777,
"country_id": 0,
"customer_num": 2,
"customs": "",
"detail_gallery_list": [
"https://img.pddpic.com/open-gw/2025-06-30/b691c104-baf8-42b2-97e2-b7258113114b.jpeg",
"https://img.pddpic.com/open-gw/2023-09-07/53e6f7ff-d15e-4e8f-8625-e293717ca1e4.jpeg",
"https://img.pddpic.com/open-gw/2023-09-07/ecff591d-32a6-42c9-ba5a-6a42829092a8.jpeg",
"https://img.pddpic.com/open-gw/2023-10-16/7034f8a0-5d88-49f8-a96f-608abb8cac80.jpeg",
"https://img.pddpic.com/open-gw/2023-10-16/e10c2b6c-d4de-4fdd-8d48-f0a334735e9a.jpeg",
"https://img.pddpic.com/open-gw/2023-10-16/c19358fb-0a4d-49ad-bcc8-b2980e938064.jpeg",
"https://img.pddpic.com/open-gw/2025-06-30/1deeb9c0-7212-432b-a309-f774db6e1adb.jpeg"
],
"goods_desc": "书名:金属工艺学 下 第6版作者'邓文英,宋力宏主编'ISBN9787040456295出版社高等教育出版社",
"goods_id": 770621582375,
"goods_name": "金属工艺学 下 第6版 邓文英,宋力宏主编 高等教育出版社 978",
"goods_property_list": [
{
"punit": "",
"ref_pid": 425,
"template_pid": 401030,
"vid": 0,
"vvalue": "9787040456295"
},
{
"punit": "",
"ref_pid": 876,
"template_pid": 401029,
"vid": 0,
"vvalue": "金属工艺学 下 第6版"
},
{
"punit": "页",
"ref_pid": 692,
"template_pid": 401032,
"vid": 0,
"vvalue": "157"
},
{
"punit": "元",
"ref_pid": 879,
"template_pid": 401034,
"vid": 0,
"vvalue": "24.70"
},
{
"punit": "",
"ref_pid": 882,
"template_pid": 401037,
"vid": 0,
"vvalue": "邓文英,宋力宏主编"
},
{
"punit": "",
"ref_pid": 880,
"template_pid": 401035,
"vid": 483761,
"vvalue": "高等教育出版社"
},
{
"punit": "",
"ref_pid": 888,
"template_pid": 401043,
"vid": 0,
"vvalue": "平装"
}
],
"goods_type": 1,
"image_url": "",
"invoice_status": 0,
"is_customs": 0,
"is_folt": 0,
"is_group_pre_sale": 0,
"is_pre_sale": 0,
"is_refundable": 1,
"is_sku_pre_sale": 0,
"market_price": 5948,
"order_limit": 999999,
"outer_goods_id": "9787040456295",
"oversea_type": 0,
"pre_sale_time": 0,
"privacy_delivery": 0,
"quan_guo_lian_bao": 0,
"second_hand": 1,
"shipment_limit_second": 172800,
"sku_list": [
{
"is_onsale": 1,
"limit_quantity": 999999,
"multi_price": 1487,
"out_sku_sn": "9787040456295",
"price": 1587,
"quantity": 0,
"reserve_quantity": 0,
"sku_id": 1753931570290,
"sku_pre_sale_time": 0,
"spec": [
{
"parent_id": 1216,
"parent_name": "尺寸",
"spec_id": 27632894279,
"spec_name": "单本 无附赠 超七天不退换"
}
],
"thumb_url": "https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
"weight": 500
}
],
"status": 4,
"tiny_name": "金属工艺学 下 第6",
"two_pieces_discount": 96,
"video_gallery": [],
"warehouse": "",
"warm_tips": "",
"zhi_huan_bu_xiu": 0
}
```
## 生成自定义规格接口 -- OutPddAuthSetSpec
### 请求信息
```gotemplate
dll.OutPddAuthSetSpec(specTypeId, specName, accessToken)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| specTypeId | int | 是 | 规格类型ID |
| specName | string | 是 | 规格名称 |
| accessToken | string | 是 | 授权令牌 |
### 响应参数
```json
{
"parentSpecId": 3820,
"specName": "全新",
"specId": 1080396526
}
```
## 修改价格接口 -- OutPddAuthUpdatePrice
### 请求信息
```gotemplate
dll.OutPddAuthUpdatePrice(jsonData)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------------|
| jsonData | int | 是 | 价格修改信息JSON字符串 |
### 响应参数
```json
[
{
"success": true,
"msg": "操作成功"
},
{
"success": false,
"msg": "操作失败"
}
]
```
## 修改库存接口 -- OutPddAuthUpdateStock
### 请求信息
```gotemplate
dll.OutPddAuthUpdateStock(jsonData)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|-----------------|
| jsonData | int | 是 | 价格修改信息JSON字符串 |
### 响应参数
```json
[
{
"success": true,
"msg": "操作成功"
},
{
"success": false,
"msg": "操作失败"
}
]
```
## 12.释放C字符串内存--FreeCString ## 12.释放C字符串内存--FreeCString
### 请求信息 ### 请求信息
```gotemplate ```gotemplate

Binary file not shown.

View File

@ -120,6 +120,46 @@ extern __declspec(dllexport) char* SelfPddGoodsAdd(char* clientId, char* clientS
// //
extern __declspec(dllexport) char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson); extern __declspec(dllexport) char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson);
// PddGoodsSpecIdGet 生成商家自定义的规格
//
extern __declspec(dllexport) char* PddGoodsSpecIdGet(char* clientId, char* clientSecret, char* accessToken, char* parentSpecId, char* specName);
// PddGoodsSkuPriceUpdate 修改商品sku价格
//
extern __declspec(dllexport) char* PddGoodsSkuPriceUpdate(char* clientId, char* clientSecret, char* accessToken, char* request);
// PddGoodsQuantityUpdate 商品库存更新接口
//
extern __declspec(dllexport) char* PddGoodsQuantityUpdate(char* clientId, char* clientSecret, char* accessToken, char* request);
// PddGoodsImageUpload 商品图片上传接口
//
extern __declspec(dllexport) char* PddGoodsImageUpload(char* clientId, char* clientSecret, char* accessToken, char* fileBase);
// OutPddAuthGetCommitDetailt 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetCommitDetailt(char* goodsCommitId, char* goodsId, char* accessToken);
// OutPddAuthGetGoodsDetail 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetGoodsDetail(char* goodsId, char* accessToken);
// OutPddAuthSetSpec 生成自定义规格
//
extern __declspec(dllexport) char* OutPddAuthSetSpec(int specTypeId, char* specName, char* accessToken);
// OutPddAuthGetCats 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetCats(void);
// OutPddAuthUpdatePrice 修改价格
//
extern __declspec(dllexport) char* OutPddAuthUpdatePrice(char* jsonData);
// OutPddAuthUpdateStock 修改库存
//
extern __declspec(dllexport) char* OutPddAuthUpdateStock(char* jsonData);
// FreeCString 释放C字符串内存 // FreeCString 释放C字符串内存
// //
extern __declspec(dllexport) void FreeCString(char* str); extern __declspec(dllexport) void FreeCString(char* str);

BIN
pdd/pdd.dll Normal file

Binary file not shown.

1024
pdd/pdd.go

File diff suppressed because it is too large Load Diff

177
pdd/pdd.h Normal file
View File

@ -0,0 +1,177 @@
/* 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 "pdd.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
// PddGoodsOuterCatMappingGet 类目预测
//
extern __declspec(dllexport) char* PddGoodsOuterCatMappingGet(char* clientId, char* clientSecret, char* accessToken, char* outerCatId, char* outerCatName, char* outerGoodsName);
// PddLogisticsCompaniesGet 快递公司查看
//
extern __declspec(dllexport) char* PddLogisticsCompaniesGet(char* clientId, char* clientSecret);
// PddErpOrderSync erp打单信息同步
//
extern __declspec(dllexport) char* PddErpOrderSync(char* clientId, char* clientSecret, char* accessToken, char* logisticsId, char* orderSn, char* orderState, char* waybillNo);
// PddOrderSynchronization 拼多多订单同步
//
extern __declspec(dllexport) char* PddOrderSynchronization(char* clientId, char* clientSecret, char* accessToken, char* logisticsCompany, char* logisticsOnlineSendJson);
// PddGoodsImgUpload 商品图片上传接口
//
extern __declspec(dllexport) char* PddGoodsImgUpload(char* clientId, char* clientSecret, char* accessToken, char* filePath);
// PddGoodsAdd 商品新增接口
//
extern __declspec(dllexport) char* PddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* goodsAddJson);
// SelfPddGoodsAdd 联合拼多多图片上传的商品新增
//
extern __declspec(dllexport) char* SelfPddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* filePath, char* goodsAddJson);
// PddOpenDecryptMaskBatch 批量数据解密脱敏接口
//
extern __declspec(dllexport) char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson);
// PddGoodsSpecIdGet 生成商家自定义的规格
//
extern __declspec(dllexport) char* PddGoodsSpecIdGet(char* clientId, char* clientSecret, char* accessToken, char* parentSpecId, char* specName);
// PddGoodsSkuPriceUpdate 修改商品sku价格
//
extern __declspec(dllexport) char* PddGoodsSkuPriceUpdate(char* clientId, char* clientSecret, char* accessToken, char* request);
// PddGoodsQuantityUpdate 商品库存更新接口
//
extern __declspec(dllexport) char* PddGoodsQuantityUpdate(char* clientId, char* clientSecret, char* accessToken, char* request);
// PddGoodsImageUpload 商品图片上传接口
//
extern __declspec(dllexport) char* PddGoodsImageUpload(char* clientId, char* clientSecret, char* accessToken, char* fileBase);
// PddOrderBasicListGet 商品图片上传接口
//
extern __declspec(dllexport) char* PddOrderBasicListGet(char* clientId, char* clientSecret, char* accessToken, char* orderBasicListGetJSON);
// PddGoodsCommitDetailGet 获取商品提交的商品详情
//
extern __declspec(dllexport) char* PddGoodsCommitDetailGet(char* clientId, char* clientSecret, char* accessToken, char* goodsCommitId, char* goodsId);
// OutPddAuthGetCommitDetailt 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetCommitDetailt(char* goodsCommitId, char* goodsId, char* accessToken);
// OutPddAuthGetGoodsDetail 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetGoodsDetail(char* goodsId, char* accessToken);
// OutPddAuthSetSpec 生成自定义规格
//
extern __declspec(dllexport) char* OutPddAuthSetSpec(int specTypeId, char* specName, char* accessToken);
// OutPddAuthGetCats 获取商品信息
//
extern __declspec(dllexport) char* OutPddAuthGetCats(void);
// OutPddAuthUpdatePrice 修改价格
//
extern __declspec(dllexport) char* OutPddAuthUpdatePrice(char* jsonData);
// OutPddAuthUpdateStock 修改库存
//
extern __declspec(dllexport) char* OutPddAuthUpdateStock(char* jsonData);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

View File

@ -134,112 +134,144 @@ func (m *pddDLL) PddErpOrderSync(clientId, clientSecret, accessToken, logisticsI
return m.cStr(info), nil return m.cStr(info), nil
} }
func main() { //func main() {
//clientId := "203c5a7ba8bd4b8488d5e26f93052642" //// 获取商品类目
//clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179" //info, err := outPddAuthGetCats()
//accessToken := "bd96218bb2a146779701506dc1e5e5c478692539" //if err != nil {
//outerCatId := "15543" // fmt.Printf(err.Error())
//outerCatName := "书籍/杂志/报纸" //}
//outerGoodsName := "书籍医家金鉴 妇产科学卷" //fmt.Println(info)
//logisticsId := 0
//orderSn := ""
//orderState := ""
//waybillNo := ""
//logisticsCompany := "德邦"
// 初始化 //// 生成自定义规格
//dll, err := InitPddDLL() //info, err := outPddAuthSetSpec(3820, "全新", "e7e8a719b9b74378980914c9ca9e4a3e57a6a0c8")
//if err != nil { //if err != nil {
// fmt.Println(err) // fmt.Printf(err.Error())
//} //}
//fmt.Println(info)
// 类目预测 //// 获取商品详情信息
//info, err := dll.PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken, outerCatId, outerCatName, outerGoodsName) //info, err := outPddAuthGetGoodsDetail("770621582375", "e7e8a719b9b74378980914c9ca9e4a3e57a6a0c8")
//if err != nil { //if err != nil {
// fmt.Println(err) // fmt.Printf(err.Error())
//} //}
//fmt.Println(info) //fmt.Println(info)
// 快递公司查看 //clientId := "203c5a7ba8bd4b8488d5e26f93052642"
//get, err := dll.PddLogisticsCompaniesGet(clientId, clientSecret) //clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
//if err != nil { //accessToken := "bd96218bb2a146779701506dc1e5e5c478692539"
// fmt.Println(err) //outerCatId := "15543"
//} //outerCatName := "书籍/杂志/报纸"
//fmt.Println(get) //outerGoodsName := "书籍医家金鉴 妇产科学卷"
//var logisticsResponse LogisticsResponse //logisticsId := 0
//if err := json.Unmarshal([]byte(get), &logisticsResponse); err != nil { //orderSn := ""
// fmt.Println(err) //orderState := ""
//} //waybillNo := ""
// //logisticsCompany := "德邦"
//var company string
//var available int
//var code string
//for _, logisticsCompanies := range logisticsResponse.LogisticsCompaniesGetResponse.LogisticsCompanies {
// if strings.Contains(logisticsCompanies.LogisticsCompany, logisticsCompany) {
// company = logisticsCompanies.LogisticsCompany
// logisticsId = logisticsCompanies.ID
// available = logisticsCompanies.Available
// code = logisticsCompanies.Code
// break
// }
//}
//fmt.Println("快递公司名称: ", company)
//fmt.Println("快递公司编码: ", logisticsId)
//fmt.Println("是否有效: ", available)
//fmt.Println("物流公司代码: ", code)
//file := "D:\\isbn_images\\result\\9780007935192.jpg" // 初始化
//open, err := os.Open(file) //dll, err := InitPddDLL()
//if err != nil { //if err != nil {
// fmt.Println(err) // fmt.Println(err)
//} //}
//defer open.Close()
////base := filepath.Base(file)
//// 商品图片上传接口
//upload, err := pddGoodsImgUpload(clientId, clientSecret, accessToken, file)
//if err != nil {
// fmt.Println(err)
//}
//fmt.Println(upload)
//get, err := pddLogisticsCompaniesGet(clientId, clientSecret) // 类目预测
//if err != nil { //info, err := dll.PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken, outerCatId, outerCatName, outerGoodsName)
// fmt.Println(err) //if err != nil {
//} // fmt.Println(err)
//fmt.Println(get) //}
//fmt.Println(info)
// 脱敏 // 快递公司查看
//get, err := dll.PddLogisticsCompaniesGet(clientId, clientSecret)
//if err != nil {
// fmt.Println(err)
//}
//fmt.Println(get)
//var logisticsResponse LogisticsResponse
//if err := json.Unmarshal([]byte(get), &logisticsResponse); err != nil {
// fmt.Println(err)
//}
//
//var company string
//var available int
//var code string
//for _, logisticsCompanies := range logisticsResponse.LogisticsCompaniesGetResponse.LogisticsCompanies {
// if strings.Contains(logisticsCompanies.LogisticsCompany, logisticsCompany) {
// company = logisticsCompanies.LogisticsCompany
// logisticsId = logisticsCompanies.ID
// available = logisticsCompanies.Available
// code = logisticsCompanies.Code
// break
// }
//}
//fmt.Println("快递公司名称: ", company)
//fmt.Println("快递公司编码: ", logisticsId)
//fmt.Println("是否有效: ", available)
//fmt.Println("物流公司代码: ", code)
//jsonStr := `[{"data_tag":"251229-272441044622514","encrypted_data":"~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"}]` //file := "D:\\isbn_images\\result\\9780007935192.jpg"
//open, err := os.Open(file)
//if err != nil {
// fmt.Println(err)
//}
//defer open.Close()
////base := filepath.Base(file)
//// 商品图片上传接口
//upload, err := pddGoodsImgUpload(clientId, clientSecret, accessToken, file)
//if err != nil {
// fmt.Println(err)
//}
//fmt.Println(upload)
//var records []DataList //get, err := pddLogisticsCompaniesGet(clientId, clientSecret)
//err := json.Unmarshal([]byte(jsonStr), &records) //if err != nil {
//if err != nil { // fmt.Println(err)
// log.Fatal("解析JSON失败:", err) //}
//} //fmt.Println(get)
//batch, err := pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, jsonStr) // 脱敏
//if err != nil {
// fmt.Println(err) //jsonStr := `[{"data_tag":"251229-272441044622514","encrypted_data":"~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"}]`
//}
//fmt.Println(batch) //var records []DataList
//clientId := "203c5a7ba8bd4b8488d5e26f93052642" //err := json.Unmarshal([]byte(jsonStr), &records)
//clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179" //if err != nil {
//accessToken := "5b1e9506827049a7a9335302e917d2b896a3d6c7" // log.Fatal("解析JSON失败:", err)
//logisticsCompany := "韵达快递" //}
//logisticsId := ""
//orderSn := "260107-652497405582514" //batch, err := pddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, jsonStr)
//orderState := "1" //if err != nil {
//waybillNo := "312944253800986" // fmt.Println(err)
// //}
//synchronization, err := pddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsId, orderSn, orderState, waybillNo) //fmt.Println(batch)
//if err != nil { //clientId := "203c5a7ba8bd4b8488d5e26f93052642"
// fmt.Println(err) //clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
//} //accessToken := "5b1e9506827049a7a9335302e917d2b896a3d6c7"
//fmt.Println(string(synchronization)) //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 {
DataTag string `json:"data_tag"` DataTag string `json:"data_tag"`
EncryptedData string `json:"encrypted_data"` EncryptedData string `json:"encrypted_data"`
} }
//func main() {
// jsonStr := `{"goods_name":"未厌居习作未厌居习作9787543421523叶圣陶(1894.10~1988.2)","carousel_gallery":["https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg"],"cat_id":0,"goods_type":1,"market_price":515,"detail_gallery":[],"out_goods_id":"9787543421523","sku_list":[{"is_onsale":0,"limit_quantity":999,"multi_price":415,"price":515,"sku_properties":[{"punit":"","ref_pid":0,"value":"","vid":0,"spec_id_list":"1","thumb_url":"https://img.pddpic.com/open-gw/2025-11-30/30eb451b-1fea-4f23-be25-b97454bf677a.jpeg","weight":250}],"quantity":999}],"is_folt":false,"is_pre_sale":false,"is_refundable":false,"second_hand":true,"cost_template_id":0,"country_id":0,"shipment_limit_second":172800}`
// add, err := pddGoodsAdd("203c5a7ba8bd4b8488d5e26f93052642",
// "892ffaa86e12b7a3d8d2942b669d9aa520ad8179",
// "1177d0c36419417eba692a3fea88f611d42f0665", jsonStr)
// if err != nil {
// fmt.Println(err.Error())
// }
// fmt.Println(add)
//}

128
pddTask/common/pddDll.go Normal file
View File

@ -0,0 +1,128 @@
package common
import "C"
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"syscall"
"unsafe"
)
// ErrorResponse 错误响应结构
type ErrorResponse struct {
ErrorMsg string `json:"error_msg"` // 错误信息
SubMsg string `json:"sub_msg"` // 子错误信息
SubCode interface{} `json:"sub_code"` // 使用json.RawMessage处理null和不同类型
ErrorCode int `json:"error_code"` // 错误代码
RequestID string `json:"request_id"` // 请求ID
}
// ErrorWrapper 最外层错误响应包装
type ErrorWrapper struct {
ErrorResponse ErrorResponse `json:"error_response"` // 错误响应
}
// GoodsImageUploadResp 商品图片上传响应结构体
type GoodsImageUploadResp struct {
GoodsImageUploadResponse struct {
ImageURL string `json:"image_url"` // 上传后的图片URL
RequestID string `json:"request_id"` // 请求ID
} `json:"goods_image_upload_response"`
}
// PddDLL 拼多多工具DLL结构
type pddDLL struct {
dll *syscall.DLL
pddGoodsOuterCatMappingGet *syscall.Proc // 类目预测接口
pddGoodsAdd *syscall.Proc // 商品新增接口
pddGoodsAd *syscall.Proc // 商品新增接口
pddGoodsImageUpload *syscall.Proc // 商品图片上传接口
freeCString *syscall.Proc // 释放C字符串
}
// InitPddDLL 初始化pddDLL
func InitPddDLL() (*pddDLL, error) {
dllPath := filepath.Join("pddTask", "dll", "pdd.dll")
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
return nil, fmt.Errorf("pdd DLL 不存在: %s", dllPath)
}
if dll, err := syscall.LoadDLL(dllPath); err != nil {
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
} else {
return &pddDLL{
dll: dll,
pddGoodsOuterCatMappingGet: dll.MustFindProc("PddGoodsOuterCatMappingGet"),
pddGoodsAdd: dll.MustFindProc("PddGoodsAdd"),
pddGoodsAd: dll.MustFindProc("PddGoodsAd"),
pddGoodsImageUpload: dll.MustFindProc("PddGoodsImageUpload"),
freeCString: dll.MustFindProc("FreeCString"),
}, nil
}
}
// cStr 获取C字符串
func (m *pddDLL) 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
}
// PddGoodsAdd 商品新增接口
func PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson string) (string, error) {
dll, err := InitPddDLL()
if err != nil {
return "", err
}
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
goodsAddJsonPtr, _ := syscall.BytePtrFromString(goodsAddJson)
info, _, _ := dll.pddGoodsAdd.Call(
uintptr(unsafe.Pointer(clientIdPtr)),
uintptr(unsafe.Pointer(clientSecretPtr)),
uintptr(unsafe.Pointer(accessTokenPtr)),
uintptr(unsafe.Pointer(goodsAddJsonPtr)),
)
result := dll.cStr(info)
return result, err
}
// PddGoodsImageUpload 商品图片上传接口
func (m *pddDLL) PddGoodsImageUpload(clientId, clientSecret, accessToken, fileBase string) (string, error) {
proc := m.pddGoodsImageUpload
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
fileBasePtr, _ := syscall.BytePtrFromString(fileBase)
info, _, _ := proc.Call(
uintptr(unsafe.Pointer(clientIdPtr)),
uintptr(unsafe.Pointer(clientSecretPtr)),
uintptr(unsafe.Pointer(accessTokenPtr)),
uintptr(unsafe.Pointer(fileBasePtr)),
)
result := m.cStr(info)
// 解析
var goodsImageUploadResp GoodsImageUploadResp
if err := json.Unmarshal([]byte(result), &goodsImageUploadResp); err != nil {
return "", fmt.Errorf("解析 goodsImageUploadResp JSON失败: %s,响应信息: %s", err, result)
}
return goodsImageUploadResp.GoodsImageUploadResponse.ImageURL, nil
}

BIN
pddTask/dll/pdd.dll Normal file

Binary file not shown.

489
pddTask/pdd-task.go Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -120,6 +120,10 @@ extern __declspec(dllexport) char* InitProxyManager(char* serversJson, char* use
// //
extern __declspec(dllexport) void FreeCString(char* str); extern __declspec(dllexport) void FreeCString(char* str);
// GetVersion 获取版本
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

BIN
proxy/proxy.dll Normal file

Binary file not shown.

View File

@ -87,6 +87,23 @@ func proxyTypeManager(proxyType, username, password, machineCode string) (string
} }
} }
func proxyTypeManagerNew(proxyType, username, password, machineCode string) (string, error) {
// 获取代理列表
proxies, err := getProxies(machineCode)
if err != nil {
return "", err
}
// 检查是否获取到有效代理
if len(proxies) == 0 {
return "", fmt.Errorf("未获取到有效代理")
}
// 获取代理
proxy := randomElement(proxies)
proxy = fmt.Sprintf("http://%s", proxy)
return proxy, nil
}
/* /*
* 构建小象代理URL * 构建小象代理URL
* param username[string] 代理用户名 * param username[string] 代理用户名
@ -607,12 +624,30 @@ func ProxyTypeManager(proxyType, username, password, machineCode *C.char) *C.cha
return C.CString(proxyURL) return C.CString(proxyURL)
} }
// ProxyTypeManagerNew 导出函数:代理类型管理器
//
//export ProxyTypeManagerNew
func ProxyTypeManagerNew(proxyType, username, password, machineCode *C.char) *C.char {
// C字符串转换为Go字符串
goProxyType := C.GoString(proxyType)
goUsername := C.GoString(username)
goPassword := C.GoString(password)
goMachineCode := C.GoString(machineCode)
// 调用代理类型管理器
proxyURL, err := proxyTypeManagerNew(goProxyType, goUsername, goPassword, goMachineCode)
if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err)
return C.CString(errorMsg)
}
return C.CString(proxyURL)
}
// GetMachineCode 导出函数:查询机器码 // GetMachineCode 导出函数:查询机器码
// //
//export GetMachineCode //export GetMachineCode
func GetMachineCode(tailCardSecret *C.char) *C.char { func GetMachineCode(tailCardSecret *C.char) *C.char {
goTailCardSecret := C.GoString(tailCardSecret) goTailCardSecret := C.GoString(tailCardSecret)
log.Printf("[DEBUG] 查询机器码调用: card=%s", goTailCardSecret)
resp, err := getMachineCode(goTailCardSecret) resp, err := getMachineCode(goTailCardSecret)
if err != nil { if err != nil {
@ -629,7 +664,6 @@ func GetMachineCode(tailCardSecret *C.char) *C.char {
return C.CString(errorMsg) return C.CString(errorMsg)
} }
log.Printf("[DEBUG] 查询机器码成功: code=%d, machine_code=%s", resp.Code, resp.Data.MachineCode)
return C.CString(string(jsonData)) return C.CString(string(jsonData))
} }
@ -703,7 +737,6 @@ func GetProxies(machineCode *C.char) *C.char {
//export CheckTailCardSecretExpired //export CheckTailCardSecretExpired
func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char { func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
goTailCardSecret := C.GoString(tailCardSecret) goTailCardSecret := C.GoString(tailCardSecret)
log.Printf("[DEBUG] 检查卡密是否过期调用: card=%s", goTailCardSecret)
isValid, err := checkTailCardSecretExpired(goTailCardSecret) isValid, err := checkTailCardSecretExpired(goTailCardSecret)
if err != nil { if err != nil {
@ -732,8 +765,6 @@ func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err) errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
return C.CString(errorMsg) return C.CString(errorMsg)
} }
log.Printf("[DEBUG] 检查卡密过期结果: is_valid=%v", isValid)
return C.CString(string(jsonData)) return C.CString(string(jsonData))
} }
@ -795,6 +826,18 @@ func FreeCString(str *C.char) {
C.free(unsafe.Pointer(str)) C.free(unsafe.Pointer(str))
} }
// PROXY_VERSION 版本号
const (
PROXY_VERSION = "v2"
)
// GetVersion 获取版本
//
//export GetVersion
func GetVersion() *C.char {
return C.CString(PROXY_VERSION)
}
// 空main函数编译DLL时需要 // 空main函数编译DLL时需要
func main() { func main() {
} }

133
proxy/proxy.h Normal file
View File

@ -0,0 +1,133 @@
/* 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
// GetProxyHealth 导出函数:获取代理健康状态(用于调试)
//
extern __declspec(dllexport) char* GetProxyHealth(void);
// ProxyTypeManager 导出函数:代理类型管理器
//
extern __declspec(dllexport) char* ProxyTypeManager(char* proxyType, char* username, char* password, char* machineCode);
// ProxyTypeManagerNew 导出函数:代理类型管理器
//
extern __declspec(dllexport) char* ProxyTypeManagerNew(char* proxyType, char* username, char* password, char* machineCode);
// GetMachineCode 导出函数:查询机器码
//
extern __declspec(dllexport) char* GetMachineCode(char* tailCardSecret);
// RechargeCard 导出函数:充值卡密
//
extern __declspec(dllexport) char* RechargeCard(char* tailCardSecret, char* machineCode);
// GetProxies 导出函数:获取代理服务器列表
//
extern __declspec(dllexport) char* GetProxies(char* machineCode);
// CheckTailCardSecretExpired 导出函数:检查卡密是否过期
//
extern __declspec(dllexport) char* CheckTailCardSecretExpired(char* tailCardSecret);
// InitProxyManager 导出函数:初始化代理管理器
//
extern __declspec(dllexport) char* InitProxyManager(char* serversJson, char* username, char* password, char* tailCardSecret, char* proxyType);
// FreeCString 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
// GetVersion 获取版本
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,7 @@
package main package main
import "fmt"
///* ///*
//#cgo LDFLAGS: -ldl //#cgo LDFLAGS: -ldl
// //
@ -697,3 +699,11 @@ package main
// //
//func main() { //func main() {
//} //}
func main() {
managerNew, err := proxyTypeManagerNew("", "", "", "07f4d0fbcff99966c2b37b0c1fb7f01c")
if err != nil {
fmt.Println(err)
}
fmt.Println(managerNew)
}

342
sqlite/sqLite.go Normal file
View File

@ -0,0 +1,342 @@
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "modernc.org/sqlite"
)
// 用户模型
type User struct {
ID int
Name string
Email string
Age int
CreatedAt time.Time
}
// 数据库连接
var db *sql.DB
func main() {
var err error
// 1. 初始化数据库连接
db, err = initDatabase("test.db")
if err != nil {
log.Fatal("初始化数据库失败:", err)
}
defer db.Close()
fmt.Println("SQLite数据库CRUD示例")
fmt.Println("====================")
//// 2. 创建用户表
//err = createTable()
//if err != nil {
// log.Fatal("创建表失败:", err)
//}
//fmt.Println("✓ 用户表创建成功")
//
//// 3. 创建用户
//user1 := User{
// Name: "张三",
// Email: "zhangsan@example.com",
// Age: 25,
//}
//
//id, err := createUser(user1)
//if err != nil {
// log.Fatal("创建用户失败:", err)
//}
//fmt.Printf("✓ 创建用户成功ID: %d\n", id)
//
user2 := User{
Name: "李四",
Email: "lisi@example.com",
Age: 30,
}
id2, _ := createUser(user2)
fmt.Printf("✓ 创建用户成功ID: %d\n", id2)
//// 4. 查询单个用户
//fmt.Println("\n查询用户ID=1:")
//user, err := getUserByID(1)
//if err != nil {
// log.Fatal("查询用户失败:", err)
//}
//printUser(user)
// 5. 查询所有用户
fmt.Println("\n查询所有用户:")
users, err := getAllUsers()
if err != nil {
log.Fatal("查询所有用户失败:", err)
}
for _, u := range users {
printUser(u)
}
//// 6. 更新用户
//fmt.Println("\n更新用户ID=1的信息:")
//updateData := map[string]interface{}{
// "name": "张三丰",
// "email": "zhangsanfeng@example.com",
// "age": 28,
//}
//rowsAffected, err := updateUser(1, updateData)
//if err != nil {
// log.Fatal("更新用户失败:", err)
//}
//fmt.Printf("✓ 更新成功,影响行数: %d\n", rowsAffected)
//
//// 7. 再次查询验证更新
//updatedUser, _ := getUserByID(1)
//printUser(updatedUser)
//
//// 8. 删除用户
//fmt.Println("\n删除用户ID=2:")
//rowsAffected, err = deleteUser(2)
//if err != nil {
// log.Fatal("删除用户失败:", err)
//}
//fmt.Printf("✓ 删除成功,影响行数: %d\n", rowsAffected)
//
//// 9. 查询删除后的用户列表
//fmt.Println("\n删除后的所有用户:")
//remainingUsers, _ := getAllUsers()
//if len(remainingUsers) == 0 {
// fmt.Println("没有用户数据")
//}
//for _, u := range remainingUsers {
// printUser(u)
//}
//
//// 10. 批量插入示例
//fmt.Println("\n批量插入用户:")
//err = batchCreateUsers()
//if err != nil {
// log.Fatal("批量插入失败:", err)
//}
//
//// 11. 分页查询示例
//fmt.Println("\n分页查询每页2条:")
//pageSize := 2
//fmt.Println("第1页:")
//page1Users, _ := getUsersByPage(1, pageSize)
//for _, u := range page1Users {
// printUser(u)
//}
//
//fmt.Println("\n第2页:")
//page2Users, _ := getUsersByPage(2, pageSize)
//for _, u := range page2Users {
// printUser(u)
//}
//
//// 12. 统计用户数量
//count, _ := countUsers()
//fmt.Printf("\n用户总数: %d\n", count)
}
// 初始化数据库连接
func initDatabase(dbPath string) (*sql.DB, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, err
}
// 设置连接池参数
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// 验证连接
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}
// 创建用户表
func createTable() error {
query := `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at);
`
_, err := db.Exec(query)
return err
}
// 创建用户返回生成的ID
func createUser(user User) (int64, error) {
query := `INSERT INTO users (name, email, age) VALUES (?, ?, ?)`
result, err := db.Exec(query, user.Name, user.Email, user.Age)
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return id, nil
}
// 根据ID查询用户
func getUserByID(id int) (User, error) {
var user User
query := `SELECT id, name, email, age, created_at FROM users WHERE id = ?`
row := db.QueryRow(query, id)
err := row.Scan(&user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt)
if err != nil {
return User{}, err
}
return user, nil
}
// 查询所有用户
func getAllUsers() ([]User, error) {
query := `SELECT id, name, email, age, created_at FROM users ORDER BY id`
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var users []User
for rows.Next() {
var user User
err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt)
if err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}
// 更新用户
func updateUser(id int, updates map[string]interface{}) (int64, error) {
// 构建SQL语句
query := "UPDATE users SET "
args := []interface{}{}
i := 0
for key, value := range updates {
if i > 0 {
query += ", "
}
query += key + " = ?"
args = append(args, value)
i++
}
query += " WHERE id = ?"
args = append(args, id)
result, err := db.Exec(query, args...)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
// 删除用户
func deleteUser(id int) (int64, error) {
query := `DELETE FROM users WHERE id = ?`
result, err := db.Exec(query, id)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
// 批量创建用户
func batchCreateUsers() error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO users (name, email, age) VALUES (?, ?, ?)")
if err != nil {
return err
}
defer stmt.Close()
users := []User{
{Name: "王五", Email: "wangwu@example.com", Age: 22},
{Name: "赵六", Email: "zhaoliu@example.com", Age: 35},
{Name: "孙七", Email: "sunqi@example.com", Age: 28},
}
for _, user := range users {
_, err := stmt.Exec(user.Name, user.Email, user.Age)
if err != nil {
return err
}
}
return tx.Commit()
}
// 分页查询用户
func getUsersByPage(page, pageSize int) ([]User, error) {
offset := (page - 1) * pageSize
query := `SELECT id, name, email, age, created_at FROM users
ORDER BY id LIMIT ? OFFSET ?`
rows, err := db.Query(query, pageSize, offset)
if err != nil {
return nil, err
}
defer rows.Close()
var users []User
for rows.Next() {
var user User
err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt)
if err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}
// 统计用户数量
func countUsers() (int, error) {
var count int
query := `SELECT COUNT(*) FROM users`
err := db.QueryRow(query).Scan(&count)
return count, err
}
// 打印用户信息
func printUser(user User) {
fmt.Printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d, 创建时间: %s\n",
user.ID, user.Name, user.Email, user.Age, user.CreatedAt.Format("2006-01-02 15:04:05"))
}

BIN
test.db Normal file

Binary file not shown.

252
timer/timer.go Normal file
View File

@ -0,0 +1,252 @@
package main
import (
"container/heap"
"fmt"
"sync"
"time"
)
//func main() {
// // 创建一个每秒触发一次的定时器
// ticker := time.NewTicker(1 * time.Second)
// defer ticker.Stop() // 确保资源被释放
//
// fmt.Println("定时器启动,每秒执行一次...")
//
// for i := 1; i <= 5; i++ {
// <-ticker.C // 等待定时器触发
// fmt.Printf("执行第 %d 次,当前时间: %v\n", i, time.Now().Format("15:04:05"))
// }
//
// fmt.Println("定时器执行完成")
//}
//func main() {
// ticker := time.NewTicker(500 * time.Millisecond)
// done := make(chan bool)
//
// // 5秒后停止定时器
// go func() {
// time.Sleep(5 * time.Second)
// ticker.Stop()
// done <- true
// fmt.Println("定时器已停止")
// }()
//
// fmt.Println("定时器启动每500毫秒执行一次...")
//
// for {
// select {
// case t := <-ticker.C:
// fmt.Printf("定时任务执行,时间: %v\n", t.Format("15:04:05.000"))
// case <-done:
// return
// }
// }
//}
//func main() {
// fmt.Println("程序启动:", time.Now().Format("15:04:05"))
//
// // 创建一个3秒后触发的单次定时器
// timer := time.NewTimer(3 * time.Second)
//
// fmt.Println("等待3秒...")
//
// // 阻塞直到定时器触发
// <-timer.C
// fmt.Println("3秒时间到!", time.Now().Format("15:04:05"))
//
// // 定时器重置
// fmt.Println("\n重置定时器为2秒...")
// timer.Reset(2 * time.Second)
// <-timer.C
// fmt.Println("2秒时间到!", time.Now().Format("15:04:05"))
//
// // 停止定时器示例
// timer2 := time.NewTimer(5 * time.Second)
// go func() {
// time.Sleep(2 * time.Second)
// if timer2.Stop() {
// fmt.Println("定时器在触发前被停止了")
// }
// }()
//
// select {
// case <-timer2.C:
// fmt.Println("定时器正常触发")
// case <-time.After(3 * time.Second):
// fmt.Println("检查完成")
// }
//}
// 延迟任务
type DelayedTask struct {
ExecuteAt time.Time
Task func()
Index int // heap需要的索引
}
// 延迟队列
type DelayQueue struct {
tasks []*DelayedTask
mu sync.Mutex
cond *sync.Cond
done chan struct{}
}
func NewDelayQueue() *DelayQueue {
dq := &DelayQueue{
tasks: make([]*DelayedTask, 0),
done: make(chan struct{}),
}
dq.cond = sync.NewCond(&dq.mu)
return dq
}
// heap接口实现
func (dq *DelayQueue) Len() int { return len(dq.tasks) }
func (dq *DelayQueue) Less(i, j int) bool {
return dq.tasks[i].ExecuteAt.Before(dq.tasks[j].ExecuteAt)
}
func (dq *DelayQueue) Swap(i, j int) {
dq.tasks[i], dq.tasks[j] = dq.tasks[j], dq.tasks[i]
dq.tasks[i].Index = i
dq.tasks[j].Index = j
}
func (dq *DelayQueue) Push(x interface{}) {
task := x.(*DelayedTask)
task.Index = len(dq.tasks)
dq.tasks = append(dq.tasks, task)
}
func (dq *DelayQueue) Pop() interface{} {
n := len(dq.tasks)
task := dq.tasks[n-1]
dq.tasks[n-1] = nil // 避免内存泄漏
dq.tasks = dq.tasks[:n-1]
return task
}
// 添加延迟任务
func (dq *DelayQueue) AddTask(delay time.Duration, task func()) {
dq.mu.Lock()
defer dq.mu.Unlock()
heap.Push(dq, &DelayedTask{
ExecuteAt: time.Now().Add(delay),
Task: task,
})
// 通知等待的 goroutine 有新的任务
dq.cond.Signal()
}
// 停止队列处理器
func (dq *DelayQueue) Stop() {
close(dq.done)
dq.cond.Broadcast()
}
// 启动队列处理器
func (dq *DelayQueue) Start() {
go func() {
for {
select {
case <-dq.done:
return
default:
dq.processTasks()
}
}
}()
}
func (dq *DelayQueue) processTasks() {
dq.mu.Lock()
// 等待直到有任务
for len(dq.tasks) == 0 {
dq.cond.Wait()
// 检查是否停止
select {
case <-dq.done:
dq.mu.Unlock()
return
default:
}
}
now := time.Now()
task := dq.tasks[0] // 查看堆顶元素
if now.Before(task.ExecuteAt) {
// 还未到执行时间,等待
waitDuration := task.ExecuteAt.Sub(now)
// 使用定时器等待
timer := time.NewTimer(waitDuration)
// 释放锁并等待
dq.mu.Unlock()
select {
case <-timer.C:
// 时间到了,重新获取锁处理任务
dq.mu.Lock()
// 再次检查任务是否还在(可能被其他操作移除)
if len(dq.tasks) > 0 && dq.tasks[0] == task {
heap.Pop(dq)
dq.mu.Unlock()
task.Task()
} else {
dq.mu.Unlock()
}
return
case <-dq.done:
timer.Stop()
return
}
}
// 立即执行任务
heap.Pop(dq)
dq.mu.Unlock()
// 执行任务
task.Task()
}
func main() {
queue := NewDelayQueue()
queue.Start()
fmt.Println("延迟队列启动,当前时间:", time.Now().Format("15:04:05"))
// 添加延迟任务
queue.AddTask(2*time.Second, func() {
fmt.Printf("[延迟2秒] 执行时间: %v\n", time.Now().Format("15:04:05"))
})
queue.AddTask(5*time.Second, func() {
fmt.Printf("[延迟5秒] 执行时间: %v\n", time.Now().Format("15:04:05"))
})
queue.AddTask(1*time.Second, func() {
fmt.Printf("[延迟1秒] 执行时间: %v\n", time.Now().Format("15:04:05"))
})
queue.AddTask(3*time.Second, func() {
fmt.Printf("[延迟3秒] 执行时间: %v\n", time.Now().Format("15:04:05"))
})
// 等待所有任务执行
time.Sleep(10 * time.Second)
// 停止队列
queue.Stop()
fmt.Println("程序结束")
}

2210
walkingWithBooks.go Normal file

File diff suppressed because it is too large Load Diff