193 lines
5.4 KiB
Go
193 lines
5.4 KiB
Go
package pdd
|
||
|
||
/*
|
||
#cgo LDFLAGS: -ldl
|
||
#include <dlfcn.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
// C包装函数,用于调用动态加载的函数
|
||
static void* get_func(void* handle, const char* name) {
|
||
return dlsym(handle, name);
|
||
}
|
||
|
||
// 包装PddGoodsOuterCatMappingGet函数
|
||
static char* call_PddGoodsOuterCatMappingGet(void* func, const char* clientId, const char* clientSecret,
|
||
const char* accessToken, const char* outerCatId, const char* outerCatName, const char* outerGoodsName) {
|
||
typedef char* (*FuncType)(const char*, const char*, const char*, const char*, const char*, const char*);
|
||
return ((FuncType)func)(clientId, clientSecret, accessToken, outerCatId, outerCatName, outerGoodsName);
|
||
}
|
||
|
||
// 包装FreeCString函数
|
||
static void call_FreeCString(void* func, char* str) {
|
||
typedef void (*FuncType)(char*);
|
||
if (func != NULL && str != NULL) {
|
||
((FuncType)func)(str);
|
||
}
|
||
}
|
||
*/
|
||
import "C"
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
"unsafe"
|
||
)
|
||
|
||
// PddResponse 定义完整的响应结构(包含成功和失败两种情况)
|
||
type PddResponse struct {
|
||
SuccessResponse *PddCategoryMappingResponse `json:"outer_cat_mapping_get_response,omitempty"`
|
||
ErrorResponse *PddErrorResponse `json:"error_response,omitempty"`
|
||
}
|
||
|
||
// PddCategoryMappingResponse 定义拼多多API响应结构(根据文档规范)
|
||
type PddCategoryMappingResponse struct {
|
||
CatID1 int64 `json:"cat_id1"` // 一级类目ID
|
||
CatID2 int64 `json:"cat_id2"` // 二级类目ID
|
||
CatID3 int64 `json:"cat_id3"` // 三级类目ID
|
||
CatID4 int64 `json:"cat_id4"` // 四级类目ID
|
||
RequestID string `json:"request_id"` // 请求ID
|
||
}
|
||
|
||
type PddSO struct {
|
||
handle unsafe.Pointer // 动态库句柄
|
||
pddGoodsOuterCatMappingGet unsafe.Pointer // 类目预测函数指针
|
||
freeCString unsafe.Pointer // 释放C字符串函数指针
|
||
}
|
||
type PddErrorResponse struct {
|
||
ErrorCode int64 `json:"error_code"` // 错误码
|
||
ErrorMsg string `json:"error_msg"` // 错误信息
|
||
SubCode *string `json:"sub_code"` // 子错误码
|
||
SubMsg string `json:"sub_msg"` // 子错误信息
|
||
RequestID string `json:"request_id"` // 请求ID
|
||
}
|
||
|
||
var (
|
||
instance *PddSO
|
||
once sync.Once
|
||
)
|
||
|
||
// GetPddInstance 获取拼多多客户端单例实例
|
||
func GetPddInstance() (*PddSO, error) {
|
||
var initErr error
|
||
once.Do(func() {
|
||
instance, initErr = InitPddSO()
|
||
})
|
||
return instance, initErr
|
||
}
|
||
|
||
// InitPddSO 初始化pddSO
|
||
func InitPddSO() (*PddSO, error) {
|
||
soPath := filepath.Join("so", "pdd.so")
|
||
if _, err := os.Stat(soPath); os.IsNotExist(err) {
|
||
return nil, fmt.Errorf("pdd SO 不存在: %s", soPath)
|
||
}
|
||
|
||
// 加载动态库
|
||
soPathC := C.CString(soPath)
|
||
defer C.free(unsafe.Pointer(soPathC))
|
||
|
||
handle := C.dlopen(soPathC, C.RTLD_LAZY)
|
||
if handle == nil {
|
||
errMsg := C.GoString(C.dlerror())
|
||
return nil, fmt.Errorf("加载pdd SO 失败: %s", errMsg)
|
||
}
|
||
|
||
// 获取函数指针
|
||
// 获取函数指针
|
||
getFunc := func(name string) (unsafe.Pointer, error) {
|
||
funcPtr := C.get_func(handle, C.CString(name))
|
||
if funcPtr == nil {
|
||
C.dlclose(handle)
|
||
return nil, fmt.Errorf("找不到函数 %s", name)
|
||
}
|
||
return funcPtr, nil
|
||
}
|
||
|
||
pddFunc, err := getFunc("PddGoodsOuterCatMappingGet")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
freeFunc, err := getFunc("FreeCString")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &PddSO{
|
||
handle: handle,
|
||
pddGoodsOuterCatMappingGet: pddFunc,
|
||
freeCString: freeFunc,
|
||
}, nil
|
||
}
|
||
|
||
// PddGoodsOuterCatMappingGet 类目检测
|
||
func (m *PddSO) PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||
|
||
// 使用闭包简化C字符串管理
|
||
toCStr := func(s string) (*C.char, func()) {
|
||
cStr := C.CString(s)
|
||
return cStr, func() { C.free(unsafe.Pointer(cStr)) }
|
||
}
|
||
|
||
clientIdC, cleanup1 := toCStr(clientId)
|
||
defer cleanup1()
|
||
|
||
clientSecretC, cleanup2 := toCStr(clientSecret)
|
||
defer cleanup2()
|
||
|
||
accessTokenC, cleanup3 := toCStr(accessToken)
|
||
defer cleanup3()
|
||
|
||
outerCatIdC, cleanup4 := toCStr(outerCatId)
|
||
defer cleanup4()
|
||
|
||
outerCatNameC, cleanup5 := toCStr(outerCatName)
|
||
defer cleanup5()
|
||
|
||
outerGoodsNameC, cleanup6 := toCStr(outerGoodsName)
|
||
defer cleanup6()
|
||
|
||
result := C.call_PddGoodsOuterCatMappingGet(
|
||
m.pddGoodsOuterCatMappingGet,
|
||
clientIdC, clientSecretC, accessTokenC,
|
||
outerCatIdC, outerCatNameC, outerGoodsNameC,
|
||
)
|
||
|
||
if result == nil {
|
||
return "", fmt.Errorf("调用PddGoodsOuterCatMappingGet失败")
|
||
}
|
||
defer C.call_FreeCString(m.freeCString, result)
|
||
|
||
return m.parseCategoryMappingResponse(C.GoString(result))
|
||
}
|
||
|
||
// parseCategoryMappingResponse 解析类目映射API响应
|
||
func (m *PddSO) parseCategoryMappingResponse(result string) (string, error) {
|
||
var apiResponse PddResponse
|
||
if err := json.Unmarshal([]byte(result), &apiResponse); err != nil {
|
||
return "", fmt.Errorf("JSON解析失败: %v, 原始数据: %s", err, result)
|
||
}
|
||
|
||
// 判断是成功响应还是错误响应
|
||
switch {
|
||
case apiResponse.ErrorResponse != nil:
|
||
errorResp := apiResponse.ErrorResponse
|
||
return "", fmt.Errorf("API调用失败 - 错误码: %d, 错误信息: %s",
|
||
errorResp.ErrorCode, errorResp.ErrorMsg)
|
||
|
||
case apiResponse.SuccessResponse != nil:
|
||
successResp := apiResponse.SuccessResponse
|
||
return fmt.Sprintf("%d/%d/%d",
|
||
successResp.CatID1,
|
||
successResp.CatID2,
|
||
successResp.CatID3), nil
|
||
|
||
default:
|
||
return "", fmt.Errorf("未知的响应格式: %s", result)
|
||
}
|
||
}
|