更新修改功能
This commit is contained in:
parent
909313e2b5
commit
fa1b388c8d
271
config/config.go
Normal file
271
config/config.go
Normal 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
24
config/config.json
Normal 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
27
config/configDll.go
Normal 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
BIN
config/dll/config.dll
Normal file
Binary file not shown.
105
config/dll/config.h
Normal file
105
config/dll/config.h
Normal 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
BIN
csv/csv.dll
Normal file
Binary file not shown.
243
csv/csv.go
243
csv/csv.go
@ -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 {
|
||||
return -1, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
@ -766,7 +766,7 @@ func (mgr *CSVManager) openFile(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 {
|
||||
return err
|
||||
}
|
||||
@ -794,7 +794,7 @@ func (mgr *CSVManager) openFileNormal(handle *CSVHandle) 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 {
|
||||
return err
|
||||
}
|
||||
@ -1023,6 +1023,83 @@ func (mgr *CSVManager) WriteRows(handleID int64, rows [][]string) (int64, error)
|
||||
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 记录行数据到日志文件
|
||||
func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error {
|
||||
// 获取句柄信息(但不锁定,因为我们只是读取元数据)
|
||||
@ -1077,6 +1154,61 @@ func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error {
|
||||
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 将缓冲区数据写入文件
|
||||
func (mgr *CSVManager) Flush(handleID int64) error {
|
||||
// 获取句柄
|
||||
@ -1135,6 +1267,24 @@ func (mgr *CSVManager) AppendRows(handleID int64, rows [][]string) (int64, error
|
||||
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文件表头
|
||||
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)
|
||||
}
|
||||
|
||||
// 保存当前文件位置
|
||||
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 {
|
||||
return nil, fmt.Errorf("获取当前文件位置失败: %w", err)
|
||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 重置到文件开始
|
||||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||||
return nil, fmt.Errorf("重置文件指针失败: %w", err)
|
||||
}
|
||||
|
||||
// 重置CSV阅读器
|
||||
reader := csv.NewReader(bufio.NewReader(handle.File))
|
||||
// 创建CSV阅读器
|
||||
reader := csv.NewReader(file)
|
||||
reader.Comma = handle.Delimiter
|
||||
reader.LazyQuotes = 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 targetRow []string
|
||||
@ -1496,7 +1659,7 @@ func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error)
|
||||
break
|
||||
}
|
||||
// 恢复文件位置
|
||||
handle.File.Seek(currentPos, 0)
|
||||
//handle.File.Seek(currentPos, 0)
|
||||
return nil, fmt.Errorf("读取行失败: %w", err)
|
||||
}
|
||||
|
||||
@ -1517,10 +1680,10 @@ func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error)
|
||||
currentRow++
|
||||
}
|
||||
|
||||
// 恢复文件位置
|
||||
if _, err := handle.File.Seek(currentPos, 0); err != nil {
|
||||
return nil, fmt.Errorf("恢复文件位置失败: %w", err)
|
||||
}
|
||||
//// 恢复文件位置
|
||||
//if _, err := handle.File.Seek(currentPos, 0); err != nil {
|
||||
// return nil, fmt.Errorf("恢复文件位置失败: %w", err)
|
||||
//}
|
||||
|
||||
if targetRow == nil {
|
||||
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))
|
||||
}
|
||||
|
||||
// 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导出函数 - 修改指定行数据
|
||||
//
|
||||
//export UpdateRow
|
||||
@ -2085,5 +2288,5 @@ func FreeCString(str *C.char) {
|
||||
}
|
||||
|
||||
// 主函数
|
||||
func main() {
|
||||
}
|
||||
//func main() {
|
||||
//}
|
||||
|
||||
133
csv/csv.h
Normal file
133
csv/csv.h
Normal 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
|
||||
699
csv/csvDll.go
699
csv/csvDll.go
@ -1,10 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//
|
||||
@ -584,108 +582,39 @@ import (
|
||||
//}
|
||||
|
||||
// 主函数 - 测试代码
|
||||
func main() {
|
||||
fmt.Println("=== CSV句柄管理器测试 ===") //7984
|
||||
|
||||
filename := "csv/test1.csv"
|
||||
////dstFile := "csv/taskLog3.csv"
|
||||
//fmt.Printf("1. 创建测试文件: %s\n", filename)
|
||||
//func main() {
|
||||
// fmt.Println("=== CSV句柄管理器测试 ===") //7984
|
||||
//
|
||||
// 创建 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()
|
||||
|
||||
// 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)
|
||||
//
|
||||
//// 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)
|
||||
@ -693,151 +622,453 @@ func main() {
|
||||
//
|
||||
// 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("写入表头成功")
|
||||
// }
|
||||
// //// 测试写入表头
|
||||
// //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", fmt.Sprintf("张三", i), "25", "zhangsan@example.com"},
|
||||
// // {"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)
|
||||
// //}
|
||||
//
|
||||
// // 写入测试数据
|
||||
// rowsData := [][]string{
|
||||
// {fmt.Sprintf("%d", i+100), "张三", fmt.Sprintf("%d", i+1000), fmt.Sprintf("%d", i+10000)},
|
||||
// }
|
||||
// //// ============ 测试修改行功能 ============
|
||||
// //
|
||||
// //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. 获取修改后的行数据")
|
||||
//
|
||||
// totalRows, err := GetManager().WriteRows(handleID, rowsData)
|
||||
// for i := 0; i < 10000; i++ {
|
||||
// row1, err := GetManager().GetRow(handleID, int64(i))
|
||||
// if err != nil {
|
||||
// fmt.Printf("批量写入数据失败: %v\n", err)
|
||||
// fmt.Printf("获取第 %d 行失败: %v\n", i, err)
|
||||
// } else {
|
||||
// fmt.Printf("批量写入数据成功,行数: %d\n", totalRows)
|
||||
// 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)
|
||||
// //}
|
||||
//
|
||||
// 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"},
|
||||
//}
|
||||
// //// 获取第3行
|
||||
// //row3, err := GetManager().GetRow(handleID, 2)
|
||||
// //if err != nil {
|
||||
// // fmt.Printf("获取第3行失败: %v\n", err)
|
||||
// //} else {
|
||||
// // fmt.Printf("第3行数据: %v\n", row3)
|
||||
// //}
|
||||
//
|
||||
//totalRows, err := GetManager().WriteRows(handleID, rowsData)
|
||||
//if err != nil {
|
||||
// fmt.Printf("批量写入数据失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("批量写入数据成功,行数: %d\n", totalRows)
|
||||
//}
|
||||
// //// ============ 测试批量修改功能 ============
|
||||
// //
|
||||
// //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)
|
||||
// //}
|
||||
//
|
||||
//// 获取写入后的总行数
|
||||
//totalRows, err = GetManager().GetTotalRows(handleID)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取总行数失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("写入后总行数: %d\n", totalRows)
|
||||
//}
|
||||
|
||||
//// ============ 测试修改行功能 ============
|
||||
//
|
||||
//fmt.Println("\n3. 测试修改行功能")
|
||||
//
|
||||
//// 修改第1行数据(索引0)
|
||||
//newRow1 := []string{"1", "张三(已修改1)", "26", "zhangsan_updated@example.com"}
|
||||
//err = GetManager().UpdateRow(handleID, 0, newRow1)
|
||||
//if err != nil {
|
||||
// fmt.Printf("修改第1行数据失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Println("修改第1行数据成功")
|
||||
//}
|
||||
//
|
||||
//// 修改第3行数据(索引2)
|
||||
//newRow3 := []string{"3", "王五(已修改)", "29", "wangwu_updated@example.com"}
|
||||
//err = GetManager().UpdateRow(handleID, 2, newRow3)
|
||||
//if err != nil {
|
||||
// fmt.Printf("修改第3行数据失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Println("修改第3行数据成功")
|
||||
//}
|
||||
//
|
||||
//// 获取修改后的行数据
|
||||
//fmt.Println("\n4. 获取修改后的行数据")
|
||||
|
||||
//获取第1行
|
||||
//row1, err := GetManager().GetRow(handleID, 10)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取第1行失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("第1行数据: %v\n", row1)
|
||||
//}
|
||||
|
||||
//// 获取第3行
|
||||
//row3, err := GetManager().GetRow(handleID, 2)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取第3行失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("第3行数据: %v\n", row3)
|
||||
//}
|
||||
|
||||
//// ============ 测试批量修改功能 ============
|
||||
//
|
||||
//fmt.Println("\n5. 测试批量修改功能")
|
||||
//
|
||||
//updates := map[int64][]string{
|
||||
// 1: {"2", "李四(批量修改)", "31", "lisi_batch@example.com"},
|
||||
// 3: {"4", "赵六(批量修改)", "36", "zhaoliu_batch@example.com"},
|
||||
//}
|
||||
//
|
||||
//updatedCount, err := GetManager().UpdateRows(handleID, updates)
|
||||
//if err != nil {
|
||||
// fmt.Printf("批量修改失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("批量修改成功,修改了%d行\n", updatedCount)
|
||||
//}
|
||||
//
|
||||
//// 验证批量修改结果
|
||||
//row2, err := GetManager().GetRow(handleID, 1)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取第2行失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("第2行数据(批量修改后): %v\n", row2)
|
||||
//}
|
||||
//
|
||||
//row4, err := GetManager().GetRow(handleID, 3)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取第4行失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("第4行数据(批量修改后): %v\n", row4)
|
||||
//}
|
||||
//
|
||||
//// 获取最终总行数
|
||||
//finalTotalRows, err := GetManager().GetTotalRows(handleID)
|
||||
//if err != nil {
|
||||
// fmt.Printf("获取最终总行数失败: %v\n", err)
|
||||
//} else {
|
||||
// fmt.Printf("最终总行数: %d\n", finalTotalRows)
|
||||
//}
|
||||
|
||||
// 清理
|
||||
// // 清理
|
||||
// GetManager().closeAllHandles()
|
||||
fmt.Println("\n测试完成!")
|
||||
// fmt.Println("\n测试完成!")
|
||||
//}
|
||||
|
||||
func main() {
|
||||
filename := "csv/taskLog.csv"
|
||||
handleID, err := GetManager().OpenCSVFile(filename, ',', true)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
fmt.Println(handleID)
|
||||
headers := []string{"ID", "序号", "姓名", "年龄"}
|
||||
err = GetManager().WriteHeader(handleID, headers)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
//rowss, i, err := GetManager().AppendRowss(handleID, rows)
|
||||
//if err != nil {
|
||||
// fmt.Println(err.Error())
|
||||
//}
|
||||
//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)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
//rowsss, err := GetManager().AppendRowsss(handleID, rows)
|
||||
//if err != nil {
|
||||
// fmt.Println(err.Error())
|
||||
//}
|
||||
//fmt.Println(rowsss)
|
||||
|
||||
GetManager().CloseHandle(handleID)
|
||||
|
||||
}
|
||||
|
||||
// WriteRowss 批量写入多行数据到CSV文件
|
||||
// 返回写入的起始行号和写入的行数,错误时返回(-1, -1, error)
|
||||
func (mgr *CSVManager) WriteRowss(handleID int64, rows [][]string) (int64, int64, error) {
|
||||
// 首先记录日志
|
||||
if err := mgr.logWriteRows(handleID, rows); err != nil {
|
||||
// 日志记录失败不影响主流程,但可以打印警告
|
||||
fmt.Printf("警告:记录日志失败: %v\n", err)
|
||||
}
|
||||
|
||||
// 获取句柄
|
||||
handle, err := mgr.getHandle(handleID)
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("WriteRows 获取句柄失败: %w", err)
|
||||
}
|
||||
defer mgr.releaseHandle(handleID)
|
||||
|
||||
// 检查句柄状态
|
||||
if !handle.beginOperation() {
|
||||
return -1, -1, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||||
}
|
||||
defer handle.endOperation()
|
||||
|
||||
handle.mu.Lock()
|
||||
defer handle.mu.Unlock()
|
||||
|
||||
if !handle.IsOpen {
|
||||
return -1, -1, fmt.Errorf("文件未打开")
|
||||
}
|
||||
|
||||
// 检查是否有表头
|
||||
if handle.HasHeader && len(handle.Header) > 0 {
|
||||
// 验证每行的列数
|
||||
for i, row := range rows {
|
||||
if len(row) != len(handle.Header) {
|
||||
return -1, -1, fmt.Errorf("第%d行列数(%d)与表头列数(%d)不匹配", i+1, len(row), len(handle.Header))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移动到文件末尾
|
||||
if _, err := handle.File.Seek(0, 2); err != nil {
|
||||
return -1, -1, fmt.Errorf("移动到文件末尾失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录写入前的行数作为起始行号
|
||||
// 注意:行号从1开始,如果是首次写入且没有表头,TotalRows=0,起始行号就是1
|
||||
startRow := handle.TotalRows + 1
|
||||
if handle.HasHeader && startRow == 1 {
|
||||
// 如果有表头,数据从第2行开始
|
||||
startRow = 2
|
||||
}
|
||||
|
||||
// 批量写入行数据
|
||||
for _, row := range rows {
|
||||
if err := handle.CSVWriter.Write(row); err != nil {
|
||||
return -1, -1, fmt.Errorf("写入行失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新行数统计
|
||||
rowsWritten := int64(len(rows))
|
||||
handle.TotalRows += rowsWritten
|
||||
handle.cachedRowCount = handle.TotalRows
|
||||
handle.rowCountCached = true
|
||||
|
||||
return startRow, rowsWritten, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
err = mgr.Flush(handleID)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
return totalRows, rowss, nil
|
||||
}
|
||||
|
||||
// AppendRows 批量追加行数据(自动Flush)
|
||||
func (mgr *CSVManager) AppendRowsss(handleID int64, rows [][]string) ([]int64, error) {
|
||||
rowsss, err := mgr.WriteRowsss(handleID, rows)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = mgr.Flush(handleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rowsss, nil
|
||||
}
|
||||
|
||||
// WriteRows 批量写入多行数据到CSV文件
|
||||
// 返回每行数据存储的行号数组(从1开始)
|
||||
func (mgr *CSVManager) WriteRowsss(handleID int64, rows [][]string) ([]int64, error) {
|
||||
// 首先记录日志
|
||||
if err := mgr.logWriteRows(handleID, rows); err != nil {
|
||||
// 日志记录失败不影响主流程,但可以打印警告
|
||||
fmt.Printf("警告:记录日志失败: %v\n", err)
|
||||
}
|
||||
|
||||
// 获取句柄
|
||||
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
|
||||
}
|
||||
|
||||
BIN
csv/dll/csv.dll
BIN
csv/dll/csv.dll
Binary file not shown.
@ -134,6 +134,10 @@ extern __declspec(dllexport) int GetError(char* buffer, int bufferSize);
|
||||
//
|
||||
extern __declspec(dllexport) void FreeCString(char* str);
|
||||
|
||||
// 获取版本信息
|
||||
//
|
||||
extern __declspec(dllexport) char* GetVersion(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package main
|
||||
|
||||
//
|
||||
///*
|
||||
//#include <stdlib.h>
|
||||
//*/
|
||||
@ -1256,39 +1255,18 @@ package main
|
||||
// C.free(unsafe.Pointer(str))
|
||||
//}
|
||||
//
|
||||
//// CSV_VERSION 版本号
|
||||
//const (
|
||||
// CSV_VERSION = "v1"
|
||||
//)
|
||||
//
|
||||
//// 获取版本信息
|
||||
////
|
||||
////export GetVersion
|
||||
//func GetVersion() *C.char {
|
||||
// return C.CString(CSV_VERSION)
|
||||
//}
|
||||
//
|
||||
//// 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
101
csv/taskLog.csv
Normal 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
|
||||
|
BIN
erp/dll/erp.dll
Normal file
BIN
erp/dll/erp.dll
Normal file
Binary file not shown.
101
erp/dll/erp.h
Normal file
101
erp/dll/erp.h
Normal 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
|
||||
BIN
expressDeliveryOrder/dll/expressDeliveryOrder.dll
Normal file
BIN
expressDeliveryOrder/dll/expressDeliveryOrder.dll
Normal file
Binary file not shown.
125
expressDeliveryOrder/dll/expressDeliveryOrder.h
Normal file
125
expressDeliveryOrder/dll/expressDeliveryOrder.h
Normal 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
|
||||
1293
expressDeliveryOrder/expressDeliveryOrder.go
Normal file
1293
expressDeliveryOrder/expressDeliveryOrder.go
Normal file
File diff suppressed because it is too large
Load Diff
20
expressDeliveryOrder/expressDeliveryOrderTest.go
Normal file
20
expressDeliveryOrder/expressDeliveryOrderTest.go
Normal 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)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package gin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
27
go.mod
27
go.mod
@ -3,6 +3,7 @@ module kfzgw-info
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
gioui.org v0.9.0
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/boombuler/barcode v1.1.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
@ -14,21 +15,24 @@ require (
|
||||
github.com/makiuchi-d/gozxing v0.1.1
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
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/tjfoc/gmsm v1.4.1
|
||||
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
|
||||
gopkg.in/ini.v1 v1.67.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.44.3
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
gioui.org/shader v1.0.8 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.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/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/elazarl/goproxy v1.7.2 // 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/universal-translator v0.18.1 // 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-yaml v1.18.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // 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/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // 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/pkg/errors v0.9.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // 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/msoleps v1.0.4 // indirect
|
||||
github.com/smartystreets/goconvey v1.8.1 // indirect
|
||||
@ -65,11 +74,17 @@ require (
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/arch v0.20.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/sync v0.17.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
|
||||
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
156
go.sum
@ -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/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/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/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/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/go.mod h1:F3j9e+BubmKvzvLjNui/1++nJuJxbkhHefbaT0kFKGY=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
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/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
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-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-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/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/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
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/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/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/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
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/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
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/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||
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/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
|
||||
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/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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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/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/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
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/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
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/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/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
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.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.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.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.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/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
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/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/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
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/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-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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
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.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
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.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
|
||||
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
|
||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||
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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
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-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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
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-20220722155255-886fb9371eb4/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.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
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-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-20210615035016-665e8c7367d1/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/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-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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
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-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/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/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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
image/123.png
Normal file
BIN
image/123.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 544 KiB |
866
kfztp.go
866
kfztp.go
@ -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.
@ -172,6 +172,10 @@ extern __declspec(dllexport) char* Initialize(char* configJSON);
|
||||
//
|
||||
extern __declspec(dllexport) void FreeCString(char* str);
|
||||
|
||||
// 获取版本信息
|
||||
//
|
||||
extern __declspec(dllexport) char* GetVersion(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Binary file not shown.
416
kongfz/kongfz.go
416
kongfz/kongfz.go
@ -84,7 +84,9 @@ type BookInfo struct {
|
||||
Category string `json:"category"` // 分类
|
||||
BuyCount string `json:"buy_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
|
||||
ItemId int64 `json:"item_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.BookPic = list.ImgUrlEntity.BigImgUrl
|
||||
bookInfo.ISBN = list.Isbn
|
||||
bookInfo.Mid = list.Mid
|
||||
// 根据长度安全填充字段
|
||||
if isReturnMsg == 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).
|
||||
Proxy(proxy).
|
||||
Set("Cookie", token).
|
||||
Set("Cookie", fmt.Sprintf("PHPSESSID=%s", token)).
|
||||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36").
|
||||
Set("Accept", "application/json, text/plain, */*").
|
||||
Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8").
|
||||
@ -1425,6 +1427,265 @@ func outGetImageByIsbn(token string, proxy string, isbn string, isLiveImage int,
|
||||
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, " ", " ")
|
||||
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, " ", " ")
|
||||
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, " ", " ")
|
||||
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
|
||||
* 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,可选值: 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,
|
||||
pageNum, returnNum int) (books []BookInfo, goodsNum string, pNum string, err error) {
|
||||
// 判断店铺ID
|
||||
@ -1488,7 +1749,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
|
||||
return nil, "", "", fmt.Errorf("无效的排序类型: %s,可选值: desc, asc", sort)
|
||||
}
|
||||
}
|
||||
var url string
|
||||
var kfzUrl string
|
||||
var pMin int
|
||||
pMin = 0
|
||||
var pMax int
|
||||
@ -1496,35 +1757,111 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
|
||||
// 判断价格下限,设置默认值0
|
||||
if priceMin == 0 && priceMax == 0 {
|
||||
// 调用的url
|
||||
url = fmt.Sprintf("https://shop.kongfz.com/%d/all/%s_%d_0_0_%d_%s_%s_%d_%d",
|
||||
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)
|
||||
} 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)
|
||||
} 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)
|
||||
} 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)
|
||||
}
|
||||
// 发送请求
|
||||
response, err := fetchResponse(url, proxy)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
|
||||
// 发送请求(移除了重试机制)
|
||||
var response *http.Response
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
// 解析HTML文档
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
// 全部商品数量
|
||||
num := doc.Find("div.crumbs-nav-main.clearfix").Find("span")
|
||||
if match := regexp.MustCompile(`\d+`).FindString(num.Text()); match != "" {
|
||||
goodsNum = match
|
||||
}
|
||||
|
||||
// 商品页数
|
||||
pg := doc.Find("li.pull-right.page_num").Find("span")
|
||||
_, split, found := strings.Cut(strings.TrimSpace(pg.Text()), "/")
|
||||
@ -1533,6 +1870,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
|
||||
} else {
|
||||
log.Printf("未找到页数!")
|
||||
}
|
||||
|
||||
// 提取商品信息
|
||||
infoDiv := doc.Find("div.list-content")
|
||||
var params ParamsInfo
|
||||
@ -1568,6 +1906,7 @@ func outGetGoodsListMsgByShopId(shopId int, proxy string, retPrice int, isImage
|
||||
params.Area = "13003000000"
|
||||
dataItem, err := getGoodsListShippingFee(params, proxy)
|
||||
if err != nil {
|
||||
fmt.Println("查询快递费失败")
|
||||
return nil, "", "", err
|
||||
}
|
||||
// 将快递费信息填充到图书信息中
|
||||
@ -3132,10 +3471,45 @@ func OutGetImageByIsbn(token, proxy, isbn *C.char, isLiveImage C.int, isReturnMs
|
||||
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
|
||||
//
|
||||
//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)
|
||||
goProxy := C.GoString(proxy)
|
||||
goRetPrice := int(retPrice)
|
||||
@ -3146,7 +3520,7 @@ func OutGetGoodsListMsgByShopId(shopId C.int, proxy *C.char, retPrice C.int, isI
|
||||
goPriceMax := float32(priceMax)
|
||||
goPageNum := int(pageNum)
|
||||
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)
|
||||
// 构建统一格式的响应
|
||||
bookInfo := struct {
|
||||
@ -3397,6 +3771,18 @@ func FreeCString(str *C.char) {
|
||||
C.free(unsafe.Pointer(str))
|
||||
}
|
||||
|
||||
// CSV_VERSION 版本号
|
||||
const (
|
||||
CSV_VERSION = "v3"
|
||||
)
|
||||
|
||||
// 获取版本信息
|
||||
//
|
||||
//export GetVersion
|
||||
func GetVersion() *C.char {
|
||||
return C.CString(CSV_VERSION)
|
||||
}
|
||||
|
||||
// 空main函数,编译DLL时需要
|
||||
func main() {
|
||||
}
|
||||
|
||||
185
kongfz/kongfz.h
Normal file
185
kongfz/kongfz.h
Normal 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
|
||||
@ -1,109 +1,107 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
//upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb",
|
||||
// "D:/work/image/9787511220660.jpg",
|
||||
// "D:\\work\\project\\kfzgw-info\\kongfz")
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(upload)
|
||||
|
||||
//var q OrderQueryParams
|
||||
//q.UserType = "buyer"
|
||||
//marshal, err := json.Marshal(q)
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(string(marshal))
|
||||
//func main() {
|
||||
// //upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||
// // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb",
|
||||
// // "D:/work/image/9787511220660.jpg",
|
||||
// // "D:\\work\\project\\kfzgw-info\\kongfz")
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(upload)
|
||||
//
|
||||
//list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||
// "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal))
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(list)
|
||||
// //var q OrderQueryParams
|
||||
// //q.UserType = "buyer"
|
||||
// //marshal, err := json.Marshal(q)
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(string(marshal))
|
||||
// //
|
||||
// //list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
|
||||
// // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal))
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(list)
|
||||
// //
|
||||
// //key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "")
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(key)
|
||||
// //
|
||||
// //upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "",
|
||||
// // "D:/work/image/9787115460622.jpg")
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(upload)
|
||||
//
|
||||
//key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "")
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(key)
|
||||
// //uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "",
|
||||
// // "D:/work/image/9787511220660.jpg")
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(uploadd)
|
||||
//
|
||||
//upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "",
|
||||
// "D:/work/image/9787115460622.jpg")
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(upload)
|
||||
|
||||
//uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "",
|
||||
// "D:/work/image/9787511220660.jpg")
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(uploadd)
|
||||
|
||||
appId := 576
|
||||
appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8"
|
||||
accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92"
|
||||
shippingComName := "韵达快递"
|
||||
orderId := 275723461
|
||||
shippingId := ""
|
||||
shippingCom := ""
|
||||
shipmentNum := "312944151258647"
|
||||
userDefined := ""
|
||||
moreShipmentNum := ""
|
||||
|
||||
synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(synchronization)
|
||||
|
||||
//data := ItemInfo{
|
||||
// ItemName: "至关重要的关系",
|
||||
// ISBN: "9787550213869",
|
||||
// Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译",
|
||||
// Press: "北京联合出版公司",
|
||||
// YearsGroup: "1",
|
||||
// PubDate: "2013-04",
|
||||
// PubDateYear: "2013",
|
||||
// PubDateMonth: "4",
|
||||
// Edition: "1",
|
||||
// Binding: "2", // 2可能表示平装
|
||||
// Quality: "95",
|
||||
// Price: "1500",
|
||||
// Number: "2",
|
||||
// ItemSn: "a1",
|
||||
// MouldId: "909963",
|
||||
// DeliverTime: "48h",
|
||||
// IsDeliverTimeDefault: "48h",
|
||||
// CatId: "43000000000000000",
|
||||
// PageType: "add",
|
||||
// OldCatId: "0000000000000000000",
|
||||
// Tpl: "13",
|
||||
// BearShipping: "buyer",
|
||||
// WeightPiece: "1",
|
||||
// IsOnSale: "1",
|
||||
// Weight: "0.5",
|
||||
// IsUseMould: "1",
|
||||
// DeliverTimeGroup: "1",
|
||||
//}
|
||||
//marshal, err := json.Marshal(data)
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
// appId := 576
|
||||
// appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8"
|
||||
// accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92"
|
||||
// shippingComName := "韵达快递"
|
||||
// orderId := 275723461
|
||||
// shippingId := ""
|
||||
// shippingCom := ""
|
||||
// shipmentNum := "312944151258647"
|
||||
// userDefined := ""
|
||||
// moreShipmentNum := ""
|
||||
//
|
||||
//file, err := outAddGoodsAndFile("7feb7d192911b5440a4c83d81decd568e3cb4833", "", "D:/work/image/9787115460622.jpg", string(marshal))
|
||||
// synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//fmt.Println(string(file))
|
||||
}
|
||||
// fmt.Println(synchronization)
|
||||
//
|
||||
// //data := ItemInfo{
|
||||
// // ItemName: "至关重要的关系",
|
||||
// // ISBN: "9787550213869",
|
||||
// // Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译",
|
||||
// // Press: "北京联合出版公司",
|
||||
// // YearsGroup: "1",
|
||||
// // PubDate: "2013-04",
|
||||
// // PubDateYear: "2013",
|
||||
// // PubDateMonth: "4",
|
||||
// // Edition: "1",
|
||||
// // Binding: "2", // 2可能表示平装
|
||||
// // Quality: "95",
|
||||
// // Price: "1500",
|
||||
// // Number: "2",
|
||||
// // ItemSn: "a1",
|
||||
// // MouldId: "909963",
|
||||
// // DeliverTime: "48h",
|
||||
// // IsDeliverTimeDefault: "48h",
|
||||
// // CatId: "43000000000000000",
|
||||
// // PageType: "add",
|
||||
// // OldCatId: "0000000000000000000",
|
||||
// // Tpl: "13",
|
||||
// // BearShipping: "buyer",
|
||||
// // WeightPiece: "1",
|
||||
// // IsOnSale: "1",
|
||||
// // Weight: "0.5",
|
||||
// // IsUseMould: "1",
|
||||
// // DeliverTimeGroup: "1",
|
||||
// //}
|
||||
// //marshal, err := json.Marshal(data)
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //
|
||||
// //file, err := outAddGoodsAndFile("7feb7d192911b5440a4c83d81decd568e3cb4833", "", "D:/work/image/9787115460622.jpg", string(marshal))
|
||||
// //if err != nil {
|
||||
// // fmt.Println(err)
|
||||
// //}
|
||||
// //fmt.Println(string(file))
|
||||
//}
|
||||
|
||||
type ItemInfo struct {
|
||||
ItemName string `json:"itemName"`
|
||||
|
||||
71
listener/listener.go
Normal file
71
listener/listener.go
Normal 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
BIN
logger/dll/logger.dll
Normal file
Binary file not shown.
107
logger/dll/logger.h
Normal file
107
logger/dll/logger.h
Normal 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
1053
logger/logger.go
Normal file
File diff suppressed because it is too large
Load Diff
840
logger/loggerDll.go
Normal file
840
logger/loggerDll.go
Normal 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个logger,3个活跃,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小时未使用)")
|
||||
}
|
||||
156
md/config.md
Normal file
156
md/config.md
Normal 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
739
md/expressDeliveryOrder.md
Normal 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中fromAppkey,EMS类型的时候可以不填,其他类型必填 |
|
||||
| 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
322
md/logger.md
Normal 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=SUCCESS,1=INFO,2=WARNING,3=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
362
md/pdd.md
@ -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-100,0表示取消,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(全量更新)时有效,默认值false;force_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版,作者:'邓文英,宋力宏主编',ISBN:9787040456295,出版社:高等教育出版社",
|
||||
"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
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
|
||||
BIN
pdd/dll/pdd.dll
BIN
pdd/dll/pdd.dll
Binary file not shown.
@ -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);
|
||||
|
||||
// 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字符串内存
|
||||
//
|
||||
extern __declspec(dllexport) void FreeCString(char* str);
|
||||
|
||||
BIN
pdd/pdd.dll
Normal file
BIN
pdd/pdd.dll
Normal file
Binary file not shown.
1022
pdd/pdd.go
1022
pdd/pdd.go
File diff suppressed because it is too large
Load Diff
177
pdd/pdd.h
Normal file
177
pdd/pdd.h
Normal 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
|
||||
@ -134,7 +134,28 @@ func (m *pddDLL) PddErpOrderSync(clientId, clientSecret, accessToken, logisticsI
|
||||
return m.cStr(info), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
//func main() {
|
||||
//// 获取商品类目
|
||||
//info, err := outPddAuthGetCats()
|
||||
//if err != nil {
|
||||
// fmt.Printf(err.Error())
|
||||
//}
|
||||
//fmt.Println(info)
|
||||
|
||||
//// 生成自定义规格
|
||||
//info, err := outPddAuthSetSpec(3820, "全新", "e7e8a719b9b74378980914c9ca9e4a3e57a6a0c8")
|
||||
//if err != nil {
|
||||
// fmt.Printf(err.Error())
|
||||
//}
|
||||
//fmt.Println(info)
|
||||
|
||||
//// 获取商品详情信息
|
||||
//info, err := outPddAuthGetGoodsDetail("770621582375", "e7e8a719b9b74378980914c9ca9e4a3e57a6a0c8")
|
||||
//if err != nil {
|
||||
// fmt.Printf(err.Error())
|
||||
//}
|
||||
//fmt.Println(info)
|
||||
|
||||
//clientId := "203c5a7ba8bd4b8488d5e26f93052642"
|
||||
//clientSecret := "892ffaa86e12b7a3d8d2942b669d9aa520ad8179"
|
||||
//accessToken := "bd96218bb2a146779701506dc1e5e5c478692539"
|
||||
@ -237,9 +258,20 @@ func main() {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//fmt.Println(string(synchronization))
|
||||
}
|
||||
//}
|
||||
|
||||
type DataList struct {
|
||||
DataTag string `json:"data_tag"`
|
||||
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
128
pddTask/common/pddDll.go
Normal 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
BIN
pddTask/dll/pdd.dll
Normal file
Binary file not shown.
489
pddTask/pdd-task.go
Normal file
489
pddTask/pdd-task.go
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -120,6 +120,10 @@ extern __declspec(dllexport) char* InitProxyManager(char* serversJson, char* use
|
||||
//
|
||||
extern __declspec(dllexport) void FreeCString(char* str);
|
||||
|
||||
// GetVersion 获取版本
|
||||
//
|
||||
extern __declspec(dllexport) char* GetVersion(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
BIN
proxy/proxy.dll
Normal file
BIN
proxy/proxy.dll
Normal file
Binary file not shown.
@ -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
|
||||
* param username[string] 代理用户名
|
||||
@ -607,12 +624,30 @@ func ProxyTypeManager(proxyType, username, password, machineCode *C.char) *C.cha
|
||||
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 导出函数:查询机器码
|
||||
//
|
||||
//export GetMachineCode
|
||||
func GetMachineCode(tailCardSecret *C.char) *C.char {
|
||||
goTailCardSecret := C.GoString(tailCardSecret)
|
||||
log.Printf("[DEBUG] 查询机器码调用: card=%s", goTailCardSecret)
|
||||
|
||||
resp, err := getMachineCode(goTailCardSecret)
|
||||
if err != nil {
|
||||
@ -629,7 +664,6 @@ func GetMachineCode(tailCardSecret *C.char) *C.char {
|
||||
return C.CString(errorMsg)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] 查询机器码成功: code=%d, machine_code=%s", resp.Code, resp.Data.MachineCode)
|
||||
return C.CString(string(jsonData))
|
||||
}
|
||||
|
||||
@ -703,7 +737,6 @@ func GetProxies(machineCode *C.char) *C.char {
|
||||
//export CheckTailCardSecretExpired
|
||||
func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
|
||||
goTailCardSecret := C.GoString(tailCardSecret)
|
||||
log.Printf("[DEBUG] 检查卡密是否过期调用: card=%s", goTailCardSecret)
|
||||
|
||||
isValid, err := checkTailCardSecretExpired(goTailCardSecret)
|
||||
if err != nil {
|
||||
@ -732,8 +765,6 @@ func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
|
||||
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
|
||||
return C.CString(errorMsg)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] 检查卡密过期结果: is_valid=%v", isValid)
|
||||
return C.CString(string(jsonData))
|
||||
}
|
||||
|
||||
@ -795,6 +826,18 @@ func FreeCString(str *C.char) {
|
||||
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时需要
|
||||
func main() {
|
||||
}
|
||||
|
||||
133
proxy/proxy.h
Normal file
133
proxy/proxy.h
Normal 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
|
||||
@ -1,5 +1,7 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
///*
|
||||
//#cgo LDFLAGS: -ldl
|
||||
//
|
||||
@ -697,3 +699,11 @@ package main
|
||||
//
|
||||
//func main() {
|
||||
//}
|
||||
|
||||
func main() {
|
||||
managerNew, err := proxyTypeManagerNew("", "", "", "07f4d0fbcff99966c2b37b0c1fb7f01c")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(managerNew)
|
||||
}
|
||||
|
||||
342
sqlite/sqLite.go
Normal file
342
sqlite/sqLite.go
Normal 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"))
|
||||
}
|
||||
252
timer/timer.go
Normal file
252
timer/timer.go
Normal 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
2210
walkingWithBooks.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user