各种修改

This commit is contained in:
97694732@qq.com 2026-06-11 13:21:55 +08:00
parent fa1b388c8d
commit ac2a39742d
40 changed files with 19786 additions and 6333 deletions

4572
csv/csv.go

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"sync"
) )
// //
@ -848,53 +847,53 @@ import (
// fmt.Println("\n测试完成") // fmt.Println("\n测试完成")
//} //}
func main() { //func main() {
filename := "csv/taskLog.csv" // filename := "csv/taskLog.csv"
handleID, err := GetManager().OpenCSVFile(filename, ',', true) // handleID, err := GetManager().OpenCSVFile(filename, ',', true)
if err != nil { // if err != nil {
fmt.Println(err.Error()) // fmt.Println(err.Error())
} // }
fmt.Println(handleID) // fmt.Println(handleID)
headers := []string{"ID", "序号", "姓名", "年龄"} // headers := []string{"ID", "序号", "姓名", "年龄"}
err = GetManager().WriteHeader(handleID, headers) // err = GetManager().WriteHeader(handleID, headers)
if err != nil { // if err != nil {
fmt.Println(err.Error()) // fmt.Println(err.Error())
} // }
//
//rowss, i, err := GetManager().AppendRowss(handleID, rows) // //rowss, i, err := GetManager().AppendRowss(handleID, rows)
//if err != nil { // //if err != nil {
// fmt.Println(err.Error()) // // fmt.Println(err.Error())
//} // //}
//fmt.Println(rowss) // //fmt.Println(rowss)
//fmt.Println(i) // //fmt.Println(i)
//
var wg sync.WaitGroup // var wg sync.WaitGroup
results := make([][]int64, 100) // results := make([][]int64, 100)
//
for i := 0; i < 100; i++ { // for i := 0; i < 100; i++ {
rows := [][]string{ // rows := [][]string{
{fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), fmt.Sprintf("张三%d", i), fmt.Sprintf("3%d", i)}, // {fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), fmt.Sprintf("张三%d", i), fmt.Sprintf("3%d", i)},
} // }
wg.Add(1) // wg.Add(1)
go func(idx int) { // go func(idx int) {
defer wg.Done() // defer wg.Done()
rowsss, _ := GetManager().AppendRowsNum(handleID, rows) // rowsss, _ := GetManager().AppendRowsNum(handleID, rows)
fmt.Println(rows, ":", rowsss) // fmt.Println(rows, ":", rowsss)
results[idx] = rowsss // results[idx] = rowsss
}(i) // }(i)
} // }
//
wg.Wait() // wg.Wait()
//
//rowsss, err := GetManager().AppendRowsss(handleID, rows) // //rowsss, err := GetManager().AppendRowsss(handleID, rows)
//if err != nil { // //if err != nil {
// fmt.Println(err.Error()) // // fmt.Println(err.Error())
//} // //}
//fmt.Println(rowsss) // //fmt.Println(rowsss)
//
GetManager().CloseHandle(handleID) // GetManager().CloseHandle(handleID)
//
} //}
// WriteRowss 批量写入多行数据到CSV文件 // WriteRowss 批量写入多行数据到CSV文件
// 返回写入的起始行号和写入的行数,错误时返回(-1, -1, error) // 返回写入的起始行号和写入的行数,错误时返回(-1, -1, error)

File diff suppressed because it is too large Load Diff

View File

@ -1,125 +0,0 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "expressDeliveryOrder.go"
#include <stdlib.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// ZtoOpenCreateOrder 中通快递--创建订单接口
//
extern __declspec(dllexport) char* ZtoOpenCreateOrder(char* requestJSON, char* appKey, char* appSecret);
// JtOrderAddOrder 极兔快递--创建订单接口
//
extern __declspec(dllexport) char* JtOrderAddOrder(char* requestJSON, char* apiAccount, char* privateKey);
// EmsAmpApiOpen 邮政快递--订单接入接口
//
extern __declspec(dllexport) char* EmsAmpApiOpen(char* requestJSON, char* secretKey);
// StoOmsExpressOrderCreate 申通快递--订单接入接口
//
extern __declspec(dllexport) char* StoOmsExpressOrderCreate(char* requestJSON, char* fromAppkey, char* secretKey, char* fromCode);
// YdCreateBmOrder 韵达快递--电子面单下单
//
extern __declspec(dllexport) char* YdCreateBmOrder(char* requestJSON, char* appKey, char* appSecret);
// YdBmGetPdfInfo 韵达快递--电子面单打印
//
extern __declspec(dllexport) char* YdBmGetPdfInfo(char* requestJSON, char* appKey, char* appSecret);
// IntegrationOrderCreate 整合所有快递--订单接口
//
extern __declspec(dllexport) char* IntegrationOrderCreate(char* orderType, char* requestJSON, char* key, char* secret, char* fromCode);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,86 @@
package main
func main() {
//apiName := "OMS_EXPRESS_ORDER_CREATE"
//content := "{\"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\"}"
//stock, err := billCodeQueryUserStock(apiName, content)
//if err != nil {
// println(err.Error())
// return
//}
//fmt.Println(string(stock))
}
// 构建请求数据
//func getStoHeader(apiName string, content string) map[string]string {
// secretKey := "2kLMr75pURbVh3FnWzY6SCr7SM6qsoO3"
// dataDigest := getStoSignature(content, secretKey)
// var param = map[string]string{
// "api_name": apiName,
// "content": content,
// "from_appkey": "CAKgwDceADOybmU",
// "from_code": "CAKgwDceADOybmU",
// "to_appkey": "sto_oms",
// "to_code": "sto_oms",
// "data_digest": dataDigest,
// }
// return param
//}
//
//func requestSto(apiName string, content string) ([]byte, error) {
// var url = "http://cloudinter-linkgatewaytest.sto.cn/gateway/link.do"
// //url = "https://cloudinter-linkgateway.sto.cn/gateway/link.do"
//
// // 获取请求参数
// params := getStoHeader(apiName, content)
//
// // 创建 multipart form-data 请求体
// body := &bytes.Buffer{}
// writer := multipart.NewWriter(body)
//
// // 添加所有参数到 form-data
// for key, value := range params {
// err := writer.WriteField(key, value)
// if err != nil {
// return nil, err
// }
// }
//
// // 关闭 writer 以完成 multipart 数据
// err := writer.Close()
// if err != nil {
// return nil, err
// }
//
// // 创建 HTTP 请求
// req, err := http.NewRequest("POST", url, body)
// if err != nil {
// return nil, err
// }
//
// // 设置 Content-Type 头,包含 boundary
// req.Header.Set("Content-Type", writer.FormDataContentType())
//
// // 发送请求
// client := &http.Client{}
// resp, err := client.Do(req)
// if err != nil {
// return nil, err
// }
// defer resp.Body.Close()
//
// // 读取响应
// responseBody, err := io.ReadAll(resp.Body)
// if err != nil {
// return nil, err
// }
//
// return responseBody, nil
//}
//
//// 生成签名
//func getStoSignature(content, secretKey string) string {
// text := content + secretKey
// hash := md5.Sum([]byte(text))
// return base64.StdEncoding.EncodeToString(hash[:])
//}

31
go.mod
View File

@ -1,11 +1,12 @@
module kfzgw-info module kfzgw-info
go 1.25 go 1.26
require ( require (
gioui.org v0.9.0 gioui.org v0.9.0
github.com/PuerkitoBio/goquery v1.10.3 github.com/PuerkitoBio/goquery v1.10.3
github.com/boombuler/barcode v1.1.0 github.com/boombuler/barcode v1.1.0
github.com/chromedp/chromedp v0.15.1
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/elastic/go-elasticsearch/v8 v8.19.0 github.com/elastic/go-elasticsearch/v8 v8.19.0
github.com/fogleman/gg v1.3.0 github.com/fogleman/gg v1.3.0
@ -15,11 +16,13 @@ require (
github.com/makiuchi-d/gozxing v0.1.1 github.com/makiuchi-d/gozxing v0.1.1
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/parnurzeal/gorequest v0.3.0 github.com/parnurzeal/gorequest v0.3.0
github.com/redis/go-redis/v9 v9.18.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tjfoc/gmsm v1.4.1 github.com/tjfoc/gmsm v1.4.1
github.com/valyala/fasthttp v1.69.0
github.com/xuri/excelize/v2 v2.10.0 github.com/xuri/excelize/v2 v2.10.0
golang.org/x/image v0.26.0 golang.org/x/image v0.26.0
golang.org/x/sys v0.37.0 golang.org/x/sys v0.42.0
gopkg.in/ini.v1 v1.67.1 gopkg.in/ini.v1 v1.67.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.44.3 modernc.org/sqlite v1.44.3
@ -28,25 +31,35 @@ require (
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
gioui.org/shader v1.0.8 // indirect gioui.org/shader v1.0.8 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elastic/elastic-transport-go/v8 v8.7.0 // indirect github.com/elastic/elastic-transport-go/v8 v8.7.0 // indirect
github.com/elazarl/goproxy v1.7.2 // indirect github.com/elazarl/goproxy v1.7.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-text/typesetting v0.3.0 // indirect github.com/go-text/typesetting v0.3.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
@ -66,21 +79,23 @@ require (
github.com/tiendc/go-deepcopy v1.7.1 // indirect github.com/tiendc/go-deepcopy v1.7.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect github.com/ugorji/go/codec v1.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/xuri/efp v0.0.1 // indirect github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.43.0 // indirect golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // 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/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.29.0 // indirect golang.org/x/mod v0.30.0 // indirect
golang.org/x/net v0.46.0 // indirect golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.17.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/text v0.30.0 // indirect golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.38.0 // indirect golang.org/x/tools v0.39.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.36.9 // indirect google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect

70
go.sum
View File

@ -11,15 +11,29 @@ 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo= github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc h1:wkN/LMi5vc60pBRWx6qpbk/aEvq3/ZVNpnMvsw8PVVU=
github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc/go.mod h1:cbyjALe67vDvlvdiG9369P8w5U2w6IshwtyD2f2Tvag=
github.com/chromedp/chromedp v0.15.1 h1:EJWiPm7BNqDqjYy6U0lTSL5wNH+iNt9GjC3a4gfjNyQ=
github.com/chromedp/chromedp v0.15.1/go.mod h1:CdTHtUqD/dqaFw/cvFWtTydoEQS44wLBuwbMR9EkOY4=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
@ -28,6 +42,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@ -49,6 +65,8 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@ -68,6 +86,12 @@ github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY= 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 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
@ -105,12 +129,16 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I= github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
@ -127,6 +155,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI= github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
@ -141,6 +171,8 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
@ -175,13 +207,21 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4= github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU= github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
@ -190,6 +230,8 @@ go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZ
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
@ -202,8 +244,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
@ -220,8 +262,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -237,8 +279,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -249,8 +291,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.19.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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -266,8 +308,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -286,8 +328,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -298,8 +340,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 KiB

Binary file not shown.

View File

@ -86,43 +86,17 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
extern "C" { extern "C" {
#endif #endif
extern char* ProcessImage(char* jsonConfig);
// =================== C 导出函数 ======================= extern char* CreateWhiteBottomCenteredImage(char* jsonConfig, int width, int height);
// 检测图片纯白占比 extern char* ResizeToHeightQuality(char* jsonConfig, int targetHeight);
// extern char* RemoveWhiteBorderAndPNG(char* jsonConfig);
extern __declspec(dllexport) char* ProcessImage(char* jsonConfig); extern char* ResizeWTToHeightQuality(char* jsonConfig, int dsWidth, int dsHeight);
extern char* CropImage(char* jsonConfig, int x, int y, int width, int height);
// 根据原始图片生成新的白底图片 extern char* CreateChineseTextImage(char* text, int width, int height, char* fontSize, char* outputPath);
// extern char* DrawChineseInfo(char* filePath, char* title, char* author, char* publisher, char* outputPath);
extern __declspec(dllexport) char* CreateWhiteBottomCenteredImage(char* jsonConfig, int width, int height); extern char* GenerateBarcode(char* barcodeType, char* content, char* filename);
extern char* AddWatermarkFromURLEx(char* jsonConfig);
// 根据高度生成等比例图片 extern void FreeCString(char* str);
//
extern __declspec(dllexport) char* ResizeToHeightQuality(char* jsonConfig, int targetHeight);
// 去掉白边并转PNG图片工具
//
extern __declspec(dllexport) char* RemoveWhiteBorderAndPNG(char* jsonConfig);
// ResizeWTToHeightQuality 图片缩放
//
extern __declspec(dllexport) char* ResizeWTToHeightQuality(char* jsonConfig, int dsWidth, int dsHeight);
// CropImage 图片裁切
//
extern __declspec(dllexport) char* CropImage(char* jsonConfig, int x, int y, int width, int height);
// CreateChineseTextImage 创建带中文字体的文本图片,支持超出部分显示...
//
extern __declspec(dllexport) char* CreateChineseTextImage(char* text, int width, int height, char* fontSize, char* outputPath);
// DrawChineseInfo 绘制书名,作者,出版社信息
//
extern __declspec(dllexport) char* DrawChineseInfo(char* filePath, char* title, char* author, char* publisher, char* outputPath);
// 导出函数释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -3,6 +3,8 @@ package main
// #include <stdlib.h> // #include <stdlib.h>
import "C" import "C"
import ( import (
"bytes"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/boombuler/barcode" "github.com/boombuler/barcode"
@ -31,7 +33,10 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time"
"unsafe" "unsafe"
"github.com/valyala/fasthttp"
) )
// Config 配置结构体 // Config 配置结构体
@ -250,11 +255,10 @@ func createWhiteBottomCenteredImage(config *Config, width, height int) (string,
// 设置背景颜色 // 设置背景颜色
var bgColor color.Color var bgColor color.Color
bgColor = color.RGBA{R: 0, G: 0, B: 0, A: 0} // 白色
bgColor = color.RGBA{255, 255, 255, 255} // 白色
// 填充透明背景 // 填充透明背景
draw.Draw(dst, dst.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src) draw.Draw(dst, dst.Bounds(), &image.Uniform{C: bgColor}, image.Point{}, draw.Src)
// 计算居中位置 // 计算居中位置
srcBounds := img.Bounds() srcBounds := img.Bounds()
@ -305,7 +309,10 @@ func resizeToHeightQuality(config *Config, targetHeight int) (string, error) {
// 保存图片到指定目录下 // 保存图片到指定目录下
filename := filepath.Base(config.FileName) filename := filepath.Base(config.FileName)
destPath := filepath.Join(config.OutputDir, config.EqualHeightDir, filename) destPath := filepath.Join(config.OutputDir, config.EqualHeightDir, filename)
saveImage(destPath, imageNew, format) err = saveImage(destPath, imageNew, format)
if err != nil {
return "", fmt.Errorf("保存图片失败: %v", err)
}
return destPath, nil return destPath, nil
} }
@ -1689,6 +1696,503 @@ func absDiff(a, b uint8) uint8 {
return b - a return b - a
} }
// =========================== 添加水印 ===============================
// WatermarkConfig 水印配置结构体
type WatermarkConfig struct {
SourceImageURL string // 源图片URL地址
WatermarkURL string // 水印图片URL地址
WatermarkBase64 string // 水印图片base64编码字符串新增优先使用
Opacity float64 // 不透明度 (0.0-1.0)
Position string // 位置: center, top-left, top-right, bottom-left, bottom-right, tile
TileSpacing int // 平铺时的间距
Scale float64 // 水印缩放比例 (0.0-1.0)
Rotation float64 // 旋转角度 (度数)
XOffset int // X轴偏移量AddWatermarkFromURLEx
YOffset int // Y轴偏移量
Timeout int // 下载超时时间默认30秒
OutputFormat string // 输出格式: "jpeg", "png", "auto"默认auto根据源图片格式
JPEGQuality int // JPEG质量 (1-100)默认95
TargetWidth int // 目标宽度0表示不缩放
TargetHeight int // 目标高度0表示不缩放
ResizeMode string // 缩放模式: "fit"(适应,保持比例,可能有黑边), "fill"(填充,裁剪), "stretch"(拉伸)
}
// 将客户端声明为全局变量或缓存
var httpClient = &fasthttp.Client{
MaxConnsPerHost: 100,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
}
// loadImageFromURL 直接从URL加载图片
func loadImageFromURL(url string, timeout int) (image.Image, string, error) {
// 设置默认超时
if timeout <= 0 {
timeout = 120
}
// 创建请求和响应对象
req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(resp)
// 设置请求URL和方法
req.SetRequestURI(url)
req.Header.SetMethod("GET")
// 设置请求头
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("If-Modified-Since", "Mon, 09 Mar 2026 07:43:59 GMT")
req.Header.Set("Priority", "u=0, i")
req.Header.Set("Sec-Ch-Ua", `"Not:A-Brand";v="99", "Microsoft Edge";v="145", "Chromium";v="145"`)
req.Header.Set("Sec-Ch-Ua-Mobile", "?0")
req.Header.Set("Sec-Ch-Ua-Platform", "Windows")
req.Header.Set("Sec-Fetch-Dest", "document")
req.Header.Set("Sec-Fetch-Mode", "navigate")
req.Header.Set("Sec-Fetch-Site", "none")
req.Header.Set("Sec-Fetch-User", "?1")
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0")
// 发送请求
err := httpClient.Do(req, resp)
if err != nil {
return nil, "", fmt.Errorf("请求失败: %v\n", err)
}
// 复制body数据避免引用问题
body := make([]byte, len(resp.Body()))
copy(body, resp.Body())
// 直接从响应体解码图片
img, format, err := image.Decode(bytes.NewReader(body))
// 主动释放body内存
body = nil
if err != nil {
return nil, "", fmt.Errorf("解码图片失败: %v", err)
}
return img, format, nil
}
// 添加新的辅助函数从base64字符串加载图片
func loadImageFromBase64(base64Str string) (image.Image, string, error) {
// 移除可能的 data:image/xxx;base64, 前缀
base64Str = strings.TrimPrefix(base64Str, "data:image/jpeg;base64,")
base64Str = strings.TrimPrefix(base64Str, "data:image/jpg;base64,")
base64Str = strings.TrimPrefix(base64Str, "data:image/png;base64,")
base64Str = strings.TrimPrefix(base64Str, "data:image/gif;base64,")
base64Str = strings.TrimPrefix(base64Str, "data:image/webp;base64,")
// 解码base64
imgData, err := base64.StdEncoding.DecodeString(base64Str)
if err != nil {
return nil, "", fmt.Errorf("解码base64失败: %v", err)
}
// 检测图片格式
format := detectImageFormat(imgData)
// 解码图片
img, _, err := image.Decode(bytes.NewReader(imgData))
// 主动释放数据内存
imgData = nil
if err != nil {
return nil, "", fmt.Errorf("解码base64图片失败: %v", err)
}
return img, format, nil
}
// 检测图片格式的辅助函数
func detectImageFormat(data []byte) string {
if len(data) < 12 {
return "unknown"
}
// PNG: 137 80 78 71 13 10 26 10
if len(data) >= 8 && data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 {
return "png"
}
// JPEG: 255 216 255
if len(data) >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF {
return "jpg"
}
// GIF: 71 73 70
if len(data) >= 3 && data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46 {
return "gif"
}
// WEBP: 82 73 70 70
if len(data) >= 12 && data[0] == 0x52 && data[1] == 0x49 && data[2] == 0x46 && data[3] == 0x46 {
return "webp"
}
return "unknown"
}
// 判断HTTP状态码是否值得重试
func isRetryableStatus(statusCode int) bool {
// 5xx服务器错误或429限流可以重试
return statusCode >= 500 || statusCode == 429
}
// AddWatermarkFromURL 从URL加载图片添加水印并返回字节集
func AddWatermarkFromURL(config WatermarkConfig) ([]byte, string, error) {
// 确保函数退出时释放内存
defer runtime.GC() // 可选,在低内存场景下使用
// 加载源图片
srcImg, srcFormat, err := loadImageFromURL(config.SourceImageURL, config.Timeout)
if err != nil {
return nil, "", fmt.Errorf("加载源图片失败: %v", err)
}
// 等比缩放原图到 TargetWidth x TargetHeight 范围内
if config.TargetWidth > 0 || config.TargetHeight > 0 {
srcW := srcImg.Bounds().Dx()
srcH := srcImg.Bounds().Dy()
var newW, newH uint
if config.TargetWidth > 0 && config.TargetHeight > 0 {
scaleX := float64(config.TargetWidth) / float64(srcW)
scaleY := float64(config.TargetHeight) / float64(srcH)
scale := math.Min(scaleX, scaleY)
newW = uint(float64(srcW) * scale)
newH = uint(float64(srcH) * scale)
} else if config.TargetWidth > 0 {
newW = uint(config.TargetWidth)
newH = uint(float64(srcH) * float64(config.TargetWidth) / float64(srcW))
} else {
newH = uint(config.TargetHeight)
newW = uint(float64(srcW) * float64(config.TargetHeight) / float64(srcH))
}
srcImg = resize.Resize(newW, newH, srcImg, resize.Lanczos3)
}
// 加载水印图片支持URL或base64
var watermarkImg image.Image
//var watermarkFormat string // 没用了
// 优先使用 base64
if config.WatermarkBase64 != "" {
watermarkImg, _, err = loadImageFromBase64(config.WatermarkBase64)
if err != nil {
return nil, "", fmt.Errorf("从base64加载水印图片失败: %v", err)
}
} else if config.WatermarkURL != "" {
// 检查是否为base64格式的URL以data:image开头
if strings.HasPrefix(config.WatermarkURL, "data:image/") {
watermarkImg, _, err = loadImageFromBase64(config.WatermarkURL)
if err != nil {
return nil, "", fmt.Errorf("从base64 URL加载水印图片失败: %v", err)
}
} else {
// 普通URL
watermarkImg, _, err = loadImageFromURL(config.WatermarkURL, config.Timeout)
if err != nil {
return nil, "", fmt.Errorf("从URL加载水印图片失败: %v", err)
}
}
} else {
return nil, "", fmt.Errorf("必须提供水印图片WatermarkURL或WatermarkBase64")
}
// 创建目标图片RGBA格式以支持透明
// 画布尺寸 = 水印尺寸
watermarkBounds := watermarkImg.Bounds()
dst := image.NewRGBA(watermarkBounds)
// 先绘制源图片(居中)
srcBounds := srcImg.Bounds()
x := (watermarkBounds.Dx() - srcBounds.Dx()) / 2
y := (watermarkBounds.Dy() - srcBounds.Dy()) / 2
draw.Draw(dst, image.Rect(x, y, x+srcBounds.Dx(), y+srcBounds.Dy()), srcImg, image.Point{}, draw.Src)
// 处理水印
err = applyWatermark(dst, watermarkImg, config)
if err != nil {
return nil, "", fmt.Errorf("应用水印失败: %v", err)
}
// 按目标尺寸缩放
finalImg := resizeOutputImage(dst, config)
// 确定输出格式
outputFormat := config.OutputFormat
if outputFormat == "" || outputFormat == "auto" {
outputFormat = srcFormat
}
// 将图片编码为字节集
imgBytes, err := encodeImageToBytes(finalImg, outputFormat, config.JPEGQuality)
if err != nil {
return nil, "", fmt.Errorf("编码图片失败: %v", err)
}
return imgBytes, outputFormat, nil
}
// encodeImageToBytes 将图片编码为字节集
func encodeImageToBytes(img image.Image, format string, quality int) ([]byte, error) {
var buf bytes.Buffer
switch strings.ToLower(format) {
case "jpeg", "jpg":
// 设置默认质量
if quality <= 0 || quality > 100 {
quality = 95
}
err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: quality})
if err != nil {
return nil, fmt.Errorf("JPEG编码失败: %v", err)
}
case "png":
err := png.Encode(&buf, img)
if err != nil {
return nil, fmt.Errorf("PNG编码失败: %v", err)
}
default:
// 默认使用PNG
err := png.Encode(&buf, img)
if err != nil {
return nil, fmt.Errorf("PNG编码失败: %v", err)
}
}
return buf.Bytes(), nil
}
// applyWatermark 应用水印到图片
func applyWatermark(dst *image.RGBA, watermark image.Image, config WatermarkConfig) error {
// 缩放水印
watermark = scaleWatermark(watermark, config.Scale)
// 根据位置绘制水印
switch config.Position {
case "tile":
drawTileWatermark(dst, watermark, config)
default:
drawSingleWatermark(dst, watermark, config)
}
return nil
}
// scaleWatermark 缩放水印
func scaleWatermark(watermark image.Image, scale float64) image.Image {
if scale <= 0 || scale >= 1 {
return watermark
}
bounds := watermark.Bounds()
newWidth := uint(float64(bounds.Dx()) * scale)
newHeight := uint(float64(bounds.Dy()) * scale)
if newWidth > 0 && newHeight > 0 {
return resize.Resize(newWidth, newHeight, watermark, resize.Lanczos3)
}
return watermark
}
// drawSingleWatermark 绘制单个水印
func drawSingleWatermark(dst *image.RGBA, watermark image.Image, config WatermarkConfig) {
bounds := dst.Bounds()
watermarkBounds := watermark.Bounds()
watermarkWidth := watermarkBounds.Dx()
watermarkHeight := watermarkBounds.Dy()
// 计算位置
var x, y int
switch config.Position {
case "center":
x = (bounds.Dx()-watermarkWidth)/2 + config.XOffset
y = (bounds.Dy()-watermarkHeight)/2 + config.YOffset
case "top-left":
x = config.XOffset
y = config.YOffset
case "top-right":
x = bounds.Dx() - watermarkWidth - config.XOffset
y = config.YOffset
case "bottom-left":
x = config.XOffset
y = bounds.Dy() - watermarkHeight - config.YOffset
case "bottom-right":
x = bounds.Dx() - watermarkWidth - config.XOffset
y = bounds.Dy() - watermarkHeight - config.YOffset
default: // 默认居中
x = (bounds.Dx()-watermarkWidth)/2 + config.XOffset
y = (bounds.Dy()-watermarkHeight)/2 + config.YOffset
}
// 确保不超出边界
x = max(0, min(x, bounds.Dx()-watermarkWidth))
y = max(0, min(y, bounds.Dy()-watermarkHeight))
// 绘制水印
drawWatermarkWithOpacity(dst, watermark, x, y, config.Opacity)
}
// drawTileWatermark 平铺水印
func drawTileWatermark(dst *image.RGBA, watermark image.Image, config WatermarkConfig) {
bounds := dst.Bounds()
watermarkBounds := watermark.Bounds()
watermarkWidth := watermarkBounds.Dx()
watermarkHeight := watermarkBounds.Dy()
spacing := config.TileSpacing
if spacing < 0 {
spacing = 0
}
stepX := watermarkWidth + spacing
stepY := watermarkHeight + spacing
// 计算起始偏移,使水印均匀分布
startX := (bounds.Dx() % stepX) / 2
startY := (bounds.Dy() % stepY) / 2
for y := startY; y < bounds.Dy(); y += stepY {
for x := startX; x < bounds.Dx(); x += stepX {
drawWatermarkWithOpacity(dst, watermark, x, y, config.Opacity)
}
}
}
// drawWatermarkWithOpacity 绘制带透明度的水印
func drawWatermarkWithOpacity(dst *image.RGBA, watermark image.Image, x, y int, opacity float64) {
if opacity < 0 {
opacity = 0
}
if opacity > 1 {
opacity = 1
}
watermarkBounds := watermark.Bounds()
for wy := 0; wy < watermarkBounds.Dy(); wy++ {
for wx := 0; wx < watermarkBounds.Dx(); wx++ {
// 计算目标位置
dx := x + wx
dy := y + wy
// 确保在目标图片范围内
if dx < 0 || dx >= dst.Bounds().Dx() || dy < 0 || dy >= dst.Bounds().Dy() {
continue
}
// 获取水印像素
wColor := watermark.At(wx, wy)
wr, wg, wb, wa := wColor.RGBA()
// 如果有透明度,考虑水印本身的透明度
if wa > 0 {
// 转换为8位
wr8 := uint8(wr >> 8)
wg8 := uint8(wg >> 8)
wb8 := uint8(wb >> 8)
wa8 := uint8(wa >> 8)
// 获取目标像素
dstColor := dst.At(dx, dy)
dr, dg, db, _ := dstColor.RGBA()
dr8 := uint8(dr >> 8)
dg8 := uint8(dg >> 8)
db8 := uint8(db >> 8)
// 混合颜色(考虑水印透明度和设置的不透明度)
alpha := float64(wa8) / 255.0 * opacity
r := uint8(float64(dr8)*(1-alpha) + float64(wr8)*alpha)
g := uint8(float64(dg8)*(1-alpha) + float64(wg8)*alpha)
b := uint8(float64(db8)*(1-alpha) + float64(wb8)*alpha)
dst.Set(dx, dy, color.RGBA{r, g, b, 255})
}
}
}
}
// resizeOutputImage 根据配置缩放输出图片
func resizeOutputImage(img image.Image, config WatermarkConfig) image.Image {
if config.TargetWidth <= 0 && config.TargetHeight <= 0 {
return img
}
srcBounds := img.Bounds()
srcW := srcBounds.Dx()
srcH := srcBounds.Dy()
// 如果只设了一个维度,另一个按比例算
targetW := config.TargetWidth
targetH := config.TargetHeight
if targetW <= 0 {
targetW = int(float64(targetH) * float64(srcW) / float64(srcH))
}
if targetH <= 0 {
targetH = int(float64(targetW) * float64(srcH) / float64(srcW))
}
switch config.ResizeMode {
case "fit":
return resizeFit(img, srcW, srcH, targetW, targetH)
case "fill":
return resizeFill(img, srcW, srcH, targetW, targetH)
case "stretch":
fallthrough
default:
return resize.Resize(uint(targetW), uint(targetH), img, resize.Lanczos3)
}
}
// resizeFit 等比缩放适应,多出部分填白
func resizeFit(img image.Image, srcW, srcH, targetW, targetH int) image.Image {
scaleX := float64(targetW) / float64(srcW)
scaleY := float64(targetH) / float64(srcH)
scale := math.Min(scaleX, scaleY)
newW := int(float64(srcW) * scale)
newH := int(float64(srcH) * scale)
scaled := resize.Resize(uint(newW), uint(newH), img, resize.Lanczos3)
// 居中放到白色画布上
dst := image.NewRGBA(image.Rect(0, 0, targetW, targetH))
draw.Draw(dst, dst.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
x := (targetW - newW) / 2
y := (targetH - newH) / 2
draw.Draw(dst, image.Rect(x, y, x+newW, y+newH), scaled, image.Point{}, draw.Over)
return dst
}
// resizeFill 等比缩放覆盖,溢出部分居中裁剪
func resizeFill(img image.Image, srcW, srcH, targetW, targetH int) image.Image {
scaleX := float64(targetW) / float64(srcW)
scaleY := float64(targetH) / float64(srcH)
scale := math.Max(scaleX, scaleY)
newW := int(float64(srcW) * scale)
newH := int(float64(srcH) * scale)
scaled := resize.Resize(uint(newW), uint(newH), img, resize.Lanczos3)
// 居中裁剪
cropX := (newW - targetW) / 2
cropY := (newH - targetH) / 2
dst := image.NewRGBA(image.Rect(0, 0, targetW, targetH))
draw.Draw(dst, dst.Bounds(), scaled, image.Point{X: cropX, Y: cropY}, draw.Src)
return dst
}
// =================== C 导出函数 ======================= // =================== C 导出函数 =======================
// 检测图片纯白占比 // 检测图片纯白占比
// //
@ -1847,14 +2351,61 @@ func GenerateBarcode(barcodeType, content, filename *C.char) *C.char {
contentStr := C.GoString(content) contentStr := C.GoString(content)
filenameStr := C.GoString(filename) filenameStr := C.GoString(filename)
filename, err := generateBarcode(barcodeTypeStr, contentStr, filenameStr) barcodeFilename, err := generateBarcode(barcodeTypeStr, contentStr, filenameStr)
if err != nil { if err != nil {
return C.CString(fmt.Sprintf("%v", err)) return C.CString(fmt.Sprintf("%v", err))
} }
return C.CString(filename) return C.CString(barcodeFilename)
} }
// 导出函数释放C字符串内存 // AddWatermarkFromURLEx 从URL添加水印并返回字节集C导出函数
//
//export AddWatermarkFromURLEx
func AddWatermarkFromURLEx(jsonConfig *C.char) *C.char {
configStr := C.GoString(jsonConfig)
var config WatermarkConfig
if err := json.Unmarshal([]byte(configStr), &config); err != nil {
return C.CString(fmt.Sprintf("ERROR:解析水印配置失败: %v", err))
}
imgBytes, format, err := AddWatermarkFromURL(config)
if err != nil {
return C.CString(fmt.Sprintf("ERROR:%v", err))
}
// 构建带MIME前缀的Base64数据
var base64Data string
switch format {
case "jpeg", "jpg":
base64Data = "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgBytes)
case "png":
base64Data = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes)
case "gif":
base64Data = "data:image/gif;base64," + base64.StdEncoding.EncodeToString(imgBytes)
default:
base64Data = "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgBytes)
}
// 构建返回结果:格式 + 字节集大小 + 字节集数据
// 使用Base64编码字节集以便在JSON中传输
result := struct {
Success bool `json:"success"`
Format string `json:"format"`
Data string `json:"data"` // Base64编码的图片数据
Size int `json:"size"`
}{
Success: true,
Format: format,
Data: base64Data,
Size: len(imgBytes),
}
resultBytes, _ := json.Marshal(result)
return C.CString(string(resultBytes))
}
// FreeCString 导出函数释放C字符串内存
// //
//export FreeCString //export FreeCString
func FreeCString(str *C.char) { func FreeCString(str *C.char) {
@ -1862,5 +2413,45 @@ func FreeCString(str *C.char) {
} }
// main 函数是必需的,即使为空 // main 函数是必需的,即使为空
//func main() { func main() {
//}
//// 图片URL
//surl := "https://img.pddpic.com/open-gw/2026-05-13/2f79386387d1b72f4081a7b661a9849a.png"
//
//// 1. 下载图片
//resp, err := http.Get(surl)
//if err != nil {
// panic(err)
//}
//defer resp.Body.Close()
//
//// 2. 读取图片二进制数据
//imgData, err := io.ReadAll(resp.Body)
//if err != nil {
// panic(err)
//}
//
//// 3. 转换为Base64字符串
//base64Str := base64.StdEncoding.EncodeToString(imgData)
//
//watermarkConfig := WatermarkConfig{
// SourceImageURL: "http://booklibimg.kfzimg.com/data/book_lib_img_v2/isbn/1/b044/b044aae81122166798a30699e0f007d2_0_1_300_300.jpg",
// WatermarkBase64: base64Str,
// Position: "center",
// Opacity: 1.0,
// Scale: 1.0,
// TileSpacing: 50,
// Timeout: 30,
// OutputFormat: "jpeg",
// JPEGQuality: 95,
// TargetWidth: 800,
// TargetHeight: 800,
// ResizeMode: "fit",
//}
//url, s, err := AddWatermarkFromURL(watermarkConfig)
//if err != nil {
// fmt.Println(err)
//} else {
// fmt.Println("url:", url, "s:", s)
//}
}

View File

@ -1,5 +1,16 @@
package main package main
import (
"fmt"
"golang.org/x/image/draw"
"image"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"
)
//func main() { //func main() {
// //// 测试长文本 // //// 测试长文本
// //longText := "这是一段非常长的中文文本需要测试自动换行功能。我们希望当文本超过图片宽度时能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容比如这里有一些English words mixed with中文。同时我们也要测试标点符号的处理例如逗号、句号、感叹号问号以及各种括号包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" + // //longText := "这是一段非常长的中文文本需要测试自动换行功能。我们希望当文本超过图片宽度时能够自动换行到下一行显示。这样就不需要手动添加换行符了。这段文本包含了中英文混合的内容比如这里有一些English words mixed with中文。同时我们也要测试标点符号的处理例如逗号、句号、感叹号问号以及各种括号包括圆括号、方括号[]、花括号{})等等。最后,我们还要测试一下当文本非常长,超过图片高度时的处理情况。" +
@ -37,3 +48,262 @@ package main
// fmt.Println(err) // fmt.Println(err)
// } // }
//} //}
// ScaleImage 缩放图片的主函数
// imgPath: 原始图片路径
// scaleWidth: 目标宽度像素如果为0则按比例自动计算
// scaleHeight: 目标高度像素如果为0则按比例自动计算
// quality: 图片质量1-100仅对JPEG有效
// 返回:缩放后的图片保存路径和错误信息
func ScaleImage(imgPath string, scaleWidth, scaleHeight int, quality int) (string, error) {
// 检查文件是否存在
if _, err := os.Stat(imgPath); os.IsNotExist(err) {
return "", fmt.Errorf("图片文件不存在: %s", imgPath)
}
// 打开原始图片
file, err := os.Open(imgPath)
if err != nil {
return "", fmt.Errorf("打开图片失败: %v", err)
}
defer file.Close()
// 解码图片
srcImg, format, err := image.Decode(file)
if err != nil {
return "", fmt.Errorf("解码图片失败: %v", err)
}
// 获取原始尺寸
srcBounds := srcImg.Bounds()
srcWidth := srcBounds.Dx()
srcHeight := srcBounds.Dy()
// 计算目标尺寸(保持宽高比)
dstWidth, dstHeight := calculateTargetSize(srcWidth, srcHeight, scaleWidth, scaleHeight)
// 创建目标图片
dstImg := image.NewRGBA(image.Rect(0, 0, dstWidth, dstHeight))
// 使用高质量的缩放算法
draw.ApproxBiLinear.Scale(dstImg, dstImg.Bounds(), srcImg, srcBounds, draw.Over, nil)
// 生成保存路径
outputPath := generateOutputPath(imgPath, dstWidth, dstHeight)
// 保存图片
if err := SaveImage(outputPath, dstImg, format, quality); err != nil {
return "", fmt.Errorf("保存图片失败: %v", err)
}
return outputPath, nil
}
// ScaleImageByWidth 按宽度缩放图片(高度自动适配)
func ScaleImageByWidth(imgPath string, targetWidth int, quality int) (string, error) {
return ScaleImage(imgPath, targetWidth, 0, quality)
}
// ScaleImageByHeight 按高度缩放图片(宽度自动适配)
func ScaleImageByHeight(imgPath string, targetHeight int, quality int) (string, error) {
return ScaleImage(imgPath, 0, targetHeight, quality)
}
// ScaleImageByPercent 按百分比缩放图片
func ScaleImageByPercent(imgPath string, percent float64, quality int) (string, error) {
if percent <= 0 {
return "", fmt.Errorf("缩放百分比必须大于0")
}
// 获取原始尺寸
file, err := os.Open(imgPath)
if err != nil {
return "", err
}
defer file.Close()
srcImg, _, err := image.Decode(file)
if err != nil {
return "", err
}
srcBounds := srcImg.Bounds()
targetWidth := int(float64(srcBounds.Dx()) * percent)
targetHeight := int(float64(srcBounds.Dy()) * percent)
return ScaleImage(imgPath, targetWidth, targetHeight, quality)
}
// calculateTargetSize 计算目标尺寸,保持宽高比
func calculateTargetSize(srcWidth, srcHeight, targetWidth, targetHeight int) (int, int) {
// 如果目标尺寸都未指定,返回原尺寸
if targetWidth == 0 && targetHeight == 0 {
return srcWidth, srcHeight
}
// 按宽度缩放
if targetWidth != 0 && targetHeight == 0 {
ratio := float64(targetWidth) / float64(srcWidth)
return targetWidth, int(float64(srcHeight) * ratio)
}
// 按高度缩放
if targetHeight != 0 && targetWidth == 0 {
ratio := float64(targetHeight) / float64(srcHeight)
return int(float64(srcWidth) * ratio), targetHeight
}
// 同时指定宽高,但保持原图宽高比,居中裁剪或留白
// 这里采用完全缩放到指定尺寸(可能会变形)
// 如需保持比例,可以取消下面的注释
/*
srcRatio := float64(srcWidth) / float64(srcHeight)
dstRatio := float64(targetWidth) / float64(targetHeight)
if srcRatio > dstRatio {
// 原图更宽,按高度缩放
newHeight := targetHeight
newWidth := int(float64(targetHeight) * srcRatio)
return newWidth, newHeight
} else {
// 原图更高,按宽度缩放
newWidth := targetWidth
newHeight := int(float64(targetWidth) / srcRatio)
return newWidth, newHeight
}
*/
return targetWidth, targetHeight
}
// generateOutputPath 生成输出文件路径
func generateOutputPath(srcPath string, width, height int) string {
dir := filepath.Dir(srcPath)
ext := filepath.Ext(srcPath)
name := strings.TrimSuffix(filepath.Base(srcPath), ext)
outputName := fmt.Sprintf("%s_scaled_%dx%d%s", name, width, height, ext)
return filepath.Join(dir, outputName)
}
// saveImage 保存图片到文件
func SaveImage(path string, img image.Image, format string, quality int) error {
// 创建输出文件
outFile, err := os.Create(path)
if err != nil {
return err
}
defer outFile.Close()
// 根据原始格式保存
switch strings.ToLower(format) {
case "jpeg", "jpg":
opts := &jpeg.Options{Quality: quality}
return jpeg.Encode(outFile, img, opts)
case "png":
return png.Encode(outFile, img)
default:
// 默认保存为PNG格式
return png.Encode(outFile, img)
}
}
// ScaleImageWithOptions 带更多选项的缩放函数
type ScaleOptions struct {
Width int // 目标宽度
Height int // 目标高度
Quality int // 图片质量 (1-100)
OutputPath string // 自定义输出路径,为空则自动生成
KeepRatio bool // 是否保持宽高比
CropCenter bool // 是否居中裁剪仅在KeepRatio为true且指定了宽高时有效
}
func ScaleImageWithOptions(imgPath string, opts ScaleOptions) (string, error) {
// 参数验证
if opts.Quality < 1 || opts.Quality > 100 {
opts.Quality = 90 // 默认质量
}
// 检查文件是否存在
if _, err := os.Stat(imgPath); os.IsNotExist(err) {
return "", fmt.Errorf("图片文件不存在: %s", imgPath)
}
// 打开并解码图片
file, err := os.Open(imgPath)
if err != nil {
return "", err
}
defer file.Close()
srcImg, format, err := image.Decode(file)
if err != nil {
return "", err
}
srcBounds := srcImg.Bounds()
srcWidth := srcBounds.Dx()
srcHeight := srcBounds.Dy()
var dstWidth, dstHeight int
if opts.KeepRatio {
// 保持宽高比
dstWidth, dstHeight = calculateTargetSize(srcWidth, srcHeight, opts.Width, opts.Height)
} else {
// 不保持宽高比,直接使用指定尺寸
dstWidth, dstHeight = opts.Width, opts.Height
if dstWidth == 0 {
dstWidth = srcWidth
}
if dstHeight == 0 {
dstHeight = srcHeight
}
}
// 创建目标图片
dstImg := image.NewRGBA(image.Rect(0, 0, dstWidth, dstHeight))
// 如果需要居中裁剪
if opts.CropCenter && opts.KeepRatio && opts.Width > 0 && opts.Height > 0 {
// 计算裁剪区域
srcRatio := float64(srcWidth) / float64(srcHeight)
dstRatio := float64(opts.Width) / float64(opts.Height)
var cropRect image.Rectangle
if srcRatio > dstRatio {
// 原图更宽,裁剪宽度
cropWidth := int(float64(srcHeight) * dstRatio)
cropX := (srcWidth - cropWidth) / 2
cropRect = image.Rect(cropX, 0, cropX+cropWidth, srcHeight)
} else {
// 原图更高,裁剪高度
cropHeight := int(float64(srcWidth) / dstRatio)
cropY := (srcHeight - cropHeight) / 2
cropRect = image.Rect(0, cropY, srcWidth, cropY+cropHeight)
}
// 先裁剪,再缩放
croppedImg := srcImg.(interface {
SubImage(r image.Rectangle) image.Image
}).SubImage(cropRect)
draw.ApproxBiLinear.Scale(dstImg, dstImg.Bounds(), croppedImg, croppedImg.Bounds(), draw.Over, nil)
} else {
// 直接缩放
draw.ApproxBiLinear.Scale(dstImg, dstImg.Bounds(), srcImg, srcBounds, draw.Over, nil)
}
// 确定输出路径
outputPath := opts.OutputPath
if outputPath == "" {
outputPath = generateOutputPath(imgPath, dstWidth, dstHeight)
}
// 保存图片
if err := SaveImage(outputPath, dstImg, format, opts.Quality); err != nil {
return "", err
}
return outputPath, nil
}

Binary file not shown.

View File

@ -87,94 +87,47 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
extern "C" { extern "C" {
#endif #endif
extern char* OutKfzLogin(char* username, char* password);
// OutLogin 孔网登录 extern char* OutKfzGetUserInfo(char* token);
// extern char* OutGetKfzShippingTemplate(char* token);
extern __declspec(dllexport) char* OutLogin(char* username, char* password); extern char* OutGetGoodsTplMsg(char* token, char* proxy, char* itemId);
extern char* OutGetGoodsListMsgFromSelfShop(char* token, char* proxy, char* itemSn, char* mouldId, char* priceMin, char* priceMax, char* startCreateTime, char* endCreateTime, char* requestType, int isItemSnEqual, int page, int size);
// OutGetUserMsg 获取孔网用户信息 extern char* OutGetTplFields(char* token, char* proxy, char* itemId);
// extern char* OutAddGoods(char* token, char* proxy, char* formData);
extern __declspec(dllexport) char* OutGetUserMsg(char* token); extern char* OutUpdateGoodsPrice(char* token, char* proxy, char* itemId, char* updateType, char* value);
extern char* OutGetBaseSelectData(char* token);
// OutGetGoodsTplMsg 获取商品模版--已登的店铺 extern char* OutAddGoodsAndFile(char* token, char* proxy, char* filePath, char* formData);
// extern char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* proxy, char* itemId); extern char* OutGetImageFilterShopId(char* token, char* proxy, char* isbn, int shopId, int isLiveImage, int isReturnMsg);
extern char* OutGetImageByIsbn(char* token, char* proxy, char* isbn, int isLiveImage, int isReturnMsg);
// OutGetGoodsListMsgFromSelfShop 获取商品列表-已登的店铺 extern char* OutGetAllGoods(char* token, char* proxy, int queryIndex, char* isbn, char* quality, char* itemName, char* author, char* press);
// extern char* OutGetMidDetail(char* token, char* mid);
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); extern char* OutGetGoodsListMsgByShopId(char* token, int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
extern char* OutGetHisGoodsList(char* token, char* shopId, char* proxy, char* retPrice, char* isImage, char* sortType, char* sort, char* priceMin, char* priceMax, int pageNum, int returnNum);
// OutAddGoods 新增商品-已登的店铺 extern char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// extern char* OutGetTopGoodsListMsg(int catId, char* proxy);
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData); extern char* KongfzDeliveryMethodList(int appId, char* appSecret, char* accessToken);
extern char* KongfzOrderDeliver(int appId, char* appSecret, char* accessToken, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
// OutAddGoodsAndFile 整合添加商品和获取孔网图片 extern char* KongfzOrderSynchronization(int appId, char* appSecret, char* accessToken, char* shippingComName, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
// extern char* KongfzImageUpload(int appId, char* appSecret, char* accessToken, char* filePath, char* savePath);
extern __declspec(dllexport) char* OutAddGoodsAndFile(char* token, char* proxy, char* filePath, char* formData); extern char* KongfzShopItemAdd(int appId, char* appSecret, char* accessToken, char* shopItemAddJson);
extern char* KongfzOrderList(int appId, char* appSecret, char* accessToken, char* orderListJson);
// OutDelGoodsFromSelfShop 删除商品-已登的店铺 extern char* KongfzOrderGet(int appId, char* appSecret, char* accessToken, char* userType, int orderId);
// extern char* KongfzOrderRedeliver(int appId, char* appSecret, char* accessToken, char* requestJson);
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId); extern char* KongfzOrderFlagAdd(int appId, char* appSecret, char* accessToken, char* requestJson);
extern char* KongfzOrderFlagDel(int appId, char* appSecret, char* accessToken, char* requestJson);
// OutGetImageFilterShopId 获取孔网商品图片和信息(官图和拍图)-带有店铺过滤 extern char* KongfzShopCategoryNameList(int appId, char* appSecret, char* accessToken);
// extern char* KongfzCommonCategory(int appId, char* appSecret, char* accessToken);
extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* proxy, char* isbn, int shopId, int isLiveImage, int isReturnMsg); extern char* KongfzShopItemDelisting(int appId, char* appSecret, char* accessToken, char* requestJson);
extern char* KongfzShopItemList(int appId, char* appSecret, char* accessToken, char* requestJson);
// OutGetImageByIsbn 获取孔网商品图片和信息(官图和拍图) extern char* KongfzShopItemDetail(int appId, char* appSecret, char* accessToken, char* requestJson);
// extern char* KongfzShopItemListing(int appId, char* appSecret, char* accessToken, char* requestJson);
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* proxy, char* isbn, int isLiveImage, int isReturnMsg); extern char* KongfzShopItemNumberUpdate(int appId, char* appSecret, char* accessToken, char* requestJson);
extern char* KongfzShopItemPriceUpdate(int appId, char* appSecret, char* accessToken, char* requestJson);
// OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID extern char* KongfzShopItemDelete(int appId, char* appSecret, char* accessToken, char* requestJson);
// extern char* Initialize(char* configJSON);
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum); extern void FreeCString(char* str);
extern char* GetVersion(void);
// 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 #ifdef __cplusplus
} }

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,185 +0,0 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "kongfz.go"
#include <stdlib.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// OutLogin 孔网登录
//
extern __declspec(dllexport) char* OutLogin(char* username, char* password);
// OutGetUserMsg 获取孔网用户信息
//
extern __declspec(dllexport) char* OutGetUserMsg(char* token);
// OutGetGoodsTplMsg 获取商品模版--已登的店铺
//
extern __declspec(dllexport) char* OutGetGoodsTplMsg(char* token, char* proxy, char* itemId);
// OutGetGoodsListMsgFromSelfShop 获取商品列表-已登的店铺
//
extern __declspec(dllexport) char* OutGetGoodsListMsgFromSelfShop(char* token, char* proxy, char* itemSn, char* priceMin, char* priceMax, char* startCreateTime, char* endCreateTime, char* requestType, int isItemSnEqual, int page, int size);
// OutAddGoods 新增商品-已登的店铺
//
extern __declspec(dllexport) char* OutAddGoods(char* token, char* proxy, char* formData);
// OutAddGoodsAndFile 整合添加商品和获取孔网图片
//
extern __declspec(dllexport) char* OutAddGoodsAndFile(char* token, char* proxy, char* filePath, char* formData);
// OutDelGoodsFromSelfShop 删除商品-已登的店铺
//
extern __declspec(dllexport) char* OutDelGoodsFromSelfShop(char* token, char* proxy, char* itemId);
// OutGetImageFilterShopId 获取孔网商品图片和信息(官图和拍图)-带有店铺过滤
//
extern __declspec(dllexport) char* OutGetImageFilterShopId(char* token, char* proxy, char* isbn, int shopId, int isLiveImage, int isReturnMsg);
// OutGetImageByIsbn 获取孔网商品图片和信息(官图和拍图)
//
extern __declspec(dllexport) char* OutGetImageByIsbn(char* token, char* proxy, char* isbn, int isLiveImage, int isReturnMsg);
// OutGetMidDetail 爬取孔网 https://item.kongfz.com/book/42291389.html 网址商品信息
//
extern __declspec(dllexport) char* OutGetMidDetail(char* token, char* mid);
// OutGetGoodsListMsgByShopId 获取商品列表通过店铺ID
//
extern __declspec(dllexport) char* OutGetGoodsListMsgByShopId(char* token, int shopId, char* proxy, int retPrice, int isImage, char* sortType, char* sort, float priceMin, float priceMax, int pageNum, int returnNum);
// OutGetGoodsMsgByDetailUrl 获取商品信息通过商品详情链接
//
extern __declspec(dllexport) char* OutGetGoodsMsgByDetailUrl(char* detailUrl, char* proxy);
// OutGetTopGoodsListMsg 获取销量榜商品列表
//
extern __declspec(dllexport) char* OutGetTopGoodsListMsg(int catId, char* proxy);
// KongfzDeliveryMethodList 获取配送方式列表
//
extern __declspec(dllexport) char* KongfzDeliveryMethodList(int appId, char* appSecret, char* accessToken);
// KongfzOrderDeliver 订单发货
//
extern __declspec(dllexport) char* KongfzOrderDeliver(int appId, char* appSecret, char* accessToken, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
// KongfzOrderSynchronization 孔网订单同步
//
extern __declspec(dllexport) char* KongfzOrderSynchronization(int appId, char* appSecret, char* accessToken, char* shippingComName, int orderId, char* shippingId, char* shippingCom, char* shipmentNum, char* userDefined, char* moreShipmentNum);
// KongfzImageUpload 上传图片接口
//
extern __declspec(dllexport) char* KongfzImageUpload(int appId, char* appSecret, char* accessToken, char* filePath, char* savePath);
// KongfzShopItemAdd 添加店铺商品
//
extern __declspec(dllexport) char* KongfzShopItemAdd(int appId, char* appSecret, char* accessToken, char* shopItemAddJson);
// KongfzOrderList 查询订单列表
//
extern __declspec(dllexport) char* KongfzOrderList(int appId, char* appSecret, char* accessToken, char* orderListJson);
// KongfzOrderGet 查询单个订单
//
extern __declspec(dllexport) char* KongfzOrderGet(int appId, char* appSecret, char* accessToken, char* userType, int orderId);
// Initialize 初始化配置
//
extern __declspec(dllexport) char* Initialize(char* configJSON);
// FreeCString 释放C字符串内存
//
extern __declspec(dllexport) void FreeCString(char* str);
// 获取版本信息
//
extern __declspec(dllexport) char* GetVersion(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,106 +1,46 @@
package main package main
//func main() { //func main() {
// //upload, err := kongfzImageUpload(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8", // // fields, err := outGetTplFields("2aaf1b863a530d4dc0a41539292587f5b35e05da", "", "9811034490")
// // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", // // if err != nil {
// // "D:/work/image/9787511220660.jpg", // // fmt.Println(err)
// // "D:\\work\\project\\kfzgw-info\\kongfz") // // }
// //if err != nil { // // marshal, _ := json.Marshal(fields)
// // fmt.Println(err) // // fmt.Println("数据:", string(marshal))
// //} // // formData := `{"author":"朱自清、逯亮 著","bearShipping":"buyer","binding":"2","booklibId":"","catId":"32002004001000000","deliverTimeGroup":"1","images[0][imgDesc]":"","images[0][imgType]":"0","images[0][imgUrl]":"sw/kfz-cos/kfzimg/24175337/7174f9932b38d071_s.jpg","images[0][isMain]":"1","images[1][imgDesc]":"","images[1][imgType]":"0","images[1][imgUrl]":"sw/kfz-cos/kfzimg/24175337/271a68c88dad67f4_s.jpg","images[1][isMain]":"0","importantDesc":"","isDeliverTimeDefault":"48h","isOnSale":"1","isbn":"9787204155293","itemDesc":"","itemId":"9890668175","itemName":"朱自清散文集/新课标课外阅读经典文学名著","itemSn":"24-9","language":"","material":"","mouldId":"975623","number":"3","oriPrice":"0.01","pageNum":"169","pageSize":"16开","pageType":"edit","paper":"6","press":"内蒙古人民出版社","price":"759.00","pubDate":"2018-10","quality":"95","qualityDesc":"","sizeHeight":"0.00","sizeLength":"0.00","sizeWidth":"0.00","tpl":"13","weight":"1.00","weightPiece":"0.00","wordNum":"230","yearsGroup":"1"}
// //fmt.Println(upload) // //`
// // goods, err := outUpdateGoods("ea9f46cd9b1c8a93f143993a363b08a835640080", "", formData)
// // if err != nil {
// // fmt.Println(err.Error())
// // }
// // marshal, _ := json.Marshal(goods)
// // fmt.Println(string(marshal))
// //
// //var q OrderQueryParams // info, err := outUpdateGoodsPrice("ea9f46cd9b1c8a93f143993a363b08a835640080", "", "9890668175", "price", "859.00")
// //q.UserType = "buyer"
// //marshal, err := json.Marshal(q)
// //if err != nil {
// // fmt.Println(err)
// //}
// //fmt.Println(string(marshal))
// //
// //list, err := kongfzOrderList(576, "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8",
// // "306849d7541661989453102bc0e1a4f000c4469ab084beddc7b10a90ca55f6bb", string(marshal))
// //if err != nil {
// // fmt.Println(err)
// //}
// //fmt.Println(list)
// //
// //key, err := outKfzimgKey("de810e9e702de07c50601ef27bb51c93878c920a", "")
// //if err != nil {
// // fmt.Println(err)
// //}
// //fmt.Println(key)
// //
// //upload, err := outKfzimgUpload("8b7f613a6f9de5dbaff8f6c419e1c1de8495a993", "",
// // "D:/work/image/9787115460622.jpg")
// //if err != nil {
// // fmt.Println(err)
// //}
// //fmt.Println(upload)
//
// //uploadd, err := outKfzimgUploadd("de810e9e702de07c50601ef27bb51c93878c920a", "",
// // "D:/work/image/9787511220660.jpg")
// //if err != nil {
// // fmt.Println(err)
// //}
// //fmt.Println(uploadd)
//
// appId := 576
// appSecret := "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8"
// accessToken := "7c5ef2e492f82457898dd9a1bc2eb725df3a67b282721c9c52bfa24e460c4d92"
// shippingComName := "韵达快递"
// orderId := 275723461
// shippingId := ""
// shippingCom := ""
// shipmentNum := "312944151258647"
// userDefined := ""
// moreShipmentNum := ""
//
// synchronization, err := kongfzOrderSynchronization(appId, appSecret, accessToken, shippingComName, orderId, shippingId, shippingCom, shipmentNum, userDefined, moreShipmentNum)
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err.Error())
// } // }
// fmt.Println(synchronization) // var apiResponse APIResponse
// // if err != nil {
// //data := ItemInfo{ // apiResponse = APIResponse{
// // ItemName: "至关重要的关系", // Success: false,
// // ISBN: "9787550213869", // Message: err.Error(),
// // Author: "[美]里德·霍夫曼、本·卡斯诺瓦 著;钱峰 译", // }
// // Press: "北京联合出版公司", // } else {
// // YearsGroup: "1", // apiResponse = APIResponse{
// // PubDate: "2013-04", // Success: true,
// // PubDateYear: "2013", // Data: info["result"],
// // PubDateMonth: "4", // }
// // Edition: "1", // }
// // Binding: "2", // 2可能表示平装 // marshal, _ := json.Marshal(apiResponse)
// // Quality: "95", // fmt.Println(string(marshal))
// // Price: "1500",
// // Number: "2", //goods, err := outGetAllGoods("1acf1d5ce6f99fde2ae31f093e4201f9c9004e3f", "", 3, "", "100", "三国演义", "罗贯中", "中国画报出版社")
// // ItemSn: "a1", //if err != nil {
// // MouldId: "909963", //fmt.Println(err)
// // DeliverTime: "48h", //}
// // IsDeliverTimeDefault: "48h", //marshal, _ := json.Marshal(goods)
// // CatId: "43000000000000000", //fmt.Println(string(marshal))
// // 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 { type ItemInfo struct {

4262
kongfz/kongfzLao.go Normal file

File diff suppressed because it is too large Load Diff

1
listener/ceshi.go Normal file
View File

@ -0,0 +1 @@
package main

View File

@ -1,71 +1,351 @@
package main package main
import ( import (
"bytes"
"context"
"encoding/json"
"fmt" "fmt"
"log" "io"
"net/http" "net/http"
"net/http/cookiejar"
"net/url"
"os"
"path/filepath"
"strings"
"time" "time"
"github.com/chromedp/chromedp"
) )
func main() { //func main() {
// 定义路由 // username := "15140030829"
http.HandleFunc("/", homeHandler) // password := "long616"
http.HandleFunc("/api/data", dataHandler) // downloadDir := "./downloads"
http.HandleFunc("/ws", websocketHandler) // os.MkdirAll(downloadDir, 0755)
//
// // 1. API 登录获取 Cookie
// fmt.Println("正在登录...")
// cookieJar, err := loginAndGetCookies(username, password)
// if err != nil {
// fmt.Printf("登录失败: %v\n", err)
// return
// }
// fmt.Println("✓ 登录成功")
//
// // 2. 使用 chromedp 的远程浏览器(自动下载)
// targetURL := "https://seller.kongfz.com/management/item/unsold/?tab=onSale"
// var downloadPath string
//
// // 创建 chromedp 上下文
// ctx, cancel := createChromeContext()
// defer cancel()
//
// // 设置超时
// ctx, cancel = context.WithTimeout(ctx, 5*time.Minute)
// defer cancel()
//
// // 执行操作
// err = chromedp.Run(ctx,
// // 注入 Cookie
// chromedp.ActionFunc(func(ctx context.Context) error {
// fmt.Println("正在注入 Session Cookie...")
// return injectCookies(ctx, cookieJar)
// }),
//
// // 导航到目标页面
// chromedp.Navigate(targetURL),
// chromedp.Sleep(5*time.Second),
//
// // 等待页面加载完成
// chromedp.WaitVisible("body", chromedp.ByQuery),
// chromedp.Sleep(2*time.Second),
//
// // 点击下载按钮
// chromedp.ActionFunc(func(ctx context.Context) error {
// fmt.Println("查找并点击下载按钮...")
// return clickDownloadButton(ctx)
// }),
//
// // 等待下载完成
// chromedp.ActionFunc(func(ctx context.Context) error {
// fmt.Println("等待文件下载...")
// path, err := waitForDownload(downloadDir, 90*time.Second)
// if err != nil {
// return err
// }
// downloadPath = path
// return nil
// }),
// )
//
// if err != nil {
// fmt.Printf("错误: %v\n", err)
// //// 打印更多错误信息
// //if chromedpErr, ok := err.(*chromedp.Err); ok {
// // fmt.Printf("Chromedp 错误详情: %v\n", chromedpErr)
// //}
// return
// }
//
// fmt.Printf("✓ 文件已保存: %s\n", downloadPath)
//}
// 中间件示例 // createChromeContext 创建 Chrome 上下文
wrappedHandler := loggingMiddleware(http.DefaultServeMux) func createChromeContext() (context.Context, context.CancelFunc) {
// 设置 Chrome 选项
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("disable-blink-features", "AutomationControlled"),
chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"),
chromedp.Flag("headless", false), // 设置为 false 可以看到浏览器窗口
chromedp.Flag("no-sandbox", true),
chromedp.Flag("disable-dev-shm-usage", true),
chromedp.Flag("disable-gpu", true),
chromedp.Flag("disable-web-security", false),
chromedp.Flag("disable-extensions", true),
chromedp.Flag("disable-plugins", true),
// 设置下载目录
chromedp.Flag("disable-default-apps", true),
chromedp.Flag("disable-background-networking", true),
// 指定下载路径(如果需要)
// chromedp.Flag("download.default_directory", downloadDir),
)
// 配置服务器 allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
server := &http.Server{ ctx, _ := chromedp.NewContext(allocCtx)
Addr: ":8080",
Handler: wrappedHandler, return ctx, cancel
ReadTimeout: 10 * time.Second, }
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second, // loginAndGetCookies 登录 API
func loginAndGetCookies(username, password string) (*cookiejar.Jar, error) {
loginURL := "https://login.kongfz.com/Pc/Login/account"
reqBody := map[string]interface{}{
"loginName": username,
"loginPass": password,
"captchaCode": "",
"autoLogin": 0,
"newUsername": "",
"returnUrl": "https://seller.kongfz.com/management/item/unsold/?tab=onSale",
"captchaId": "",
} }
fmt.Println("HTTP Server listening on :8080") jsonData, _ := json.Marshal(reqBody)
// 启动服务器 jar, _ := cookiejar.New(nil)
if err := server.ListenAndServe(); err != nil { client := &http.Client{
log.Fatal("Server error:", err) Jar: jar,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return nil // 允许重定向
},
} }
}
func homeHandler(w http.ResponseWriter, r *http.Request) { req, _ := http.NewRequest("POST", loginURL, bytes.NewBuffer(jsonData))
if r.URL.Path != "/" { req.Header.Set("Content-Type", "application/json")
http.NotFound(w, r) req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
return req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
req.Header.Set("Referer", "https://login.kongfz.com/")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
} }
fmt.Fprintf(w, "Welcome to HTTP Server!\n") defer resp.Body.Close()
}
func dataHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(resp.Body)
switch r.Method { if err != nil {
case "GET": return nil, fmt.Errorf("读取响应失败: %v", err)
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)
} }
fmt.Printf("登录响应: %s\n", string(body))
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
if code, ok := result["code"]; ok {
switch v := code.(type) {
case float64:
if v != 0 {
return nil, fmt.Errorf("登录失败: %v", result["msg"])
}
case int:
if v != 0 {
return nil, fmt.Errorf("登录失败: %v", result["msg"])
}
}
}
// 获取并打印所有 Cookie
cookies := jar.Cookies(&url.URL{Scheme: "https", Host: "kongfz.com"})
fmt.Printf("获取到 %d 个 Cookie\n", len(cookies))
for _, c := range cookies {
fmt.Printf("Cookie: %s=%s\n", c.Name, c.Value[:min(20, len(c.Value))])
}
return jar, nil
} }
func websocketHandler(w http.ResponseWriter, r *http.Request) { func injectCookies(ctx context.Context, jar *cookiejar.Jar) error {
// WebSocket处理逻辑 // 获取所有 Cookie
fmt.Fprintf(w, "WebSocket endpoint") cookies := jar.Cookies(&url.URL{Scheme: "https", Host: "kongfz.com"})
// 先设置 document.cookie
for _, cookie := range cookies {
// 构建 Cookie 字符串
cookieStr := fmt.Sprintf("%s=%s; path=/; domain=.kongfz.com", cookie.Name, cookie.Value)
if cookie.Domain != "" {
cookieStr += fmt.Sprintf("; domain=%s", cookie.Domain)
}
if cookie.Path != "" {
cookieStr += fmt.Sprintf("; path=%s", cookie.Path)
}
jsCode := fmt.Sprintf(`document.cookie = '%s';`, cookieStr)
err := chromedp.Evaluate(jsCode, nil).Do(ctx)
if err != nil {
fmt.Printf("设置 cookie %s 失败: %v\n", cookie.Name, err)
}
}
// 验证 Cookie 是否设置成功
var cookiesCount int
err := chromedp.Evaluate(`document.cookie.length`, &cookiesCount).Do(ctx)
if err != nil {
return fmt.Errorf("验证 Cookie 失败: %v", err)
}
fmt.Printf("Cookie 注入完成,当前 document.cookie 长度: %d\n", cookiesCount)
return nil
} }
// 中间件:记录请求日志 func clickDownloadButton(ctx context.Context) error {
func loggingMiddleware(next http.Handler) http.Handler { // 多种方式尝试点击下载按钮
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 调用下一个处理器 // 方法1: 通过文本查找
next.ServeHTTP(w, r) var clicked bool
err := chromedp.Evaluate(`
(function() {
// 禁用确认对话框
window.confirm = function() { return true; };
window.alert = function() { return true; };
// 记录日志 // 查找包含"下载查询结果"的按钮
log.Printf("%s %s %s %v", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start)) var buttons = Array.from(document.querySelectorAll('button, a, span, div'));
}) var downloadBtn = buttons.find(el => el.innerText && el.innerText.includes('下载查询结果'));
if (downloadBtn) {
downloadBtn.click();
return true;
}
// 尝试通过类名查找
var btnByClass = document.querySelector('.download-btn, .export-btn, [class*="download"], [class*="export"]');
if (btnByClass) {
btnByClass.click();
return true;
}
return false;
})()
`, &clicked).Do(ctx)
if err != nil {
return fmt.Errorf("执行点击脚本失败: %v", err)
}
if !clicked {
// 截图以便调试
var buf []byte
err = chromedp.CaptureScreenshot(&buf).Do(ctx)
if err == nil {
os.WriteFile("debug_screenshot.png", buf, 0644)
fmt.Println("已保存调试截图: debug_screenshot.png")
}
// 获取页面标题和URL
var title, url string
chromedp.Title(&title).Do(ctx)
chromedp.Location(&url).Do(ctx)
fmt.Printf("当前页面: %s, URL: %s\n", title, url)
return fmt.Errorf("未找到下载按钮")
}
fmt.Println("✓ 已点击下载按钮")
time.Sleep(3 * time.Second)
return nil
}
func getCookiesFromJar(jar *cookiejar.Jar, domain string) []*http.Cookie {
parsedURL, _ := url.Parse(fmt.Sprintf("https://%s/", domain))
return jar.Cookies(parsedURL)
}
func waitForDownload(dir string, timeout time.Duration) (string, error) {
deadline := time.Now().Add(timeout)
lastCount := 0
for time.Now().Before(deadline) {
entries, err := os.ReadDir(dir)
if err != nil {
return "", err
}
downloading := false
var excels []string
for _, entry := range entries {
name := entry.Name()
if strings.HasSuffix(name, ".crdownload") ||
strings.HasSuffix(name, ".tmp") ||
strings.HasSuffix(name, ".download") {
downloading = true
fmt.Printf("下载中: %s\n", name)
} else if strings.HasSuffix(name, ".xls") ||
strings.HasSuffix(name, ".xlsx") ||
strings.HasSuffix(name, ".csv") {
excels = append(excels, filepath.Join(dir, name))
}
}
if !downloading && len(excels) > 0 {
// 找到最新的文件
var newest string
var newestTime time.Time
for _, f := range excels {
info, err := os.Stat(f)
if err == nil {
if info.ModTime().After(newestTime) {
newestTime = info.ModTime()
newest = f
}
}
}
// 确保文件写入完成
time.Sleep(2 * time.Second)
if newest != "" {
fmt.Printf("下载完成: %s\n", newest)
return newest, nil
}
}
if len(excels) != lastCount {
fmt.Printf("发现 %d 个 Excel 文件\n", len(excels))
lastCount = len(excels)
}
time.Sleep(2 * time.Second)
}
return "", fmt.Errorf("下载超时 (%v)", timeout)
}
func min(a, b int) int {
if a < b {
return a
}
return b
} }

772
md/pdd.md
View File

@ -623,6 +623,778 @@ dll.PddGoodsQuantityUpdate(clientId, clientSecret, accessToken, request)
} }
``` ```
## 12. 商品图片上传接口base64方式--PddGoodsImageUpload
### 请求信息
```gotemplate
dll.PddGoodsImageUpload(clientId, clientSecret, accessToken, fileBase)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| fileBase | string | 是 | 图片base64编码字符串 |
### 响应示例
```json
{
"goods_image_upload_response": {
"image_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
"request_id": "17666480184871655"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 13. 订单基础信息列表查询接口(根据成交时间)--PddOrderBasicListGet
### 请求信息
```gotemplate
dll.PddOrderBasicListGet(clientId, clientSecret, accessToken, orderBasicListGetJSONStr)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| orderBasicListGetJSONStr | string | 是 | 订单列表查询JSON字符串 |
#### 请求JSON结构示例
```json
{
"order_status": 1,
"refund_status": 1,
"start_confirm_at": 1700000000,
"end_confirm_at": 1700100000,
"page": 1,
"page_size": 50
}
```
### 响应示例
```json
{
"order_basic_list_get_response": {
"order_list": [
{
"order_sn": "250101-123456789",
"goods_id": 123456789,
"goods_name": "测试商品",
"goods_quantity": 1,
"order_amount": 8900,
"order_status": 1,
"confirm_time": 1700000000
}
],
"total_count": 100,
"request_id": "17666480184871656"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 14. 获取商品提交的商品详情--PddGoodsCommitDetailGet
### 请求信息
```gotemplate
dll.PddGoodsCommitDetailGet(clientId, clientSecret, accessToken, goodsCommitId, goodsId)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| goodsCommitId | string | 是 | 商品提交ID |
| goodsId | string | 是 | 商品ID |
### 响应示例
```json
{
"goods_commit_detail_get_response": {
"goods_id": 123456789,
"goods_name": "测试商品",
"cat_id": 20111,
"goods_desc": "商品描述",
"image_url": "http://oms-imageimg.pinduoduo.com/upload/xxx.jpg",
"sku_list": [],
"request_id": "17666480184871657"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 15. 获取拼多多系统时间--PddTimeGet
### 请求信息
```gotemplate
dll.PddTimeGet(clientId, clientSecret, accessToken)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
### 响应示例
```json
{
"time_get_response": {
"time": 1700000000000,
"request_id": "17666480184871658"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 16. 查询面单服务订购及面单使用情况--PddWaybillSearch
### 请求信息
```gotemplate
dll.PddWaybillSearch(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 查询参数JSON字符串 |
#### 请求JSON结构示例
```json
{
"wp_code": "STO"
}
```
### 响应示例
```json
{
"waybill_search_response": {
"wp_code": "STO",
"total_quantity": 1000,
"used_quantity": 100,
"created_at": 1700000000,
"request_id": "17666480184871659"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 17. 电子面单取号--PddFdsWaybillGet
### 请求信息
```gotemplate
dll.PddFdsWaybillGet(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 取号请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"wp_code": "STO",
"order_sn": "250101-123456789",
"sender": {
"name": "张三",
"mobile": "13800000000",
"province": "广东省",
"city": "深圳市",
"town": "南山区",
"address": "xx街道xx号"
},
"receiver": {
"name": "李四",
"mobile": "13900000000",
"province": "广东省",
"city": "广州市",
"town": "天河区",
"address": "xx街道xx号"
}
}
```
### 响应示例
```json
{
"fds_waybill_get_response": {
"waybill_code": "1234567890",
"print_data": "{\"content\": \"打印数据\"}",
"request_id": "17666480184871660"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 18. 商家取消获取的电子面单号--PddWaybillCancel
### 请求信息
```gotemplate
dll.PddWaybillCancel(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 取消面单请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"wp_code": "STO",
"waybill_code": "1234567890"
}
```
### 响应示例
```json
{
"waybill_cancel_response": {
"is_success": true,
"request_id": "17666480184871661"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 19. 商品列表接口--PddGoodsListGet
### 请求信息
```gotemplate
dll.PddGoodsListGet(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 商品列表查询JSON字符串 |
#### 请求JSON结构示例
```json
{
"is_onsale": 1,
"page": 1,
"page_size": 50
}
```
### 响应示例
```json
{
"goods_list_get_response": {
"goods_list": [
{
"goods_id": 123456789,
"goods_name": "测试商品",
"goods_sn": "G202501200001",
"goods_status": 1,
"goods_type": 1
}
],
"total_count": 100,
"request_id": "17666480184871662"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 20. 电子面单云打印接口--PddWaybillGet
### 请求信息
```gotemplate
dll.PddWaybillGet(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"wp_code": "STO",
"waybill_code": "1234567890"
}
```
### 响应示例
```json
{
"waybill_get_response": {
"print_data": "{\"content\": \"打印数据\"}",
"request_id": "17666480184871663"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 21. 通过面单号查询面单信息--PddWaybillQueryByWaybillcode
### 请求信息
```gotemplate
dll.PddWaybillQueryByWaybillcode(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 查询面单信息JSON字符串 |
#### 请求JSON结构示例
```json
{
"wp_code": "STO",
"waybill_code": "1234567890"
}
```
### 响应示例
```json
{
"waybill_query_by_waybillcode_response": {
"wp_code": "STO",
"waybill_code": "1234567890",
"order_sn": "250101-123456789",
"print_data": "{\"content\": \"打印数据\"}",
"request_id": "17666480184871664"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 22. 云打印--PddCloudPrint
### 请求信息
```gotemplate
dll.PddCloudPrint(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"print_data": "{\"content\": \"打印数据\"}",
"printer_id": "printer_001"
}
```
### 响应示例
```json
{
"cloud_print_response": {
"is_success": true,
"request_id": "17666480184871665"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 23. 云打印任务查询--PddCloudPrintTaskQuery
### 请求信息
```gotemplate
dll.PddCloudPrintTaskQuery(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印任务查询JSON字符串 |
#### 请求JSON结构示例
```json
{
"task_id": "task_001"
}
```
### 响应示例
```json
{
"cloud_print_task_query_response": {
"task_id": "task_001",
"task_status": 2,
"request_id": "17666480184871666"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 24. 云打印验证码--PddCloudPrintVerifyCode
### 请求信息
```gotemplate
dll.PddCloudPrintVerifyCode(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印验证码请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"printer_id": "printer_001",
"verify_code": "123456"
}
```
### 响应示例
```json
{
"cloud_print_verify_code_response": {
"is_success": true,
"request_id": "17666480184871667"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 25. 云打印机绑定--PddCloudPrinterBind
### 请求信息
```gotemplate
dll.PddCloudPrinterBind(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印机绑定JSON字符串 |
#### 请求JSON结构示例
```json
{
"printer_id": "printer_001",
"printer_name": "前台打印机"
}
```
### 响应示例
```json
{
"cloud_printer_bind_response": {
"is_success": true,
"printer_id": "printer_001",
"request_id": "17666480184871668"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 26. 云打印机设置--PddCloudPrinterSetting
### 请求信息
```gotemplate
dll.PddCloudPrinterSetting(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印机设置JSON字符串 |
#### 请求JSON结构示例
```json
{
"printer_id": "printer_001",
"auto_print": 1,
"print_count": 1
}
```
### 响应示例
```json
{
"cloud_printer_setting_response": {
"is_success": true,
"request_id": "17666480184871669"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 27. 云打印机状态查询--PddCloudPrinterStatusQuery
### 请求信息
```gotemplate
dll.PddCloudPrinterStatusQuery(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 云打印机状态查询JSON字符串 |
#### 请求JSON结构示例
```json
{
"printer_id": "printer_001"
}
```
### 响应示例
```json
{
"cloud_printer_status_query_response": {
"printer_id": "printer_001",
"printer_status": 1,
"request_id": "17666480184871670"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 28. 商品上架状态设置--PddGoodsSaleStatusSet
### 请求信息
```gotemplate
dll.PddGoodsSaleStatusSet(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 商品上架状态设置JSON字符串 |
#### 请求JSON结构示例
```json
{
"goods_id": 123456789,
"is_onsale": 1
}
```
### 响应示例
```json
{
"goods_sale_status_set_response": {
"is_success": true,
"goods_id": 123456789,
"request_id": "17666480184871671"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 29. 删除商品接口--PddDeleteGoodsCommit
### 请求信息
```gotemplate
dll.PddDeleteGoodsCommit(clientId, clientSecret, accessToken, requestJson)
```
### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--|--|--|----------|
| clientId | string | 是 | 拼多多开放平台ClientID |
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
| accessToken | string | 是 | 授权令牌 |
| requestJson | string | 是 | 删除商品请求JSON字符串 |
#### 请求JSON结构示例
```json
{
"goods_id": 123456789
}
```
### 响应示例
```json
{
"delete_goods_commit_response": {
"is_success": true,
"goods_id": 123456789,
"request_id": "17666480184871672"
}
}
```
### 错误响应示例
```json
{
"error_response": {
"error_msg": "公共参数错误:type",
"sub_msg": "",
"sub_code": null,
"error_code": 10001,
"request_id": "15440104776643887"
}
}
```
## 获取商品信息接口 -- OutPddAuthGetCommitDetailt ## 获取商品信息接口 -- OutPddAuthGetCommitDetailt
### 请求信息 ### 请求信息
```gotemplate ```gotemplate

2231
newWalkingWithBooks.go Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -87,82 +87,42 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
extern "C" { extern "C" {
#endif #endif
extern char* PddGoodsOuterCatMappingGet(char* clientId, char* clientSecret, char* accessToken, char* outerCatId, char* outerCatName, char* outerGoodsName);
// PddGoodsOuterCatMappingGet 类目预测 extern char* PddLogisticsCompaniesGet(char* clientId, char* clientSecret);
// extern char* PddErpOrderSync(char* clientId, char* clientSecret, char* accessToken, char* logisticsId, char* orderSn, char* orderState, char* waybillNo);
extern __declspec(dllexport) char* PddGoodsOuterCatMappingGet(char* clientId, char* clientSecret, char* accessToken, char* outerCatId, char* outerCatName, char* outerGoodsName); extern char* PddOrderSynchronization(char* clientId, char* clientSecret, char* accessToken, char* logisticsCompany, char* logisticsOnlineSendJson);
extern char* PddGoodsImgUpload(char* clientId, char* clientSecret, char* accessToken, char* filePath);
// PddLogisticsCompaniesGet 快递公司查看 extern char* PddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* goodsAddJson);
// extern char* SelfPddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* filePath, char* goodsAddJson);
extern __declspec(dllexport) char* PddLogisticsCompaniesGet(char* clientId, char* clientSecret); extern char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson);
extern char* PddGoodsSpecIdGet(char* clientId, char* clientSecret, char* accessToken, char* parentSpecId, char* specName);
// PddErpOrderSync erp打单信息同步 extern char* PddGoodsSkuPriceUpdate(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// extern char* PddGoodsQuantityUpdate(char* clientId, char* clientSecret, char* accessToken, char* request);
extern __declspec(dllexport) char* PddErpOrderSync(char* clientId, char* clientSecret, char* accessToken, char* logisticsId, char* orderSn, char* orderState, char* waybillNo); extern char* PddGoodsImageUpload(char* clientId, char* clientSecret, char* accessToken, char* fileBase);
extern char* PddOrderBasicListGet(char* clientId, char* clientSecret, char* accessToken, char* orderBasicListGetJSON);
// PddOrderSynchronization 拼多多订单同步 extern char* PddGoodsCommitDetailGet(char* clientId, char* clientSecret, char* accessToken, char* goodsCommitId, char* goodsId);
// extern char* PddTimeGet(char* clientId, char* clientSecret, char* accessToken);
extern __declspec(dllexport) char* PddOrderSynchronization(char* clientId, char* clientSecret, char* accessToken, char* logisticsCompany, char* logisticsOnlineSendJson); extern char* PddWaybillSearch(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern char* PddFdsWaybillGet(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// PddGoodsImgUpload 商品图片上传接口 extern char* PddWaybillCancel(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// extern char* PddGoodsListGet(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern __declspec(dllexport) char* PddGoodsImgUpload(char* clientId, char* clientSecret, char* accessToken, char* filePath); extern char* PddWaybillGet(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern char* PddWaybillQueryByWaybillcode(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// PddGoodsAdd 商品新增接口 extern char* PddCloudPrint(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// extern char* PddCloudPrintTaskQuery(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern __declspec(dllexport) char* PddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* goodsAddJson); extern char* PddCloudPrintVerifyCode(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern char* PddCloudPrinterBind(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// SelfPddGoodsAdd 联合拼多多图片上传的商品新增 extern char* PddCloudPrinterSetting(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// extern char* PddCloudPrinterStatusQuery(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern __declspec(dllexport) char* SelfPddGoodsAdd(char* clientId, char* clientSecret, char* accessToken, char* filePath, char* goodsAddJson); extern char* PddGoodsSaleStatusSet(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
extern char* PddDeleteGoodsCommit(char* clientId, char* clientSecret, char* accessToken, char* requestJson);
// PddOpenDecryptMaskBatch 批量数据解密脱敏接口 extern char* OutPddAuthGetCommitDetailt(char* goodsCommitId, char* goodsId, char* accessToken);
// extern char* OutPddAuthGetGoodsDetail(char* goodsId, char* accessToken);
extern __declspec(dllexport) char* PddOpenDecryptMaskBatch(char* clientId, char* clientSecret, char* accessToken, char* reqJson); extern char* OutPddAuthSetSpec(int specTypeId, char* specName, char* accessToken);
extern char* OutPddAuthGetCats(void);
// PddGoodsSpecIdGet 生成商家自定义的规格 extern char* OutPddAuthUpdatePrice(char* jsonData);
// extern char* OutPddAuthUpdateStock(char* jsonData);
extern __declspec(dllexport) char* PddGoodsSpecIdGet(char* clientId, char* clientSecret, char* accessToken, char* parentSpecId, char* specName); extern void FreeCString(char* str);
// 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);
#ifdef __cplusplus #ifdef __cplusplus
} }

1731
pdd/pdd.go

File diff suppressed because it is too large Load Diff

View File

@ -266,12 +266,32 @@ type DataList struct {
} }
//func main() { //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}` // //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", // add, err := pddTimeGet("203c5a7ba8bd4b8488d5e26f93052642",
// "892ffaa86e12b7a3d8d2942b669d9aa520ad8179", // "892ffaa86e12b7a3d8d2942b669d9aa520ad8179",
// "1177d0c36419417eba692a3fea88f611d42f0665", jsonStr) // "1177d0c36419417eba692a3fea88f611d42f0665")
// if err != nil { // if err != nil {
// fmt.Println(err.Error()) // fmt.Println(err.Error())
// } // }
// fmt.Println(add) // fmt.Println(add)
//} //}
//func main() {
// 替换为你的图片路径
//imagePath := "D:\\isbn_images\\9771005867004.jpg"
//url := "http://103.236.68.64:19000/public-img/bbb.jpg"
// http://36.212.7.35:19000/public-img/e5ef25ae85853f3e_s.jpg
//fPath := "D:\\isbn_images\\9780064408561.jpg"
//
//baseUrl := "http://36.212.7.35:19000/public-img/" + filepath.Base(fPath)
////picSpaceResult, err := utils.UploadImageToSpace(imgResult.PicPath, baseUrl)
//
//space, err := UploadImageToSpace(fPath, baseUrl)
//if err != nil {
// fmt.Println(err)
//}
//fmt.Println(space)
//}

Binary file not shown.

View File

@ -1,129 +0,0 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "proxy.go"
#include <stdlib.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// GetProxyHealth 导出函数:获取代理健康状态(用于调试)
//
extern __declspec(dllexport) char* GetProxyHealth(void);
// ProxyTypeManager 导出函数:代理类型管理器
//
extern __declspec(dllexport) char* ProxyTypeManager(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

Binary file not shown.

View File

@ -8,538 +8,168 @@ import (
"crypto/md5" "crypto/md5"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/parnurzeal/gorequest"
"log"
"math/rand" "math/rand"
"net/url"
"strings" "strings"
"sync" "sync"
"time" "time"
"unsafe" "unsafe"
"github.com/parnurzeal/gorequest"
) )
// 代理类型常量
const ( const (
CalfElephantProxyType = "CALF_ELEPHANT_PROXY" // 小象代理类型 TailProxyType = "TAIL_PROXY" // 尾巴代理类型
TailProxyType = "TAIL_PROXY" // 尾巴代理类型
) )
// 小象代理服务器列表 // 互斥锁,用于保护全局随机数生成器的并发访问
var ( 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 randMutex sync.Mutex
globalRand *rand.Rand // 全局随机数生成器 globalRand *rand.Rand
// 代理健康状态管理
// 代理健康状态映射表key为代理主机名value为健康状态
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
}
// ProxyHealth 代理健康状态结构体,用于跟踪代理的健康状况
type ProxyHealth struct {
SuccessCount int // 成功次数
FailCount int // 失败次数
LastCheck time.Time // 最后检查时间
ResponseTime time.Duration // 响应时间
IsHealthy bool // 是否健康
}
// 初始化函数,在程序启动时自动执行
func init() { func init() {
// 创建全局的随机数生成器,使用当前时间作为种子
globalRand = rand.New(rand.NewSource(time.Now().UnixNano())) globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
} }
/* // 根据代理类型获取代理URL
* 获取代理URL代理类型管理器根据代理类型构建不同的代理URL func getProxyByType(proxyType, machineCode string) (string, error) {
* param proxyType[string] 代理类型
* param username[string] 代理用户名
* param password[string] 代理密码
* param machineCode[string] 机器码
* return 代理服务器IP,错误信息
*/
func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
switch proxyType { switch proxyType {
case CalfElephantProxyType:
// 小象代理使用用户名和密码构建代理URL
return buildCalfElephantProxyURL(username, password)
case TailProxyType: case TailProxyType:
// 尾巴代理使用机器码构建代理URL return getTailProxyURL(machineCode)
return buildTailProxyURL(machineCode)
default: default:
// 不支持的代理类型,返回错误
return "", fmt.Errorf("不支持的代理类型: %s", proxyType) return "", fmt.Errorf("不支持的代理类型: %s", proxyType)
} }
} }
func proxyTypeManagerNew(proxyType, username, password, machineCode string) (string, error) { // 获取尾巴代理URL
func getTailProxyURL(machineCode string) (string, error) {
// 获取代理列表 // 获取代理列表
proxies, err := getProxies(machineCode) proxies, err := getProxies(machineCode)
if err != nil { if err != nil {
return "", err return "", err
} }
// 检查是否获取到有效代理
if len(proxies) == 0 { if len(proxies) == 0 {
return "", fmt.Errorf("未获取到有效代理") return "", fmt.Errorf("未获取到有效代理")
} }
// 获取代理
// 随机选择一个代理
proxy := randomElement(proxies) proxy := randomElement(proxies)
proxy = fmt.Sprintf("http://%s", proxy) proxyURL := fmt.Sprintf("http://%s", proxy)
return proxy, nil
}
/*
* 构建小象代理URL
* param username[string] 代理用户名
* param password[string] 代理密码
* return 代理服务器IP,错误信息
*/
func buildCalfElephantProxyURL(username, password string) (string, error) {
// 随机选择一个代理服务器
server := randomServer()
// 构建代理URL格式http://用户名:密码@服务器:端口
proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
url.QueryEscape(username), // URL编码用户名防止特殊字符问题
url.QueryEscape(password), // URL编码密码防止特殊字符问题
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 return proxyURL, nil
} }
/*
* 尝试下一个小象代理服务器
* param username[string] 代理用户名
* param password[string] 代理密码
* param failedServer[string] 代理服务器
* return 代理服务器IP,错误信息
*/
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) {
// 构建代理URL
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
* param machineCode[string] 机器码
* return 代理服务器IP,错误信息
*/
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)
}
/*
* 过滤健康代理返回当前健康的代理列表
* param proxies[[]string] 内置代理服务器数组
* return 内置代理服务器数组,错误信息
*/
func filterHealthyProxies(proxies []string) []string {
// 获取读锁允许多个goroutine同时读取
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
}
/*
* 查找可用的尾巴代理
* param proxies[[]string] 内置代理服务器数组
* return 可用的代理服务器IP,错误信息
*/
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 // 等待组用于等待所有检测goroutine完成
// 限制并发数,避免同时检测太多代理导致网络拥塞
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)
}
// 等待所有检测goroutine完成
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("所有尾巴代理都不可用")
}
/*
* 检测代理健康状态通过访问测试网站验证代理可用性
* param proxyURL[string] 代理服务器
* return 错误信息
*/
func checkProxyHealth(proxyURL string) error {
start := time.Now() // 记录开始时间,用于计算响应时间
// 创建HTTP请求设置代理和超时
req := gorequest.New().Proxy(proxyURL).Timeout(10 * time.Second)
// 发送GET请求到测试网站
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)
}
// 检查HTTP状态码
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
}
/*
* 更新代理健康状态
* param proxyURL[string] 代理服务器
* param success[bool] 是否成功
* param responseTime[time.Duration] 响应时间
*/
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
}
}
}
/*
* 获取代理主机名
* param proxyURL[string] 代理服务器
* return 代理服务器IP
*/
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 { func randomElement(slice []string) string {
randMutex.Lock() // 获取互斥锁
defer randMutex.Unlock() // 确保函数结束时释放锁
return slice[globalRand.Intn(len(slice))] // 随机选择一个元素
}
// 打乱服务器顺序
func shuffleServers(servers []string) []string {
randMutex.Lock() randMutex.Lock()
defer randMutex.Unlock() defer randMutex.Unlock()
// 创建服务器副本,避免修改原始切片 return slice[globalRand.Intn(len(slice))]
shuffled := make([]string, len(servers))
copy(shuffled, servers)
// 使用Fisher-Yates算法打乱顺序
globalRand.Shuffle(len(shuffled), func(i, j int) {
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
})
return shuffled
} }
/* // MachineCodeResponse 查询机器码响应结构体
* 检查卡密是否过期 type MachineCodeResponse struct {
* param tailCardSecret[string] 卡密 Code int `json:"code"`
* return 是否过期错误信息 Message string `json:"message"`
*/ Data MachineCodeData `json:"data"`
func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
// 获取机器码信息
code, err := getMachineCode(tailCardSecret)
if err != nil {
return false, fmt.Errorf("请求错误: %v", err)
}
// 解析过期时间字符串
targetTime, err := time.Parse("2006-01-02 15:04:05", code.Data.IpExpTime)
if err != nil {
return false, fmt.Errorf("时间格式错误: %v", err)
}
currentTime := time.Now()
// 卡密日期在当前日期之后表示有效
if targetTime.After(currentTime) {
return true, nil
} else {
// 卡密已过期
return false, fmt.Errorf("卡密已经过期!过期时间: %v", targetTime)
}
} }
// 定义响应结构体 // MachineCodeData 机器码数据
type getMachineCodeResp struct { type MachineCodeData struct {
Code int `json:"code"` // 状态码 MachineCode string `json:"machine_code"` // 机器码
Message string `json:"message"` // 消息 IpExpTime string `json:"ip_exp_time"` // 使用时间
Data struct { IpThread int `json:"ip_thread"` // 线程数
MachineCode string `json:"machine_code"` // 机器码 IpCardCode string `json:"ip_card_code"` // 卡密
IpExpTime string `json:"ip_exp_time"` // IP过期时间
IpThread int `json:"ip_thread"` // IP线程数
IpCardCode string `json:"ip_card_code"` // IP卡密
} `json:"data"`
} }
/* // 查询机器码
* 查询机器码 func getMachineCode(tailCardSecret string) (*MachineCodeData, error) {
* param tailCardSecret[string] 卡密 url := "http://36.134.51.135:7842/api/proxies/ip_show"
* return 响应结构体错误信息
*/
func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
url := "http://114.66.2.223:7842/api/proxies/ip_show"
// 构建请求数据
data := map[string]interface{}{ data := map[string]interface{}{
"ip_card_code": tailCardSecret, "ip_card_code": tailCardSecret,
"agent_id": 9999, "agent_id": 9999,
} }
// 发送POST请求
_, body, errs := gorequest.New().Post(url).Send(data).End() _, body, errs := gorequest.New().Post(url).Send(data).End()
if len(errs) > 0 { if len(errs) > 0 {
return nil, fmt.Errorf("查询机器码失败: %v", errs) return nil, fmt.Errorf("查询机器码失败: %v", errs)
} }
// 解析响应
var resp getMachineCodeResp var resp MachineCodeResponse
if err := json.Unmarshal([]byte(body), &resp); err != nil { if err := json.Unmarshal([]byte(body), &resp); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err) return nil, fmt.Errorf("解析响应失败: %v", err)
} }
// 处理不同的响应码
if resp.Code == 201 { switch resp.Code {
// 需要充值卡密 case 201:
machineCode, err := rechargeCard(tailCardSecret, resp.Data.MachineCode) machineCode, err := rechargeCard(tailCardSecret, resp.Data.MachineCode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp.Data.MachineCode = machineCode resp.Data.MachineCode = machineCode
return &resp, nil return &resp.Data, nil
} else if resp.Code == 200 { case 200:
// 成功获取机器码 return &resp.Data, nil
return &resp, nil default:
} else {
// 其他错误
return nil, fmt.Errorf("查询机器码失败: %s", resp.Message) return nil, fmt.Errorf("查询机器码失败: %s", resp.Message)
} }
} }
/* // 充值卡密
* 充值卡密
* param tailCardSecret[string] 卡密
* param machineCode[string] 机器码
* return 机器码错误信息
*/
func rechargeCard(tailCardSecret, machineCode string) (string, error) { func rechargeCard(tailCardSecret, machineCode string) (string, error) {
url := "http://114.66.2.223:7842/api/proxies/ip_recharge" url := "http://36.134.51.135:7842/api/proxies/ip_recharge"
// 构建请求数据
data := map[string]interface{}{ data := map[string]interface{}{
"machine_code": machineCode, "machine_code": machineCode,
"ip_card_code": tailCardSecret, "ip_card_code": tailCardSecret,
"agent_id": 9999, "agent_id": 9999,
} }
// 发送POST请求
_, body, errs := gorequest.New().Post(url).Send(data).End() _, body, errs := gorequest.New().Post(url).Send(data).End()
if len(errs) > 0 { if len(errs) > 0 {
return "", fmt.Errorf("充值卡密失败: %v", errs) return "", fmt.Errorf("充值卡密失败: %v", errs)
} }
// 解析响应
var resp struct { var resp struct {
Code int `json:"code"` // 状态码 Code int `json:"code"`
Message string `json:"message"` // 返回消息 Message string `json:"message"`
Data struct { Data struct {
IpExpTime string `json:"ip_exp_time"` // 过期时间 IpExpTime string `json:"ip_exp_time"`
IpThread int `json:"ip_thread"` // 线程 IpThread int `json:"ip_thread"`
MachineCode string `json:"machine_code"` // 机器码 MachineCode string `json:"machine_code"`
} `json:"data"` } `json:"data"`
} }
if err := json.Unmarshal([]byte(body), &resp); err != nil { if err := json.Unmarshal([]byte(body), &resp); err != nil {
return "", fmt.Errorf("解析响应失败: %v", err) return "", fmt.Errorf("解析响应失败: %v", err)
} }
// 检查响应状态
if resp.Code != 200 { if resp.Code != 200 {
return "", fmt.Errorf("充值卡密失败: %s", resp.Message) return "", fmt.Errorf("充值卡密失败: %s", resp.Message)
} }
return resp.Data.MachineCode, nil return resp.Data.MachineCode, nil
} }
/* // 获取代理服务器列表
* 获取代理服务器列表
* param machineCode[string] 机器码
* return 代理服务器组错误信息
*/
func getProxies(machineCode string) ([]string, error) { func getProxies(machineCode string) ([]string, error) {
log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
// 生成签名 // 生成签名
sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode)))) sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode))))
// 构建请求URL // 构建请求URL
GetProxiesUrl := fmt.Sprintf("http://114.66.2.223:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999", getProxiesUrl := fmt.Sprintf("http://36.134.51.135:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999",
machineCode, sign) machineCode, sign)
// 发送GET请求 // 发送GET请求
req := gorequest.New().Get(GetProxiesUrl).Timeout(20 * time.Second) req := gorequest.New().Get(getProxiesUrl).Timeout(20 * time.Second)
_, body, errs := req.End() _, body, errs := req.End()
if len(errs) > 0 { if len(errs) > 0 {
return nil, fmt.Errorf("获取代理失败: %v", errs) return nil, fmt.Errorf("获取代理失败: %v", errs)
} }
// 检查是否为JSON错误响应(响应以{开头,以}结尾) // 检查是否为JSON错误响应
if strings.HasPrefix(strings.TrimSpace(body), "{") && strings.HasSuffix(strings.TrimSpace(body), "}") { trimmedBody := strings.TrimSpace(body)
// 尝试解析为JSON错误响应 if strings.HasPrefix(trimmedBody, "{") && strings.HasSuffix(trimmedBody, "}") {
var errorResp struct { var errorResp struct {
Code int `json:"code"` Code int `json:"code"`
Message string `json:"message"` Message string `json:"message"`
@ -549,190 +179,139 @@ func getProxies(machineCode string) ([]string, error) {
} }
} }
// 解析响应(每行一个代理地址) // 解析响应
lines := strings.Split(strings.TrimSpace(body), "\n") lines := strings.Split(trimmedBody, "\n")
var proxies []string var proxies []string
for _, line := range lines { for _, line := range lines {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
// 过滤空行和JSON格式的行
if line != "" && !strings.HasPrefix(line, "{") { if line != "" && !strings.HasPrefix(line, "{") {
proxies = append(proxies, line) proxies = append(proxies, line)
} }
} }
// 检查是否获取到有效代理
if len(proxies) == 0 { if len(proxies) == 0 {
return nil, fmt.Errorf("未获取到有效代理") return nil, fmt.Errorf("未获取到有效代理")
} }
log.Printf("[INFO] 获取到 %d 个代理", len(proxies))
return proxies, nil return proxies, nil
} }
// 初始化代理信息 // 检查卡密是否过期
func initProxy() { func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
resp, err := getMachineCode(tailCardSecret)
}
// =================== C 导出函数 =======================
// GetProxyHealth 导出函数:获取代理健康状态(用于调试)
//
//export GetProxyHealth
func GetProxyHealth() *C.char {
proxyHealthMutex.RLock()
defer proxyHealthMutex.RUnlock()
// 构建健康信息映射
healthInfo := make(map[string]interface{})
for proxy, health := range proxyHealthMaps {
healthInfo[proxy] = map[string]interface{}{
"success_count": health.SuccessCount,
"fail_count": health.FailCount,
"last_check": health.LastCheck.Format(time.RFC3339),
"response_time": health.ResponseTime.String(),
"is_healthy": health.IsHealthy,
}
}
// 序列化为JSON
jsonData, err := json.Marshal(healthInfo)
if err != nil { if err != nil {
return C.CString(fmt.Sprintf(`{"error": "序列化健康信息失败: %v"}`, err)) return false, fmt.Errorf("请求错误: %v", err)
} }
return C.CString(string(jsonData)) targetTime, err := time.Parse("2006-01-02 15:04:05", resp.IpExpTime)
}
// ProxyTypeManager 导出函数:代理类型管理器
//
//export ProxyTypeManager
func ProxyTypeManager(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)
log.Printf("[DEBUG] 代理类型管理器调用: type=%s", goProxyType)
// 调用代理类型管理器
proxyURL, err := proxyTypeManager(goProxyType, goUsername, goPassword, goMachineCode)
if err != nil { if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err) return false, fmt.Errorf("时间格式错误: %v", err)
log.Printf("[ERROR] 代理类型管理器错误: %v", err)
return C.CString(errorMsg)
} }
log.Printf("[DEBUG] 代理类型管理器返回: %s", proxyURL) if targetTime.After(time.Now()) {
return C.CString(proxyURL) return true, nil
}
return false, fmt.Errorf("卡密已经过期!过期时间: %v", targetTime)
} }
// ProxyTypeManagerNew 导出函数:代理类型管理器 // =================== C导出函数 =======================
//
//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)
// 调用代理类型管理器 // ResponseAPI 统一响应数据结构
proxyURL, err := proxyTypeManagerNew(goProxyType, goUsername, goPassword, goMachineCode) type ResponseAPI struct {
Success bool `json:"success"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 生成JSON响应字符串
func jsonResponse(data interface{}, err error) string {
resp := ResponseAPI{
Success: err == nil,
Message: getErrorMsg(err),
Data: data,
}
bytes, _ := json.Marshal(resp)
return string(bytes)
}
// 获取错误信息
func getErrorMsg(err error) string {
if err != nil { if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err) return err.Error()
return C.CString(errorMsg)
} }
return C.CString(proxyURL) return ""
} }
// GetMachineCode 导出函数:查询机器码 // GetProxyByType 根据代理类型获取代理URL
//
//export GetProxyByType
func GetProxyByType(proxyType, machineCode *C.char) *C.char {
proxyTypeStr := C.GoString(proxyType)
machineCodeStr := C.GoString(machineCode)
proxyURL, err := getProxyByType(proxyTypeStr, machineCodeStr)
return C.CString(jsonResponse(proxyURL, err))
}
// GetTailProxyURL 获取尾巴代理URL
//
//export GetTailProxyURL
func GetTailProxyURL(machineCode *C.char) *C.char {
machineCodeStr := C.GoString(machineCode)
proxyURL, err := getTailProxyURL(machineCodeStr)
return C.CString(jsonResponse(proxyURL, err))
}
// GetMachineCode 查询机器码
// //
//export GetMachineCode //export GetMachineCode
func GetMachineCode(tailCardSecret *C.char) *C.char { func GetMachineCode(tailCardSecret *C.char) *C.char {
goTailCardSecret := C.GoString(tailCardSecret) tailCardSecretStr := C.GoString(tailCardSecret)
resp, err := getMachineCode(tailCardSecretStr)
resp, err := getMachineCode(goTailCardSecret) return C.CString(jsonResponse(resp, err))
if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err)
log.Printf("[ERROR] 查询机器码错误: %v", err)
return C.CString(errorMsg)
}
// 将响应转换为JSON
jsonData, err := json.Marshal(resp)
if err != nil {
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
log.Printf("[ERROR] 序列化机器码响应失败: %v", err)
return C.CString(errorMsg)
}
return C.CString(string(jsonData))
} }
// RechargeCard 导出函数:充值卡密 // RechargeCard 充值卡密
// //
//export RechargeCard //export RechargeCard
func RechargeCard(tailCardSecret, machineCode *C.char) *C.char { func RechargeCard(tailCardSecret, machineCode *C.char) *C.char {
goTailCardSecret := C.GoString(tailCardSecret) goTailCardSecret := C.GoString(tailCardSecret)
goMachineCode := C.GoString(machineCode) goMachineCode := C.GoString(machineCode)
log.Printf("[DEBUG] 充值卡密调用: card=%s, machine_code=%s", goTailCardSecret, goMachineCode)
newMachineCode, err := rechargeCard(goTailCardSecret, goMachineCode) newMachineCode, err := rechargeCard(goTailCardSecret, goMachineCode)
if err != nil { if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err) return C.CString(jsonResponse(nil, err))
log.Printf("[ERROR] 充值卡密错误: %v", err)
return C.CString(errorMsg)
} }
// 构建成功响应
response := map[string]interface{}{ response := map[string]interface{}{
"success": true, "machineCode": newMachineCode,
"machine_code": newMachineCode, "rechargeInfo": "充值成功",
"message": "充值成功",
} }
jsonData, err := json.Marshal(response) return C.CString(jsonResponse(response, nil))
if err != nil {
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
log.Printf("[ERROR] 序列化充值响应失败: %v", err)
return C.CString(errorMsg)
}
log.Printf("[DEBUG] 充值卡密成功: new_machine_code=%s", newMachineCode)
return C.CString(string(jsonData))
} }
// GetProxies 导出函数:获取代理服务器列表 // GetProxies 获取代理服务器列表
// //
//export GetProxies //export GetProxies
func GetProxies(machineCode *C.char) *C.char { func GetProxies(machineCode *C.char) *C.char {
goMachineCode := C.GoString(machineCode) goMachineCode := C.GoString(machineCode)
log.Printf("[DEBUG] 获取代理服务器列表调用: machine_code=%s", goMachineCode)
proxies, err := getProxies(goMachineCode) proxies, err := getProxies(goMachineCode)
if err != nil { if err != nil {
errorMsg := fmt.Sprintf("ERROR: %v", err) return C.CString(jsonResponse(nil, err))
log.Printf("[ERROR] 获取代理服务器列表错误: %v", err)
return C.CString(errorMsg)
} }
// 将代理列表转换为JSON
response := map[string]interface{}{ response := map[string]interface{}{
"success": true,
"count": len(proxies), "count": len(proxies),
"proxies": proxies, "proxies": proxies,
} }
jsonData, err := json.Marshal(response) return C.CString(jsonResponse(response, nil))
if err != nil {
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
log.Printf("[ERROR] 序列化代理列表失败: %v", err)
return C.CString(errorMsg)
}
log.Printf("[DEBUG] 获取代理服务器列表成功: count=%d", len(proxies))
return C.CString(string(jsonData))
} }
// CheckTailCardSecretExpired 导出函数:检查卡密是否过期 // CheckTailCardSecretExpired 检查卡密是否过期
// //
//export CheckTailCardSecretExpired //export CheckTailCardSecretExpired
func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char { func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
@ -740,104 +319,30 @@ func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
isValid, err := checkTailCardSecretExpired(goTailCardSecret) isValid, err := checkTailCardSecretExpired(goTailCardSecret)
if err != nil { if err != nil {
// 如果是过期错误,返回特定格式 return C.CString(jsonResponse(nil, err))
if strings.Contains(err.Error(), "卡密已经过期") {
response := map[string]interface{}{
"is_valid": false,
"message": err.Error(),
}
jsonData, _ := json.Marshal(response)
return C.CString(string(jsonData))
}
errorMsg := fmt.Sprintf("ERROR: %v", err)
log.Printf("[ERROR] 检查卡密过期错误: %v", err)
return C.CString(errorMsg)
} }
response := map[string]interface{}{ response := map[string]interface{}{
"is_valid": isValid, "isValid": isValid,
"message": "卡密有效", "checkInfo": "卡密有效",
} }
return C.CString(jsonResponse(response, nil))
jsonData, err := json.Marshal(response)
if err != nil {
errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
return C.CString(errorMsg)
}
return C.CString(string(jsonData))
} }
// InitProxyManager 导出函数:初始化代理管理器 // FreeCString 释放C字符串内存
//
//export InitProxyManager
func InitProxyManager(serversJson, username, password, tailCardSecret, proxyType *C.char) *C.char {
goServersJson := C.GoString(serversJson)
goUsername := C.GoString(username)
goPassword := C.GoString(password)
goTailCardSecret := C.GoString(tailCardSecret)
goProxyType := C.GoString(proxyType)
log.Printf("[DEBUG] 初始化代理管理器调用: type=%s", goProxyType)
// 解析服务器列表
var serverList []string
if goServersJson != "" {
if err := json.Unmarshal([]byte(goServersJson), &serverList); err != nil {
errorMsg := fmt.Sprintf("ERROR: 解析服务器列表失败: %v", err)
log.Printf("[ERROR] 解析服务器列表失败: %v", err)
return C.CString(errorMsg)
}
}
// 更新全局服务器列表
if len(serverList) > 0 {
randMutex.Lock()
servers = serverList
randMutex.Unlock()
log.Printf("[INFO] 更新服务器列表,共 %d 个服务器", len(servers))
}
// 创建代理管理器
manager := ProxyManager{
servers: servers,
username: goUsername,
password: goPassword,
tailCardSecret: goTailCardSecret,
proxyType: goProxyType,
}
// 序列化管理器信息
jsonData, err := json.Marshal(manager)
if err != nil {
errorMsg := fmt.Sprintf("ERROR: 序列化代理管理器失败: %v", err)
log.Printf("[ERROR] 序列化代理管理器失败: %v", err)
return C.CString(errorMsg)
}
log.Printf("[DEBUG] 代理管理器初始化成功")
return C.CString(string(jsonData))
}
// FreeCString 导出函数释放C字符串内存
// //
//export FreeCString //export FreeCString
func FreeCString(str *C.char) { func FreeCString(str *C.char) {
C.free(unsafe.Pointer(str)) C.free(unsafe.Pointer(str))
} }
// PROXY_VERSION 版本号
const (
PROXY_VERSION = "v2"
)
// GetVersion 获取版本 // GetVersion 获取版本
// //
//export GetVersion //export GetVersion
func GetVersion() *C.char { func GetVersion() *C.char {
return C.CString(PROXY_VERSION) return C.CString("v3")
} }
// 空main函数编译DLL时需要 // 空main函数
func main() { func main() {
} }

View File

@ -1,133 +0,0 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
extern size_t _GoStringLen(_GoString_ s);
extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
/* Start of preamble from import "C" comments. */
#line 3 "proxy.go"
#include <stdlib.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
#include <complex>
typedef std::complex<float> GoComplex64;
typedef std::complex<double> GoComplex128;
#endif
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
// 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

848
proxy/proxyLao.go Normal file
View File

@ -0,0 +1,848 @@
package main
///*
//#include <stdlib.h>
//*/
//import "C"
//import (
// "crypto/md5"
// "encoding/json"
// "fmt"
// "github.com/parnurzeal/gorequest"
// "log"
// "math/rand"
// "net/url"
// "strings"
// "sync"
// "time"
// "unsafe"
//)
//
//// 代理类型常量
//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 // 全局随机数生成器
// // 代理健康状态管理
// // 代理健康状态映射表key为代理主机名value为健康状态
// 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
//}
//
//// ProxyHealth 代理健康状态结构体,用于跟踪代理的健康状况
//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代理类型管理器根据代理类型构建不同的代理URL
//* param proxyType[string] 代理类型
//* param username[string] 代理用户名
//* param password[string] 代理密码
//* param machineCode[string] 机器码
//* return 代理服务器IP,错误信息
// */
//func proxyTypeManager(proxyType, username, password, machineCode string) (string, error) {
// switch proxyType {
// case CalfElephantProxyType:
// // 小象代理使用用户名和密码构建代理URL
// return buildCalfElephantProxyURL(username, password)
// case TailProxyType:
// // 尾巴代理使用机器码构建代理URL
// return buildTailProxyURL(machineCode)
// default:
// // 不支持的代理类型,返回错误
// return "", fmt.Errorf("不支持的代理类型: %s", proxyType)
// }
//}
//
//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] 代理用户名
//* param password[string] 代理密码
//* return 代理服务器IP,错误信息
// */
//func buildCalfElephantProxyURL(username, password string) (string, error) {
// // 随机选择一个代理服务器
// server := randomServer()
// // 构建代理URL格式http://用户名:密码@服务器:端口
// proxyURL := fmt.Sprintf("http://%s:%s@%s:%d",
// url.QueryEscape(username), // URL编码用户名防止特殊字符问题
// url.QueryEscape(password), // URL编码密码防止特殊字符问题
// 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
//}
//
///*
//* 尝试下一个小象代理服务器
//* param username[string] 代理用户名
//* param password[string] 代理密码
//* param failedServer[string] 代理服务器
//* return 代理服务器IP,错误信息
// */
//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) {
// // 构建代理URL
// 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
//* param machineCode[string] 机器码
//* return 代理服务器IP,错误信息
// */
//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)
//}
//
///*
//* 过滤健康代理,返回当前健康的代理列表
//* param proxies[[]string] 内置代理服务器数组
//* return 内置代理服务器数组,错误信息
// */
//func filterHealthyProxies(proxies []string) []string {
// // 获取读锁允许多个goroutine同时读取
// 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
//}
//
///*
//* 查找可用的尾巴代理
//* param proxies[[]string] 内置代理服务器数组
//* return 可用的代理服务器IP,错误信息
// */
//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 // 等待组用于等待所有检测goroutine完成
//
// // 限制并发数,避免同时检测太多代理导致网络拥塞
// 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)
// }
// // 等待所有检测goroutine完成
// 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("所有尾巴代理都不可用")
//}
//
///*
//* 检测代理健康状态,通过访问测试网站验证代理可用性
//* param proxyURL[string] 代理服务器
//* return 错误信息
// */
//func checkProxyHealth(proxyURL string) error {
// start := time.Now() // 记录开始时间,用于计算响应时间
// // 创建HTTP请求设置代理和超时
// req := gorequest.New().Proxy(proxyURL).Timeout(10 * time.Second)
// // 发送GET请求到测试网站
// 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)
// }
// // 检查HTTP状态码
// 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
//}
//
///*
//* 更新代理健康状态
//* param proxyURL[string] 代理服务器
//* param success[bool] 是否成功
//* param responseTime[time.Duration] 响应时间
// */
//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
// }
// }
//}
//
///*
//* 获取代理主机名
//* param proxyURL[string] 代理服务器
//* return 代理服务器IP
// */
//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)
// // 使用Fisher-Yates算法打乱顺序
// globalRand.Shuffle(len(shuffled), func(i, j int) {
// shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
// })
// return shuffled
//}
//
///*
//* 检查卡密是否过期
//* param tailCardSecret[string] 卡密
//* return 是否过期,错误信息
// */
//func checkTailCardSecretExpired(tailCardSecret string) (bool, error) {
// // 获取机器码信息
// code, err := getMachineCode(tailCardSecret)
// if err != nil {
// return false, fmt.Errorf("请求错误: %v", err)
// }
// // 解析过期时间字符串
// targetTime, err := time.Parse("2006-01-02 15:04:05", code.Data.IpExpTime)
// if err != nil {
// return false, fmt.Errorf("时间格式错误: %v", err)
// }
// currentTime := time.Now()
// // 卡密日期在当前日期之后表示有效
// if targetTime.After(currentTime) {
// return true, nil
// } else {
// // 卡密已过期
// return false, fmt.Errorf("卡密已经过期!过期时间: %v", targetTime)
// }
//}
//
//// 定义响应结构体
//type getMachineCodeResp struct {
// Code int `json:"code"` // 状态码
// Message string `json:"message"` // 消息
// Data struct {
// MachineCode string `json:"machine_code"` // 机器码
// IpExpTime string `json:"ip_exp_time"` // IP过期时间
// IpThread int `json:"ip_thread"` // IP线程数
// IpCardCode string `json:"ip_card_code"` // IP卡密
// } `json:"data"`
//}
//
///*
//* 查询机器码
//* param tailCardSecret[string] 卡密
//* return 响应结构体,错误信息
// */
//func getMachineCode(tailCardSecret string) (*getMachineCodeResp, error) {
// url := "http://114.66.2.223:7842/api/proxies/ip_show"
// // 构建请求数据
// data := map[string]interface{}{
// "ip_card_code": tailCardSecret,
// "agent_id": 9999,
// }
// // 发送POST请求
// _, body, errs := gorequest.New().Post(url).Send(data).End()
// if len(errs) > 0 {
// return nil, fmt.Errorf("查询机器码失败: %v", errs)
// }
// // 解析响应
// var resp getMachineCodeResp
// if err := json.Unmarshal([]byte(body), &resp); err != nil {
// return nil, fmt.Errorf("解析响应失败: %v", err)
// }
// // 处理不同的响应码
// if resp.Code == 201 {
// // 需要充值卡密
// machineCode, err := rechargeCard(tailCardSecret, resp.Data.MachineCode)
// if err != nil {
// return nil, err
// }
// resp.Data.MachineCode = machineCode
// return &resp, nil
// } else if resp.Code == 200 {
// // 成功获取机器码
// return &resp, nil
// } else {
// // 其他错误
// return nil, fmt.Errorf("查询机器码失败: %s", resp.Message)
// }
//}
//
///*
//* 充值卡密
//* param tailCardSecret[string] 卡密
//* param machineCode[string] 机器码
//* return 机器码,错误信息
// */
//func rechargeCard(tailCardSecret, machineCode string) (string, error) {
// url := "http://114.66.2.223:7842/api/proxies/ip_recharge"
// // 构建请求数据
// data := map[string]interface{}{
// "machine_code": machineCode,
// "ip_card_code": tailCardSecret,
// "agent_id": 9999,
// }
// // 发送POST请求
// _, body, errs := gorequest.New().Post(url).Send(data).End()
// if len(errs) > 0 {
// return "", fmt.Errorf("充值卡密失败: %v", errs)
// }
// // 解析响应
// var resp struct {
// Code int `json:"code"` // 状态码
// Message string `json:"message"` // 返回消息
// Data struct {
// IpExpTime string `json:"ip_exp_time"` // 过期时间
// IpThread int `json:"ip_thread"` // 线程
// MachineCode string `json:"machine_code"` // 机器码
// } `json:"data"`
// }
// if err := json.Unmarshal([]byte(body), &resp); err != nil {
// return "", fmt.Errorf("解析响应失败: %v", err)
// }
//
// // 检查响应状态
// if resp.Code != 200 {
// return "", fmt.Errorf("充值卡密失败: %s", resp.Message)
// }
// return resp.Data.MachineCode, nil
//}
//
///*
//* 获取代理服务器列表
//* param machineCode[string] 机器码
//* return 代理服务器组,错误信息
// */
//func getProxies(machineCode string) ([]string, error) {
// log.Printf("[INFO] 开始获取代理列表: %s", machineCode)
// // 生成签名
// sign := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("9999%s9999", machineCode))))
//
// // 构建请求URL
// GetProxiesUrl := fmt.Sprintf("http://114.66.2.223:7842/api/proxies/get_proxy?machine_code=%s&sign=%s&agent_id=9999",
// machineCode, sign)
//
// // 发送GET请求
// 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)
// // 过滤空行和JSON格式的行
// if line != "" && !strings.HasPrefix(line, "{") {
// proxies = append(proxies, line)
// }
// }
// // 检查是否获取到有效代理
// if len(proxies) == 0 {
// return nil, fmt.Errorf("未获取到有效代理")
// }
//
// log.Printf("[INFO] 获取到 %d 个代理", len(proxies))
// return proxies, nil
//}
//
//// 初始化代理信息
//func initProxy() {
//
//}
//
//// =================== C 导出函数 =======================
//
//// GetProxyHealth 导出函数:获取代理健康状态(用于调试)
////
////export GetProxyHealth
//func GetProxyHealth() *C.char {
// proxyHealthMutex.RLock()
// defer proxyHealthMutex.RUnlock()
// // 构建健康信息映射
// healthInfo := make(map[string]interface{})
// for proxy, health := range proxyHealthMaps {
// healthInfo[proxy] = map[string]interface{}{
// "success_count": health.SuccessCount,
// "fail_count": health.FailCount,
// "last_check": health.LastCheck.Format(time.RFC3339),
// "response_time": health.ResponseTime.String(),
// "is_healthy": health.IsHealthy,
// }
// }
// // 序列化为JSON
// jsonData, err := json.Marshal(healthInfo)
// if err != nil {
// return C.CString(fmt.Sprintf(`{"error": "序列化健康信息失败: %v"}`, err))
// }
//
// return C.CString(string(jsonData))
//}
//
//// ProxyTypeManager 导出函数:代理类型管理器
////
////export ProxyTypeManager
//func ProxyTypeManager(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)
//
// log.Printf("[DEBUG] 代理类型管理器调用: type=%s", goProxyType)
// // 调用代理类型管理器
// proxyURL, err := proxyTypeManager(goProxyType, goUsername, goPassword, goMachineCode)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: %v", err)
// log.Printf("[ERROR] 代理类型管理器错误: %v", err)
// return C.CString(errorMsg)
// }
//
// log.Printf("[DEBUG] 代理类型管理器返回: %s", proxyURL)
// return C.CString(proxyURL)
//}
//
//// 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)
//
// resp, err := getMachineCode(goTailCardSecret)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: %v", err)
// log.Printf("[ERROR] 查询机器码错误: %v", err)
// return C.CString(errorMsg)
// }
//
// // 将响应转换为JSON
// jsonData, err := json.Marshal(resp)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
// log.Printf("[ERROR] 序列化机器码响应失败: %v", err)
// return C.CString(errorMsg)
// }
//
// return C.CString(string(jsonData))
//}
//
//// RechargeCard 导出函数:充值卡密
////
////export RechargeCard
//func RechargeCard(tailCardSecret, machineCode *C.char) *C.char {
// goTailCardSecret := C.GoString(tailCardSecret)
// goMachineCode := C.GoString(machineCode)
// log.Printf("[DEBUG] 充值卡密调用: card=%s, machine_code=%s", goTailCardSecret, goMachineCode)
//
// newMachineCode, err := rechargeCard(goTailCardSecret, goMachineCode)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: %v", err)
// log.Printf("[ERROR] 充值卡密错误: %v", err)
// return C.CString(errorMsg)
// }
//
// // 构建成功响应
// response := map[string]interface{}{
// "success": true,
// "machine_code": newMachineCode,
// "message": "充值成功",
// }
//
// jsonData, err := json.Marshal(response)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
// log.Printf("[ERROR] 序列化充值响应失败: %v", err)
// return C.CString(errorMsg)
// }
//
// log.Printf("[DEBUG] 充值卡密成功: new_machine_code=%s", newMachineCode)
// return C.CString(string(jsonData))
//}
//
//// GetProxies 导出函数:获取代理服务器列表
////
////export GetProxies
//func GetProxies(machineCode *C.char) *C.char {
// goMachineCode := C.GoString(machineCode)
// log.Printf("[DEBUG] 获取代理服务器列表调用: machine_code=%s", goMachineCode)
//
// proxies, err := getProxies(goMachineCode)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: %v", err)
// log.Printf("[ERROR] 获取代理服务器列表错误: %v", err)
// return C.CString(errorMsg)
// }
//
// // 将代理列表转换为JSON
// response := map[string]interface{}{
// "success": true,
// "count": len(proxies),
// "proxies": proxies,
// }
//
// jsonData, err := json.Marshal(response)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
// log.Printf("[ERROR] 序列化代理列表失败: %v", err)
// return C.CString(errorMsg)
// }
//
// log.Printf("[DEBUG] 获取代理服务器列表成功: count=%d", len(proxies))
// return C.CString(string(jsonData))
//}
//
//// CheckTailCardSecretExpired 导出函数:检查卡密是否过期
////
////export CheckTailCardSecretExpired
//func CheckTailCardSecretExpired(tailCardSecret *C.char) *C.char {
// goTailCardSecret := C.GoString(tailCardSecret)
//
// isValid, err := checkTailCardSecretExpired(goTailCardSecret)
// if err != nil {
// // 如果是过期错误,返回特定格式
// if strings.Contains(err.Error(), "卡密已经过期") {
// response := map[string]interface{}{
// "is_valid": false,
// "message": err.Error(),
// }
// jsonData, _ := json.Marshal(response)
// return C.CString(string(jsonData))
// }
//
// errorMsg := fmt.Sprintf("ERROR: %v", err)
// log.Printf("[ERROR] 检查卡密过期错误: %v", err)
// return C.CString(errorMsg)
// }
//
// response := map[string]interface{}{
// "is_valid": isValid,
// "message": "卡密有效",
// }
//
// jsonData, err := json.Marshal(response)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: 序列化响应失败: %v", err)
// return C.CString(errorMsg)
// }
// return C.CString(string(jsonData))
//}
//
//// InitProxyManager 导出函数:初始化代理管理器
////
////export InitProxyManager
//func InitProxyManager(serversJson, username, password, tailCardSecret, proxyType *C.char) *C.char {
// goServersJson := C.GoString(serversJson)
// goUsername := C.GoString(username)
// goPassword := C.GoString(password)
// goTailCardSecret := C.GoString(tailCardSecret)
// goProxyType := C.GoString(proxyType)
//
// log.Printf("[DEBUG] 初始化代理管理器调用: type=%s", goProxyType)
//
// // 解析服务器列表
// var serverList []string
// if goServersJson != "" {
// if err := json.Unmarshal([]byte(goServersJson), &serverList); err != nil {
// errorMsg := fmt.Sprintf("ERROR: 解析服务器列表失败: %v", err)
// log.Printf("[ERROR] 解析服务器列表失败: %v", err)
// return C.CString(errorMsg)
// }
// }
//
// // 更新全局服务器列表
// if len(serverList) > 0 {
// randMutex.Lock()
// servers = serverList
// randMutex.Unlock()
// log.Printf("[INFO] 更新服务器列表,共 %d 个服务器", len(servers))
// }
//
// // 创建代理管理器
// manager := ProxyManager{
// servers: servers,
// username: goUsername,
// password: goPassword,
// tailCardSecret: goTailCardSecret,
// proxyType: goProxyType,
// }
//
// // 序列化管理器信息
// jsonData, err := json.Marshal(manager)
// if err != nil {
// errorMsg := fmt.Sprintf("ERROR: 序列化代理管理器失败: %v", err)
// log.Printf("[ERROR] 序列化代理管理器失败: %v", err)
// return C.CString(errorMsg)
// }
//
// log.Printf("[DEBUG] 代理管理器初始化成功")
// return C.CString(string(jsonData))
//}
//
//// FreeCString 导出函数释放C字符串内存
////
////export FreeCString
//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() {
// managerNew, err := proxyTypeManagerNew("", "", "", "07f4d0fbcff99966c2b37b0c1fb7f01c")
// if err != nil {
// fmt.Println(err.Error())
// }
// fmt.Println(managerNew)
//}

View File

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

675
redis/redis.go Normal file
View File

@ -0,0 +1,675 @@
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/redis/go-redis/v9"
)
// RedisClient Redis客户端结构体
type RedisClient struct {
Client *redis.Client
Ctx context.Context
}
// RedisConfig Redis配置结构体
type RedisConfig struct {
Addr string // 服务器地址:端口
Password string // 密码
DB int // 数据库编号
PoolSize int // 连接池大小
MinIdleConns int // 最小空闲连接数
DialTimeout time.Duration // 连接超时
ReadTimeout time.Duration // 读取超时
WriteTimeout time.Duration // 写入超时
PoolTimeout time.Duration // 获取连接超时
}
// NewRedisClient 创建新的Redis客户端
func NewRedisClient(config *RedisConfig) *RedisClient {
if config == nil {
// 默认配置 - 根据您的需求设置
config = &RedisConfig{
Addr: "36.212.20.113:7963",
Password: "",
DB: 2,
PoolSize: 10,
MinIdleConns: 5,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
PoolTimeout: 4 * time.Second,
}
}
rdb := redis.NewClient(&redis.Options{
Addr: config.Addr,
Password: config.Password,
DB: config.DB,
PoolSize: config.PoolSize,
MinIdleConns: config.MinIdleConns,
DialTimeout: config.DialTimeout,
ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout,
PoolTimeout: config.PoolTimeout,
})
return &RedisClient{
Client: rdb,
Ctx: context.Background(),
}
}
// Close 关闭Redis连接
func (r *RedisClient) Close() error {
return r.Client.Close()
}
// Ping 测试连接
func (r *RedisClient) Ping() error {
pong, err := r.Client.Ping(r.Ctx).Result()
if err != nil {
return fmt.Errorf("Redis连接失败: %v", err)
}
log.Printf("Redis连接成功: %s", pong)
return nil
}
// ==================== 字符串操作 ====================
// Set 设置键值对
func (r *RedisClient) Set(key string, value interface{}, expiration time.Duration) error {
err := r.Client.Set(r.Ctx, key, value, expiration).Err()
if err != nil {
return fmt.Errorf("设置键 %s 失败: %v", key, err)
}
return nil
}
// Get 获取字符串值
func (r *RedisClient) Get(key string) (string, error) {
val, err := r.Client.Get(r.Ctx, key).Result()
if err == redis.Nil {
return "", fmt.Errorf("键 %s 不存在", key)
} else if err != nil {
return "", fmt.Errorf("获取键 %s 失败: %v", key, err)
}
return val, nil
}
// GetInt 获取整数值
func (r *RedisClient) GetInt(key string) (int, error) {
val, err := r.Client.Get(r.Ctx, key).Int()
if err == redis.Nil {
return 0, fmt.Errorf("键 %s 不存在", key)
} else if err != nil {
return 0, fmt.Errorf("获取键 %s 失败: %v", key, err)
}
return val, nil
}
// GetInt64 获取64位整数值
func (r *RedisClient) GetInt64(key string) (int64, error) {
val, err := r.Client.Get(r.Ctx, key).Int64()
if err == redis.Nil {
return 0, fmt.Errorf("键 %s 不存在", key)
} else if err != nil {
return 0, fmt.Errorf("获取键 %s 失败: %v", key, err)
}
return val, nil
}
// GetFloat64 获取浮点数值
func (r *RedisClient) GetFloat64(key string) (float64, error) {
val, err := r.Client.Get(r.Ctx, key).Float64()
if err == redis.Nil {
return 0, fmt.Errorf("键 %s 不存在", key)
} else if err != nil {
return 0, fmt.Errorf("获取键 %s 失败: %v", key, err)
}
return val, nil
}
// GetBool 获取布尔值
func (r *RedisClient) GetBool(key string) (bool, error) {
val, err := r.Client.Get(r.Ctx, key).Bool()
if err == redis.Nil {
return false, fmt.Errorf("键 %s 不存在", key)
} else if err != nil {
return false, fmt.Errorf("获取键 %s 失败: %v", key, err)
}
return val, nil
}
// SetNX 只有当键不存在时设置值
func (r *RedisClient) SetNX(key string, value interface{}, expiration time.Duration) (bool, error) {
success, err := r.Client.SetNX(r.Ctx, key, value, expiration).Result()
if err != nil {
return false, fmt.Errorf("SetNX操作失败: %v", err)
}
return success, nil
}
// SetXX 只有当键存在时设置值
func (r *RedisClient) SetXX(key string, value interface{}, expiration time.Duration) (bool, error) {
success, err := r.Client.SetXX(r.Ctx, key, value, expiration).Result()
if err != nil {
return false, fmt.Errorf("SetXX操作失败: %v", err)
}
return success, nil
}
// MSet 批量设置键值对
func (r *RedisClient) MSet(values map[string]interface{}) error {
pairs := make([]interface{}, 0, len(values)*2)
for k, v := range values {
pairs = append(pairs, k, v)
}
err := r.Client.MSet(r.Ctx, pairs...).Err()
if err != nil {
return fmt.Errorf("批量设置失败: %v", err)
}
return nil
}
// MGet 批量获取值
func (r *RedisClient) MGet(keys ...string) ([]interface{}, error) {
vals, err := r.Client.MGet(r.Ctx, keys...).Result()
if err != nil {
return nil, fmt.Errorf("批量获取失败: %v", err)
}
return vals, nil
}
// Incr 自增1
func (r *RedisClient) Incr(key string) (int64, error) {
val, err := r.Client.Incr(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("自增失败: %v", err)
}
return val, nil
}
// IncrBy 自增指定值
func (r *RedisClient) IncrBy(key string, value int64) (int64, error) {
val, err := r.Client.IncrBy(r.Ctx, key, value).Result()
if err != nil {
return 0, fmt.Errorf("自增 %d 失败: %v", value, err)
}
return val, nil
}
// Decr 自减1
func (r *RedisClient) Decr(key string) (int64, error) {
val, err := r.Client.Decr(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("自减失败: %v", err)
}
return val, nil
}
// DecrBy 自减指定值
func (r *RedisClient) DecrBy(key string, value int64) (int64, error) {
val, err := r.Client.DecrBy(r.Ctx, key, value).Result()
if err != nil {
return 0, fmt.Errorf("自减 %d 失败: %v", value, err)
}
return val, nil
}
// Append 追加字符串
func (r *RedisClient) Append(key, value string) (int64, error) {
length, err := r.Client.Append(r.Ctx, key, value).Result()
if err != nil {
return 0, fmt.Errorf("追加字符串失败: %v", err)
}
return length, nil
}
// StrLen 获取字符串长度
func (r *RedisClient) StrLen(key string) (int64, error) {
length, err := r.Client.StrLen(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("获取字符串长度失败: %v", err)
}
return length, nil
}
// ==================== 键操作 ====================
// Exists 检查键是否存在
func (r *RedisClient) Exists(keys ...string) (int64, error) {
count, err := r.Client.Exists(r.Ctx, keys...).Result()
if err != nil {
return 0, fmt.Errorf("检查键是否存在失败: %v", err)
}
return count, nil
}
// Del 删除键
func (r *RedisClient) Del(keys ...string) (int64, error) {
count, err := r.Client.Del(r.Ctx, keys...).Result()
if err != nil {
return 0, fmt.Errorf("删除键失败: %v", err)
}
return count, nil
}
// Expire 设置过期时间
func (r *RedisClient) Expire(key string, expiration time.Duration) (bool, error) {
success, err := r.Client.Expire(r.Ctx, key, expiration).Result()
if err != nil {
return false, fmt.Errorf("设置过期时间失败: %v", err)
}
return success, nil
}
// TTL 获取剩余过期时间
func (r *RedisClient) TTL(key string) (time.Duration, error) {
ttl, err := r.Client.TTL(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("获取过期时间失败: %v", err)
}
return ttl, nil
}
// Persist 移除过期时间
func (r *RedisClient) Persist(key string) (bool, error) {
success, err := r.Client.Persist(r.Ctx, key).Result()
if err != nil {
return false, fmt.Errorf("移除过期时间失败: %v", err)
}
return success, nil
}
// Type 获取键类型
func (r *RedisClient) Type(key string) (string, error) {
typeStr, err := r.Client.Type(r.Ctx, key).Result()
if err != nil {
return "", fmt.Errorf("获取键类型失败: %v", err)
}
return typeStr, nil
}
// Keys 查找所有匹配的键
func (r *RedisClient) Keys(pattern string) ([]string, error) {
keys, err := r.Client.Keys(r.Ctx, pattern).Result()
if err != nil {
return nil, fmt.Errorf("查找键失败: %v", err)
}
return keys, nil
}
// RandomKey 随机获取一个键
func (r *RedisClient) RandomKey() (string, error) {
key, err := r.Client.RandomKey(r.Ctx).Result()
if err != nil {
return "", fmt.Errorf("随机获取键失败: %v", err)
}
return key, nil
}
// Rename 重命名键
func (r *RedisClient) Rename(key, newKey string) error {
err := r.Client.Rename(r.Ctx, key, newKey).Err()
if err != nil {
return fmt.Errorf("重命名键失败: %v", err)
}
return nil
}
// RenameNX 只有当新键不存在时重命名
func (r *RedisClient) RenameNX(key, newKey string) (bool, error) {
success, err := r.Client.RenameNX(r.Ctx, key, newKey).Result()
if err != nil {
return false, fmt.Errorf("重命名键失败: %v", err)
}
return success, nil
}
// ==================== Hash操作 ====================
// HSet 设置Hash字段
func (r *RedisClient) HSet(key string, values ...interface{}) error {
err := r.Client.HSet(r.Ctx, key, values...).Err()
if err != nil {
return fmt.Errorf("设置Hash失败: %v", err)
}
return nil
}
// HGet 获取Hash字段
func (r *RedisClient) HGet(key, field string) (string, error) {
val, err := r.Client.HGet(r.Ctx, key, field).Result()
if err == redis.Nil {
return "", fmt.Errorf("字段 %s 不存在", field)
} else if err != nil {
return "", fmt.Errorf("获取Hash字段失败: %v", err)
}
return val, nil
}
// HGetAll 获取所有Hash字段
func (r *RedisClient) HGetAll(key string) (map[string]string, error) {
vals, err := r.Client.HGetAll(r.Ctx, key).Result()
if err != nil {
return nil, fmt.Errorf("获取所有Hash字段失败: %v", err)
}
return vals, nil
}
// HDel 删除Hash字段
func (r *RedisClient) HDel(key string, fields ...string) (int64, error) {
count, err := r.Client.HDel(r.Ctx, key, fields...).Result()
if err != nil {
return 0, fmt.Errorf("删除Hash字段失败: %v", err)
}
return count, nil
}
// HExists 检查Hash字段是否存在
func (r *RedisClient) HExists(key, field string) (bool, error) {
exists, err := r.Client.HExists(r.Ctx, key, field).Result()
if err != nil {
return false, fmt.Errorf("检查Hash字段失败: %v", err)
}
return exists, nil
}
// HKeys 获取所有Hash字段名
func (r *RedisClient) HKeys(key string) ([]string, error) {
keys, err := r.Client.HKeys(r.Ctx, key).Result()
if err != nil {
return nil, fmt.Errorf("获取Hash字段名失败: %v", err)
}
return keys, nil
}
// HVals 获取所有Hash字段值
func (r *RedisClient) HVals(key string) ([]string, error) {
vals, err := r.Client.HVals(r.Ctx, key).Result()
if err != nil {
return nil, fmt.Errorf("获取Hash字段值失败: %v", err)
}
return vals, nil
}
// HLen 获取Hash字段数量
func (r *RedisClient) HLen(key string) (int64, error) {
count, err := r.Client.HLen(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("获取Hash字段数量失败: %v", err)
}
return count, nil
}
// HIncrBy Hash字段自增
func (r *RedisClient) HIncrBy(key, field string, incr int64) (int64, error) {
val, err := r.Client.HIncrBy(r.Ctx, key, field, incr).Result()
if err != nil {
return 0, fmt.Errorf("Hash字段自增失败: %v", err)
}
return val, nil
}
// ==================== List操作 ====================
// LPush 从左侧插入列表
func (r *RedisClient) LPush(key string, values ...interface{}) (int64, error) {
count, err := r.Client.LPush(r.Ctx, key, values...).Result()
if err != nil {
return 0, fmt.Errorf("LPush失败: %v", err)
}
return count, nil
}
// RPush 从右侧插入列表
func (r *RedisClient) RPush(key string, values ...interface{}) (int64, error) {
count, err := r.Client.RPush(r.Ctx, key, values...).Result()
if err != nil {
return 0, fmt.Errorf("RPush失败: %v", err)
}
return count, nil
}
// LPop 从左侧弹出元素
func (r *RedisClient) LPop(key string) (string, error) {
val, err := r.Client.LPop(r.Ctx, key).Result()
if err == redis.Nil {
return "", fmt.Errorf("列表为空")
} else if err != nil {
return "", fmt.Errorf("LPop失败: %v", err)
}
return val, nil
}
// RPop 从右侧弹出元素
func (r *RedisClient) RPop(key string) (string, error) {
val, err := r.Client.RPop(r.Ctx, key).Result()
if err == redis.Nil {
return "", fmt.Errorf("列表为空")
} else if err != nil {
return "", fmt.Errorf("RPop失败: %v", err)
}
return val, nil
}
// LLen 获取列表长度
func (r *RedisClient) LLen(key string) (int64, error) {
length, err := r.Client.LLen(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("获取列表长度失败: %v", err)
}
return length, nil
}
// LRange 获取列表范围
func (r *RedisClient) LRange(key string, start, stop int64) ([]string, error) {
vals, err := r.Client.LRange(r.Ctx, key, start, stop).Result()
if err != nil {
return nil, fmt.Errorf("获取列表范围失败: %v", err)
}
return vals, nil
}
// ==================== Set操作 ====================
// SAdd 添加集合元素
func (r *RedisClient) SAdd(key string, members ...interface{}) (int64, error) {
count, err := r.Client.SAdd(r.Ctx, key, members...).Result()
if err != nil {
return 0, fmt.Errorf("SAdd失败: %v", err)
}
return count, nil
}
// SMembers 获取所有集合成员
func (r *RedisClient) SMembers(key string) ([]string, error) {
members, err := r.Client.SMembers(r.Ctx, key).Result()
if err != nil {
return nil, fmt.Errorf("获取集合成员失败: %v", err)
}
return members, nil
}
// SIsMember 检查是否是集合成员
func (r *RedisClient) SIsMember(key string, member interface{}) (bool, error) {
exists, err := r.Client.SIsMember(r.Ctx, key, member).Result()
if err != nil {
return false, fmt.Errorf("检查集合成员失败: %v", err)
}
return exists, nil
}
// SCard 获取集合基数
func (r *RedisClient) SCard(key string) (int64, error) {
count, err := r.Client.SCard(r.Ctx, key).Result()
if err != nil {
return 0, fmt.Errorf("获取集合基数失败: %v", err)
}
return count, nil
}
// ==================== Sorted Set操作 ====================
// ZAdd 添加有序集合元素
func (r *RedisClient) ZAdd(key string, members ...redis.Z) (int64, error) {
count, err := r.Client.ZAdd(r.Ctx, key, members...).Result()
if err != nil {
return 0, fmt.Errorf("ZAdd失败: %v", err)
}
return count, nil
}
// ZRange 获取有序集合范围
func (r *RedisClient) ZRange(key string, start, stop int64) ([]string, error) {
vals, err := r.Client.ZRange(r.Ctx, key, start, stop).Result()
if err != nil {
return nil, fmt.Errorf("ZRange失败: %v", err)
}
return vals, nil
}
// ZRangeWithScores 获取有序集合范围和分数
func (r *RedisClient) ZRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
vals, err := r.Client.ZRangeWithScores(r.Ctx, key, start, stop).Result()
if err != nil {
return nil, fmt.Errorf("ZRangeWithScores失败: %v", err)
}
return vals, nil
}
// ZScore 获取元素分数
func (r *RedisClient) ZScore(key, member string) (float64, error) {
score, err := r.Client.ZScore(r.Ctx, key, member).Result()
if err == redis.Nil {
return 0, fmt.Errorf("元素不存在")
} else if err != nil {
return 0, fmt.Errorf("获取分数失败: %v", err)
}
return score, nil
}
// ==================== JSON序列化操作 ====================
// SetJSON 将结构体序列化为JSON并存储
func (r *RedisClient) SetJSON(key string, data interface{}, expiration time.Duration) error {
jsonData, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("JSON序列化失败: %v", err)
}
return r.Set(key, jsonData, expiration)
}
// GetJSON 获取JSON并反序列化到结构体
func (r *RedisClient) GetJSON(key string, result interface{}) error {
data, err := r.Get(key)
if err != nil {
return err
}
err = json.Unmarshal([]byte(data), result)
if err != nil {
return fmt.Errorf("JSON反序列化失败: %v", err)
}
return nil
}
// ==================== 分布式锁 ====================
// TryLock 尝试获取分布式锁
func (r *RedisClient) TryLock(key string, expiration time.Duration) (bool, error) {
success, err := r.Client.SetNX(r.Ctx, key, "locked", expiration).Result()
if err != nil {
return false, fmt.Errorf("获取锁失败: %v", err)
}
return success, nil
}
// ReleaseLock 释放分布式锁
func (r *RedisClient) ReleaseLock(key string) error {
// 使用Lua脚本确保原子性操作
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
result, err := r.Client.Eval(r.Ctx, script, []string{key}, "locked").Result()
if err != nil {
return fmt.Errorf("释放锁失败: %v", err)
}
if result.(int64) == 0 {
return fmt.Errorf("锁已释放或不属于当前客户端")
}
return nil
}
// ==================== 管道操作 ====================
// Pipeline 执行管道操作
func (r *RedisClient) Pipeline(fn func(pipe redis.Pipeliner) error) error {
pipe := r.Client.Pipeline()
err := fn(pipe)
if err != nil {
return err
}
_, err = pipe.Exec(r.Ctx)
return err
}
// ==================== 事务操作 ====================
// Watch 监视键并执行事务
func (r *RedisClient) Watch(fn func(tx *redis.Tx) error, keys ...string) error {
err := r.Client.Watch(r.Ctx, fn, keys...)
if err != nil {
return fmt.Errorf("事务执行失败: %v", err)
}
return nil
}
// ==================== 发布订阅 ====================
// Publish 发布消息
func (r *RedisClient) Publish(channel string, message interface{}) error {
err := r.Client.Publish(r.Ctx, channel, message).Err()
if err != nil {
return fmt.Errorf("发布消息失败: %v", err)
}
return nil
}
// Subscribe 订阅频道
func (r *RedisClient) Subscribe(channels ...string) *redis.PubSub {
return r.Client.Subscribe(r.Ctx, channels...)
}
// ==================== 数据库操作 ====================
// SelectDB 切换数据库
// 注意go-redis/v9的Client没有直接的Select方法需要通过Do命令执行
func (r *RedisClient) SelectDB(db int) error {
// 使用Do命令执行SELECT
cmd := redis.NewCmd(r.Ctx, "SELECT", db)
err := r.Client.Process(r.Ctx, cmd)
if err != nil {
return fmt.Errorf("切换数据库到 %d 失败: %v", db, err)
}
// 检查命令执行结果
_, err = cmd.Result()
if err != nil {
return fmt.Errorf("切换数据库到 %d 失败: %v", db, err)
}
return nil
}
//// 主函数
//func main() {
//}

334
redis/redisDll.go Normal file
View File

@ -0,0 +1,334 @@
package main
import (
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"time"
)
// KfzVerifyPriceInfo 主结构体
type KfzVerifyPriceInfo struct {
Isbn string `json:"isbn"` // ISBN
OnSaleCount string `json:"onSaleCount"` // 在售数量
AvgPrice string `json:"avgPrice"` // 平均价格
LastPushTime string `json:"lastPushTime"` // 最后推送时间
Source string `json:"source"` // 来源
Data []BookItem `json:"data"` // 商品列表
}
// BookItem 商品项的具体结构
type BookItem struct {
Price string `json:"price"` // 价格 "17.01"
ShippingFee float64 `json:"shippingFee"` // 运费 0 或 5.8
Condition string `json:"condition"` // 品相 "八五品"
ImgBigUrl string `json:"imgBigUrl"` // 图片URL
ShopAvgShippingTime string `json:"shopAvgShippingTime"` // 平均发货时间 "10"
ShopSuccessOrderRate string `json:"shopSuccessOrderRate"` // 成功率 "90.03"
GoodsUrl string `json:"goodsUrl"` // 商品URL "575550/9643470711"
Title string `json:"title"` // 标题
GoodsImgCount int `json:"goodsImgCount"` // 图片数量 9
}
// ParseRedisData 解析Redis返回的数据
func ParseRedisData(redisData interface{}) (*KfzVerifyPriceInfo, error) {
// 将redisData转换为[]interface{}类型
data, ok := redisData.([]interface{})
if !ok {
return nil, fmt.Errorf("redis数据格式错误: 期望数组,得到 %T", redisData)
}
// 检查数组长度
if len(data) < 6 {
return nil, fmt.Errorf("redis数据格式错误: 数组长度不足期望至少6个元素实际 %d", len(data))
}
// 创建结果对象
result := &KfzVerifyPriceInfo{}
// 解析ISBN (索引0)
if isbn, ok := data[0].(string); ok {
result.Isbn = isbn
} else {
result.Isbn = fmt.Sprintf("%v", data[0])
}
// 解析在售数量 (索引2)
if onSaleCount, ok := data[2].(string); ok {
result.OnSaleCount = onSaleCount
} else {
result.OnSaleCount = fmt.Sprintf("%v", data[2])
}
// 解析平均价格 (索引3)
if avgPrice, ok := data[3].(string); ok {
result.AvgPrice = avgPrice
} else {
result.AvgPrice = fmt.Sprintf("%v", data[3])
}
// 解析最后推送时间 (索引4)
if lastPushTime, ok := data[4].(string); ok {
result.LastPushTime = lastPushTime
} else {
result.LastPushTime = fmt.Sprintf("%v", data[4])
}
// 解析来源 (索引5)
if source, ok := data[5].(string); ok {
result.Source = source
} else {
result.Source = fmt.Sprintf("%v", data[5])
}
// 解析商品列表 (索引1)
bookItems, err := parseBookItems(data[1])
if err != nil {
return nil, fmt.Errorf("解析商品列表失败: %v", err)
}
result.Data = bookItems
return result, nil
}
// parseBookItems 解析商品列表
func parseBookItems(items interface{}) ([]BookItem, error) {
// 将items转换为[]interface{}
itemList, ok := items.([]interface{})
if !ok {
return nil, fmt.Errorf("商品列表格式错误: 期望数组,得到 %T", items)
}
bookItems := make([]BookItem, 0, len(itemList))
for i, item := range itemList {
// 每个商品项应该是一个包含9个元素的数组
bookData, ok := item.([]interface{})
if !ok {
log.Printf("第%d个商品格式错误: 期望数组,得到 %T", i, item)
continue
}
if len(bookData) < 9 {
log.Printf("第%d个商品数据长度不足: 期望至少9个元素实际 %d", i, len(bookData))
continue
}
bookItem := BookItem{}
// 解析价格 (索引0)
if price, ok := bookData[0].(string); ok {
bookItem.Price = price
} else {
bookItem.Price = fmt.Sprintf("%v", bookData[0])
}
// 解析运费 (索引1)
switch v := bookData[1].(type) {
case float64:
bookItem.ShippingFee = v
case int:
bookItem.ShippingFee = float64(v)
case string:
if f, err := strconv.ParseFloat(v, 64); err == nil {
bookItem.ShippingFee = f
}
default:
bookItem.ShippingFee = 0
}
// 解析品相 (索引2)
if condition, ok := bookData[2].(string); ok {
bookItem.Condition = condition
} else {
bookItem.Condition = fmt.Sprintf("%v", bookData[2])
}
// 解析图片URL (索引3)
if imgUrl, ok := bookData[3].(string); ok {
bookItem.ImgBigUrl = imgUrl
} else {
bookItem.ImgBigUrl = fmt.Sprintf("%v", bookData[3])
}
// 解析平均发货时间 (索引4)
if shippingTime, ok := bookData[4].(string); ok {
bookItem.ShopAvgShippingTime = shippingTime
} else {
bookItem.ShopAvgShippingTime = fmt.Sprintf("%v", bookData[4])
}
// 解析成功率 (索引5)
if successRate, ok := bookData[5].(string); ok {
bookItem.ShopSuccessOrderRate = successRate
} else {
bookItem.ShopSuccessOrderRate = fmt.Sprintf("%v", bookData[5])
}
// 解析商品URL (索引6)
if goodsUrl, ok := bookData[6].(string); ok {
bookItem.GoodsUrl = goodsUrl
} else {
bookItem.GoodsUrl = fmt.Sprintf("%v", bookData[6])
}
// 解析标题 (索引7)
if title, ok := bookData[7].(string); ok {
bookItem.Title = title
} else {
bookItem.Title = fmt.Sprintf("%v", bookData[7])
}
// 解析图片数量 (索引8)
switch v := bookData[8].(type) {
case int:
bookItem.GoodsImgCount = v
case float64:
bookItem.GoodsImgCount = int(v)
case string:
if count, err := strconv.Atoi(v); err == nil {
bookItem.GoodsImgCount = count
}
default:
bookItem.GoodsImgCount = 0
}
bookItems = append(bookItems, bookItem)
}
return bookItems, nil
}
// GetValueFromDB1 当从DB2获取失败时从DB1的cp85hash中查找对应的key并加1
func GetValueFromDB1(redisClient *RedisClient, originalKey string) (string, error) {
// 提取ISBN号去掉"cp:"前缀)
isbn := strings.TrimPrefix(originalKey, "cp:")
// 切换到DB1
err := redisClient.SelectDB(1)
if err != nil {
return "", fmt.Errorf("切换到DB1失败: %v", err)
}
// 从cp85hash中获取对应的值
hashKey := "cp85"
value, err := redisClient.HGet(hashKey, isbn)
if err != nil {
return "", fmt.Errorf("从cp85hash获取数据失败: %v", err)
}
log.Println("cp85Isbn:", isbn, "value:", value)
// 将值转换为整数并加1
intValue, err := strconv.Atoi(value)
if err != nil {
return "", fmt.Errorf("值转换失败: %v", err)
}
newValue := intValue + 1
// 更新hash中的值
err = redisClient.HSet(hashKey, isbn, strconv.Itoa(newValue))
if err != nil {
return "", fmt.Errorf("更新cp85hash值失败: %v", err)
}
log.Printf("成功更新DB1中cp85hash的键 %s值从 %d 增加到 %d", isbn, intValue, newValue)
// 切换回DB2保持一致性
err = redisClient.SelectDB(2)
if err != nil {
log.Printf("切换回DB2失败: %v", err)
}
return "", nil
}
// 修改您的main函数
func main() {
// 自定义配置
config := &RedisConfig{
Addr: "36.212.20.113:7963",
Password: "j8nZ4jra2E",
DB: 2,
PoolSize: 20,
MinIdleConns: 10,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
PoolTimeout: 4 * time.Second,
}
// 使用自定义配置
redisClient := NewRedisClient(config)
defer redisClient.Close()
// 字符串操作示例
key := "cp:9787040110951"
// 获取字符串值
val, err := redisClient.Get(key)
if err != nil {
log.Printf("从DB2获取失败: %v", err)
// 尝试从DB1的cp85hash中查找并加1
newValue, db1Err := GetValueFromDB1(redisClient, key)
if db1Err != nil {
log.Printf("从DB1获取也失败: %v", db1Err)
return
}
fmt.Printf("从DB1的cp85hash中找到并更新值: %s\n", newValue)
return
}
fmt.Printf("键 %s 的原始值: %s\n", key, val)
// 由于Redis返回的是JSON字符串需要先解析
var redisData interface{}
err = json.Unmarshal([]byte(val), &redisData)
if err != nil {
log.Printf("JSON解析失败: %v", err)
return
}
// 解析为结构体
result, err := ParseRedisData(redisData)
if err != nil {
log.Printf("解析Redis数据失败: %v", err)
return
}
// 输出解析后的结构体
fmt.Println("\n=== 解析后的数据 ===")
fmt.Printf("ISBN: %s\n", result.Isbn)
fmt.Printf("在售数量: %s\n", result.OnSaleCount)
fmt.Printf("平均价格: %s\n", result.AvgPrice)
fmt.Printf("最后推送时间: %s\n", result.LastPushTime)
fmt.Printf("来源: %s\n", result.Source)
fmt.Printf("商品数量: %d\n", len(result.Data))
// 输出商品列表
for i, item := range result.Data {
fmt.Printf("\n--- 商品 %d ---\n", i+1)
fmt.Printf(" 价格: %s\n", item.Price)
fmt.Printf(" 运费: %.2f\n", item.ShippingFee)
fmt.Printf(" 品相: %s\n", item.Condition)
fmt.Printf(" 图片URL: %s\n", item.ImgBigUrl)
fmt.Printf(" 平均发货时间: %s\n", item.ShopAvgShippingTime)
fmt.Printf(" 成功率: %s\n", item.ShopSuccessOrderRate)
fmt.Printf(" 商品URL: %s\n", item.GoodsUrl)
fmt.Printf(" 标题: %s\n", item.Title)
fmt.Printf(" 图片数量: %d\n", item.GoodsImgCount)
}
// 如果需要转换为JSON
jsonResult, err := json.MarshalIndent(result, "", " ")
if err != nil {
log.Printf("JSON序列化失败: %v", err)
} else {
fmt.Println("\n=== JSON格式输出 ===")
fmt.Println(string(jsonResult))
}
}

1
version.yaml Normal file
View File

@ -0,0 +1 @@
verifyPriceLatestVersion: v1.0