init:xianyu_noisbn_publishGoods
This commit is contained in:
commit
e3429f9bad
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/logs/
|
||||
/planB/logs/
|
||||
/planA.exe
|
||||
/taskDb.db
|
||||
/planB/planB.exe
|
||||
/.idea
|
||||
/planB/.idea
|
||||
/planB/img/
|
||||
BIN
__debug_bin.exe
Normal file
BIN
__debug_bin.exe
Normal file
Binary file not shown.
BIN
__debug_bin.exe2740605166
Normal file
BIN
__debug_bin.exe2740605166
Normal file
Binary file not shown.
BIN
__debug_bin.exe630942224
Normal file
BIN
__debug_bin.exe630942224
Normal file
Binary file not shown.
133
config.yaml
Normal file
133
config.yaml
Normal file
@ -0,0 +1,133 @@
|
||||
server:
|
||||
port: "8080" #服务器端口
|
||||
f_port : "8284" #F程序端口
|
||||
filter: 1 #是否开启违禁词过滤器 0=关闭 1=开启
|
||||
replace_mark: "0" #标题违规词是否替换* 0 不替换 1 替换(替换会继续发布,不替换则不发布)
|
||||
redis_exp: 192 #redis过期时间 192小时(8天)
|
||||
read_db: "mysql" #读数据库 mysql sqlite
|
||||
err_pause_time: 3000 #错误暂停时间(毫秒)
|
||||
sign_key: "jRQdCh52Z55Kzh1hADaA2ZtdTKetj2PXk60Tz5Yc0iz9aD8Wafbk7CwAZ8cz69A9zb9caZ3k9dnR3Ys06J5nYFPrZ0xE9p6TY8DCD538ryiRjW81YTPmk41tCEnXizPh" #签名密钥
|
||||
data_day: 2 #数据保存时间(天)
|
||||
is_c: true #是否启动 C程序
|
||||
speed: #限速器
|
||||
pdd_speed: 18 #拼多多 每秒多少个任务
|
||||
xianyu_speed: 5 #闲鱼 每秒多少个任务
|
||||
watermark: 15 #打水印速率的个数
|
||||
minio: #minio 图片空间
|
||||
url: "103.236.68.64:19000" #minio地址
|
||||
access_key_id: "minio" #minio keyId
|
||||
secret_access_key: "bhkXyaD2WdAF7C6z" #minio key
|
||||
bucket_name: "task-xianyu" #存储桶
|
||||
target_dir: "test" #目标目录
|
||||
use_ssl: false #是否使用 SSL
|
||||
alive:
|
||||
fluent: 50 #存活状态-流畅时间(毫秒)
|
||||
slow: 200 #存活状态-缓慢时间(毫秒)
|
||||
pool_config:
|
||||
size: 500 #协程数量
|
||||
with_expiry_duration: 10 #过期时间
|
||||
with_pre_alloc: true #预分配
|
||||
with_max_blocking_tasks: 2000 #阻塞任务数
|
||||
with_nonblocking : true #非阻塞
|
||||
mysql_config:
|
||||
db_name: "task_user" #数据库名称
|
||||
user: "root" #数据库用户名
|
||||
password: "root" #数据库密码
|
||||
host: "127.0.0.1" #数据库地址
|
||||
port: 3306 #数据库端口
|
||||
loglevel: "silent" #数据库日志级别 info=开发环境:输出所有日志(SQL、执行时间等) warn=预发布:输出警告+错误 error=生产环境:只输出错误日志 silent=生产环境:关闭所有日志
|
||||
max_retry_times: 3 #数据库重试次数
|
||||
base_retry_interval: 100 #数据库重试间隔(毫秒)
|
||||
max_retry_interval: 3 #数据库最大重试间隔(秒)
|
||||
max_open_conns: 100 #数据库最大打开连接数
|
||||
max_idle_conns: 20 #数据库最大空闲连接数
|
||||
conn_max_idle_time: 30 #数据库连接最大空闲时间(分钟)
|
||||
conn_max_lifetime: 1 #数据库连接最大生命周期(小数)
|
||||
psi_mysql_config:
|
||||
db_name: "psi" #数据库名称
|
||||
user: "psi" #数据库用户名
|
||||
password: "6d7f5DK2G5PCasBp" #数据库密码
|
||||
host: "175.27.224.66" #数据库地址
|
||||
port: 3306 #数据库端口
|
||||
loglevel: "silent" #数据库日志级别 info=开发环境:输出所有日志(SQL、执行时间等) warn=预发布:输出警告+错误 error=生产环境:只输出错误日志 silent=生产环境:关闭所有日志
|
||||
max_retry_times: 3 #数据库重试次数
|
||||
base_retry_interval: 100 #数据库重试间隔(毫秒)
|
||||
max_retry_interval: 3 #数据库最大重试间隔(秒)
|
||||
max_open_conns: 100 #数据库最大打开连接数
|
||||
max_idle_conns: 20 #数据库最大空闲连接数
|
||||
conn_max_idle_time: 30 #数据库连接最大空闲时间(分钟)
|
||||
conn_max_lifetime: 1 #数据库连接最大生命周期(小数)
|
||||
redis_config:
|
||||
- db_name: "任务池"
|
||||
db: 0
|
||||
addr: "127.0.0.1:6379"
|
||||
password: "123456"
|
||||
- db_name: "书品库"
|
||||
db: 7
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "店铺信息"
|
||||
db: 8
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "出版社信息列表"
|
||||
db: 3
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "省市区列表"
|
||||
db: 4
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "没有图片的 isbn"
|
||||
db: 5
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "没有书籍的 isbn"
|
||||
db: 6
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "淘宝店铺"
|
||||
db: 9
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
pdd_config:
|
||||
client_id: "203c5a7ba8bd4b8488d5e26f93052642" #拼多多clientId
|
||||
client_secret: "892ffaa86e12b7a3d8d2942b669d9aa520ad8179" #拼多多clientSecret
|
||||
kfz_config:
|
||||
app_id: 576 #孔夫子appid
|
||||
app_secret: "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8" #孔夫子appsecret
|
||||
http_url:
|
||||
task_url: "http://127.0.0.1:8080" #A 程序接口地址
|
||||
file_url:
|
||||
xian_yu_dll: "D:\\source\\planA\\planB\\modules\\xianYu" #闲鱼 DLL库路径
|
||||
pdd_dll: "D:\\source\\planA\\planB\\modules\\pdd" #拼多多 DLL库路径
|
||||
kfz_dll: "D:\\source\\planA\\planB\\modules\\kfz" #孔夫子 DLL库路径
|
||||
log_dll: "D:\\source\\planA\\planB\\modules\\logs" #日志 DLL库路径
|
||||
image_dll: "D:\\source\\planA\\planB\\modules\\image" #水印 DLL库路径
|
||||
b_file_name: "D:\\source\\planA\\planB\\planB.exe" #B 程序文件路径
|
||||
c_file_name: "D:\\source\\planA\\planC\\planC.exe" #C 程序文件路径
|
||||
d_file_name: "D:\\source\\planA\\planD\\planD.exe" #D 程序文件路径
|
||||
e_file_name: "D:\\source\\planA\\planE\\planE.exe" #E 程序文件路径
|
||||
f_file_name: "D:\\source\\planA\\planF\\planF.exe" #F 程序文件路径
|
||||
create_task_url: "https://api.buzhiyushu.cn/zhishu/baseInfo/addNewTask" #新增任务接口
|
||||
create_task_notice_url: "http://36.212.12.92:8055/task" #核价软件提交数据通知接口
|
||||
create_operation_task_notice_url: "http://36.212.12.92:8055/taskV2" #操作商品任务核价软件提交数据通知接口
|
||||
banned_word_substitution_url : "http://36.212.12.247:13001/task/getFilterSetNew" #违禁词替换接口
|
||||
xy_banned_word_substitution_url : "http://146.56.192.164:19095/health" #闲鱼违禁词替换接口
|
||||
pdd_token_url: "https://api.buzhiyushu.cn/huidiao/pdd/getToken" #获取系统规定拼多多 token
|
||||
deduction_url: "https://api.buzhiyushu.cn/zhishu/userRecharge/apiBalancePayment" #扣费接口
|
||||
pdd_get_goods_url: "http://pdd.buzhiyushu.cn/api/pdd/auth/getShopGoodsList" #查询拼多多商品接口
|
||||
pdd_get_goods_detail_url: "http://192.168.101.127:8085/api/pdd/auth/newGetShopGoodsDetailList" #查询拼多多商品详情列表接口
|
||||
pdd_add_goods_url: "http://192.168.101.127:18099/task/putShopGoods" #添加拼多多商品接口
|
||||
pdd_get_sku_id: "http://192.168.101.127:18099/shopGoods/getShopGoods" #批量获取 skuId接口
|
||||
xianyu_add_goods_url: "http://119.45.237.193:14008/task/putShopGoods" #添加闲鱼商品接口
|
||||
kfz_add_goods_url: "http://119.45.237.193:14009/task/kfzverifyPricePublishGoods" #添加孔夫子商品接口
|
||||
del_task_url: "http://119.45.237.193:14008/shopGoods/delShopGoodsk" #删除任务通知接口
|
||||
backup_url: "C:\\file\\backup" #备份文件路径
|
||||
pdd_goods_details_url: "D:\\file\\pdd_goods_details" #保存拼多多详情路径
|
||||
update_token_url: "http://146.56.227.42:9099/api/updateToken" #更新拼多多 token 到redis
|
||||
kfz_img_temp_url: "D:\\file\\kfzImg" #孔夫子图片临时路径
|
||||
kfz_img_http_url: "https://www0.kfzimg.com/" #孔夫子图片 http 路径
|
||||
get_pdd_goods_shopid_isbn_url : "http://192.168.101.127:18099/shopGoods/selectTrilateralIds" #获取拼多多商品 shopId 和 isbn
|
||||
get_subscription_expiration_date_url : "http://119.45.237.193:9096/api/user/getKfzUserRecbusiness" #获取订阅到期时间
|
||||
pdd_img_temp_url: "D:\\file\\kfzImg" #拼多多图片临时路径
|
||||
36
controlState/lock/lock.go
Normal file
36
controlState/lock/lock.go
Normal file
@ -0,0 +1,36 @@
|
||||
package lock
|
||||
|
||||
import "sync"
|
||||
|
||||
// 用sync.Map替代原生map,天然支持并发安全
|
||||
var lock sync.Map
|
||||
|
||||
// GetLock 获取锁(返回true表示已上锁,false表示未上锁)
|
||||
func GetLock(key string) bool {
|
||||
v, ok := lock.Load(key)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// 断言为bool类型(确保存储的是布尔值)
|
||||
locked, ok := v.(bool)
|
||||
return ok && locked
|
||||
}
|
||||
|
||||
// SetLock 设置锁(原子操作)
|
||||
func SetLock(key string) {
|
||||
lock.Store(key, true)
|
||||
}
|
||||
|
||||
// DestroyLock 销毁锁(原子操作)
|
||||
func DestroyLock(key string) {
|
||||
lock.Delete(key)
|
||||
}
|
||||
|
||||
// TryLock 尝试加锁(核心:检查+设置原子化)
|
||||
// 返回true表示加锁成功,false表示已被上锁
|
||||
func TryLock(key string) bool {
|
||||
// LoadOrStore:如果key不存在则存储值,返回false;如果已存在则返回true
|
||||
_, loaded := lock.LoadOrStore(key, true)
|
||||
// loaded为true表示已上锁,返回false;loaded为false表示加锁成功,返回true
|
||||
return !loaded
|
||||
}
|
||||
55
controlState/serviceAlive/serviceAlive.go
Normal file
55
controlState/serviceAlive/serviceAlive.go
Normal file
@ -0,0 +1,55 @@
|
||||
package serviceAlive
|
||||
|
||||
// ServiceStatus 定义服务状态结构体
|
||||
type ServiceStatus struct {
|
||||
Times int // 次数/状态值
|
||||
Msg string // 消息信息
|
||||
}
|
||||
|
||||
var Service = map[string]ServiceStatus{
|
||||
"mysql": {Times: 0, Msg: ""},
|
||||
"redis": {Times: 0, Msg: ""},
|
||||
"sqlite": {Times: 0, Msg: ""},
|
||||
"pdd": {Times: 0, Msg: ""},
|
||||
"通知取出bodyOver接口": {Times: 0, Msg: ""},
|
||||
"违禁词替换接口": {Times: 0, Msg: ""},
|
||||
"闲鱼违禁词": {Times: 0, Msg: ""},
|
||||
}
|
||||
|
||||
// SetServiceAlive 设置服务状态
|
||||
func SetServiceAlive(key string, times int) {
|
||||
if status, ok := Service[key]; ok {
|
||||
status.Times = times
|
||||
Service[key] = status
|
||||
}
|
||||
}
|
||||
|
||||
// SetServiceAliveWithMsg 设置服务状态(带消息)
|
||||
func SetServiceAliveWithMsg(key string, times int, msg string) {
|
||||
Service[key] = ServiceStatus{
|
||||
Times: times,
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateServiceTimes 只更新服务次数
|
||||
func UpdateServiceTimes(key string, times int) {
|
||||
if status, ok := Service[key]; ok {
|
||||
status.Times = times
|
||||
Service[key] = status
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateServiceMsg 只更新服务消息
|
||||
func UpdateServiceMsg(key string, msg string) {
|
||||
if status, ok := Service[key]; ok {
|
||||
status.Msg = msg
|
||||
Service[key] = status
|
||||
}
|
||||
}
|
||||
|
||||
// GetServiceStatus 获取服务状态
|
||||
func GetServiceStatus(key string) (ServiceStatus, bool) {
|
||||
status, ok := Service[key]
|
||||
return status, ok
|
||||
}
|
||||
33
controller/admin.go
Normal file
33
controller/admin.go
Normal file
@ -0,0 +1,33 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"planA/tool"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// DelRedisTask 删除redis中指定任务
|
||||
func DelRedisTask(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 从路径参数获取 id
|
||||
vars := mux.Vars(data)
|
||||
taskId := vars["id"]
|
||||
// 验证 taskId
|
||||
if taskId == "" {
|
||||
errMsg := "任务 ID不能为空"
|
||||
tool.Error(httpMsg, errMsg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// 删除任务
|
||||
tool.Success(httpMsg, taskId)
|
||||
}
|
||||
|
||||
// DelMysqlTask 删除mysql中指定任务
|
||||
func DelMysqlTask(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
tool.Success(httpMsg, "删除mysql中指定任务")
|
||||
}
|
||||
|
||||
// DelSqliteTask 删除sqlite中指定任务
|
||||
func DelSqliteTask(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
tool.Success(httpMsg, "删除sqlite中指定任务")
|
||||
}
|
||||
37
controller/alive.go
Normal file
37
controller/alive.go
Normal file
@ -0,0 +1,37 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"planA/controlState/serviceAlive"
|
||||
config2 "planA/initialization/config"
|
||||
"planA/tool"
|
||||
)
|
||||
|
||||
// GetServiceAliveList 获取存活状态列表
|
||||
func GetServiceAliveList(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
//获取存活状态列表
|
||||
aliveConfig, getAliveConfigErr := config2.GetAliveConfig()
|
||||
if getAliveConfigErr != nil {
|
||||
tool.Error(httpMsg, getAliveConfigErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var ret []map[string]interface{}
|
||||
alive := serviceAlive.Service
|
||||
for k, v := range alive {
|
||||
status := 0
|
||||
// v.Times 是原来的计数值
|
||||
if v.Times > aliveConfig.Fluent && v.Times < aliveConfig.Slow {
|
||||
status = 1
|
||||
} else if v.Times >= aliveConfig.Slow {
|
||||
status = 2
|
||||
}
|
||||
ret = append(ret, map[string]interface{}{
|
||||
"name": k,
|
||||
"times": v.Times, // 修改为 v.Times
|
||||
"msg": v.Msg, // 新增 msg 字段
|
||||
"status": status,
|
||||
})
|
||||
}
|
||||
tool.Success(httpMsg, ret)
|
||||
}
|
||||
77
controller/body.go
Normal file
77
controller/body.go
Normal file
@ -0,0 +1,77 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"planA/service"
|
||||
"planA/tool"
|
||||
_type "planA/type"
|
||||
"planA/validator"
|
||||
)
|
||||
|
||||
// GetTbOneBodyWait 查询淘宝bodyWait
|
||||
func GetTbOneBodyWait(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetBodyWaitOneValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 查询bodyWait
|
||||
bodyWait, getTbBodyWaitErr := service.GetOneBodyFromRight(dataVal.TaskId)
|
||||
if getTbBodyWaitErr != nil {
|
||||
tool.Error(httpMsg, getTbBodyWaitErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 bodyWait 转为结构体
|
||||
var bodyWaits _type.TaskBody
|
||||
unmarshalErr := json.Unmarshal([]byte(bodyWait), &bodyWaits)
|
||||
if unmarshalErr != nil {
|
||||
tool.Error(httpMsg, unmarshalErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
tool.Success(httpMsg, bodyWaits)
|
||||
}
|
||||
|
||||
// InsertTbBodyOver 插入 bodyOver
|
||||
func InsertTbBodyOver(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.InsertTbBodyOver(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
//解析bodyover
|
||||
var bodyOver _type.TaskBody
|
||||
unmarshalErr := json.Unmarshal([]byte(dataVal.Data), &bodyOver)
|
||||
if unmarshalErr != nil {
|
||||
tool.Error(httpMsg, unmarshalErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
//// 更新任务尾
|
||||
//updateTaskFootersErr := service.UpdateTaskFooters(dataVal.TaskId, bodyOver.Detail.Status, 1)
|
||||
//if updateTaskFootersErr != nil {
|
||||
// tool.Error(httpMsg, updateTaskFootersErr.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
//}
|
||||
//// 更新任务头
|
||||
//updateTaskHeadersErr := service.UpdateTaskHeaders(dataVal.TaskId, bodyOver.Detail.Status, 1)
|
||||
//if updateTaskHeadersErr != nil {
|
||||
// tool.Error(httpMsg, updateTaskHeadersErr.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
//}
|
||||
|
||||
//插入bodyOver
|
||||
insertErr := service.InsertOneBodyOver(dataVal.TaskId, dataVal.Data)
|
||||
if insertErr != nil {
|
||||
tool.Error(httpMsg, insertErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
515
controller/delTask.go
Normal file
515
controller/delTask.go
Normal file
@ -0,0 +1,515 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
planBType "planA/planB/type"
|
||||
"planA/service"
|
||||
serviceMysql "planA/service/mysql"
|
||||
"planA/tool"
|
||||
toolPdd "planA/tool/pdd"
|
||||
_type "planA/type"
|
||||
mysqlType "planA/type/mysql"
|
||||
"planA/validator"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetDelTaskByPage 分页查询 删除任务
|
||||
func GetDelTaskByPage(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetDelTaskValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
delTaskArr, total, getDelTaskByPageErr := serviceMysql.GetDelTaskByPage(page, size, "")
|
||||
if getDelTaskByPageErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": delTaskArr,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDelTaskByPageByUserId 分页查询 删除任务-用户
|
||||
func GetDelTaskByPageByUserId(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetDelTaskByUserIdValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
delTaskArr, total, getDelTaskByPageErr := serviceMysql.GetDelTaskByPage(page, size, dataVal.UserId)
|
||||
if getDelTaskByPageErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": delTaskArr,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDelTaskDetail 获取删除任务详情列表
|
||||
func GetDelTaskDetail(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetDelTaskDetailValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
delTaskArr, total, getDelTaskByPageErr := serviceMysql.GetDelTaskDetailByPage(page, size, dataVal.TaskId, 3)
|
||||
if getDelTaskByPageErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": delTaskArr,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
|
||||
}
|
||||
|
||||
// CreateTbDelTask 创建淘宝删除任务
|
||||
func CreateTbDelTask(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.CreateTbDelTaskValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
delNum := data.FormValue("del_num")
|
||||
taskCount := 0
|
||||
if dataVal.TaskType == "1" || dataVal.TaskType == "2" {
|
||||
if delNum == "" || delNum == "0" {
|
||||
errMsg := "删除数量不能为空与0"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 dataVal.TaskCount 转为 int
|
||||
var err error
|
||||
taskCount, err = strconv.Atoi(delNum)
|
||||
if err != nil {
|
||||
errMsg := "任务数量转换失败: " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
delTime := data.FormValue("del_time")
|
||||
delTimes := time.Time{}
|
||||
if dataVal.TaskType == "3" {
|
||||
if delTime == "" || delTime == "0" {
|
||||
errMsg := "删除时间不能为空"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 dataVal.DelTime 转为 time.Time
|
||||
layout := "2006-01-02 15:04:05" // 常见格式
|
||||
var err error
|
||||
delTimes, err = time.Parse(layout, delTime)
|
||||
if err != nil {
|
||||
errMsg := "时间转换失败: " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 将 dataVal.TaskType 转为 int
|
||||
taskType, err := strconv.Atoi(dataVal.TaskType)
|
||||
if err != nil {
|
||||
errMsg := "任务类型转换失败: " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 查询店铺数据
|
||||
shopDataStr, err := service.GetTaskShop(dataVal.ShopID)
|
||||
if err != nil {
|
||||
errMsg := "获取店铺数据失败: shopId " + dataVal.ShopID + " " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// 解析 json数据
|
||||
shopData, err := toolPdd.ParseShopData(shopDataStr)
|
||||
if err != nil {
|
||||
errMsg := "解析店铺数据失败:" + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if shopData.Shop == nil {
|
||||
errMsg := "店铺数据为空"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
//请求创建任务接口并获取任务 id
|
||||
taskId, err := CreateTaskRequest(dataVal.ShopID, dataVal.TaskType)
|
||||
if err != nil {
|
||||
errMsg := "店铺ID " + dataVal.ShopID + " 淘宝删除任务 请求创建任务接口失败: " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
taskTypes := 0
|
||||
if taskType == 1 {
|
||||
taskTypes = 5
|
||||
} else if taskType == 2 {
|
||||
taskTypes = 10
|
||||
} else if taskType == 3 {
|
||||
taskTypes = 11
|
||||
}
|
||||
|
||||
var priceRange []_type.PriceRange
|
||||
priceTemplateRangeStr := shopData.PriceTemplate.RangePrice
|
||||
err = json.Unmarshal([]byte(priceTemplateRangeStr), &priceRange)
|
||||
if err != nil {
|
||||
errMsg := "解析价格模板失败:" + err.Error() + " 原始数据:" + shopData.PriceTemplate.RangePrice
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
createAt := time.Now().Unix()
|
||||
taskData, createTaskDataErr := CreateTaskData(taskId, int64(taskTypes), createAt, shopData.Shop, priceRange, shopData.Spec, shopData.ShopDetail, shopData.ShopContext, strconv.Itoa(taskCount), 1, 1, "1")
|
||||
if createTaskDataErr != nil {
|
||||
errMsg := "创建任务数据失败: " + createTaskDataErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 taskData 转为json
|
||||
taskDataJson, err := json.Marshal(taskData)
|
||||
if err != nil {
|
||||
errMsg := "任务数据转换失败: " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
header := string(taskDataJson)
|
||||
|
||||
createAts := time.Unix(createAt, 0)
|
||||
status := 0
|
||||
taskCountOver := 0
|
||||
// 创建任务
|
||||
task := mysqlType.DelTask{
|
||||
UserID: &shopData.Shop.CreateBy,
|
||||
ShopID: &dataVal.ShopID,
|
||||
TaskID: &taskId,
|
||||
ShopType: &dataVal.TaskType,
|
||||
TaskType: &taskType,
|
||||
ShopName: &shopData.Shop.ShopName,
|
||||
TaskCount: &taskCount,
|
||||
Header: &header,
|
||||
CreateAt: &createAts,
|
||||
StopAt: &delTimes,
|
||||
Status: &status,
|
||||
TaskCountOver: &taskCountOver,
|
||||
}
|
||||
createDelTaskErr := serviceMysql.CreateDelTask(task)
|
||||
if createDelTaskErr != nil {
|
||||
errMsg := "创建任务失败: " + createDelTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建详情表
|
||||
createTableIfNotExistsErr := serviceMysql.CreateTableIfNotExists(taskId)
|
||||
if createTableIfNotExistsErr != nil {
|
||||
errMsg := "创建详情表失败: " + createTableIfNotExistsErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, taskId)
|
||||
}
|
||||
|
||||
// CreateTbDelTaskDetails 插入淘宝删除任务数据
|
||||
func CreateTbDelTaskDetails(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.CreateTbDelTaskDetailsValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if dataVal.Status == "" {
|
||||
dataVal.Status = "0"
|
||||
}
|
||||
|
||||
//判断任务是否存在
|
||||
_, getTaskErr := serviceMysql.GetDelTaskByTaskId(dataVal.TaskID)
|
||||
if getTaskErr != nil {
|
||||
if errors.Is(getTaskErr, sql.ErrNoRows) || getTaskErr.Error() == "record not found" {
|
||||
errMsg := "任务不存在: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
errMsg := "获取任务失败: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// 创建详情表
|
||||
createTableIfNotExistsErr := serviceMysql.CreateTableIfNotExists(dataVal.TaskID)
|
||||
if createTableIfNotExistsErr != nil {
|
||||
errMsg := "创建详情表失败: " + createTableIfNotExistsErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 string 类型的 Status 转换为 int64
|
||||
var statusInt64 int64
|
||||
if dataVal.Status != "" {
|
||||
statusInt64, _ = strconv.ParseInt(dataVal.Status, 10, 64)
|
||||
}
|
||||
|
||||
// 转换 GoodsId
|
||||
var goodsIdInt64 int64
|
||||
if dataVal.GoodsId != "" {
|
||||
if val, err := strconv.ParseInt(dataVal.GoodsId, 10, 64); err == nil {
|
||||
goodsIdInt64 = val
|
||||
}
|
||||
}
|
||||
|
||||
// 转换 TaskID
|
||||
var taskIDInt64 int64
|
||||
if dataVal.TaskID != "" {
|
||||
if val, err := strconv.ParseInt(dataVal.TaskID, 10, 64); err == nil {
|
||||
taskIDInt64 = val
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前时间
|
||||
createAtTime := time.Unix(time.Now().Unix(), 0)
|
||||
createAtStr := time.Now().Format("2006-01-02")
|
||||
|
||||
// 插入任务详情
|
||||
taskDetails := planBType.DelTaskDetail{
|
||||
TaskID: &dataVal.TaskID,
|
||||
Isbn: &dataVal.Isbn,
|
||||
BookName: &dataVal.BookName,
|
||||
GoodsID: &goodsIdInt64,
|
||||
Status: &statusInt64,
|
||||
Err: &dataVal.Err,
|
||||
DeleteAt: &createAtTime,
|
||||
DeleteDate: &createAtStr,
|
||||
CreateAt: &createAtTime,
|
||||
}
|
||||
insertTbDelTaskDetailsErr := serviceMysql.InsertDelTaskDetail(taskIDInt64, taskDetails)
|
||||
if insertTbDelTaskDetailsErr != nil {
|
||||
errMsg := "插入任务详情失败: " + insertTbDelTaskDetailsErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
|
||||
// UpdateTbDelTaskDetailsStatus 修改指定淘宝删除任务详情状态
|
||||
func UpdateTbDelTaskDetailsStatus(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.UpdateTbDelTaskDetailsStatusValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断任务是否存在
|
||||
_, getTaskErr := serviceMysql.GetDelTaskByTaskId(dataVal.TaskID)
|
||||
if getTaskErr != nil {
|
||||
if errors.Is(getTaskErr, sql.ErrNoRows) || getTaskErr.Error() == "record not found" {
|
||||
errMsg := "任务不存在: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
errMsg := "获取任务失败: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 dataVal.Status 转为int
|
||||
statusInt, err := strconv.Atoi(dataVal.Status)
|
||||
if err != nil {
|
||||
errMsg := "状态值格式错误"
|
||||
tool.Error(httpMsg, errMsg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
updateTbDelTaskDetailsStatusErr := serviceMysql.UpdateDelTaskDetailStatus(dataVal.TaskID, dataVal.GoodsId, statusInt, dataVal.Err)
|
||||
if updateTbDelTaskDetailsStatusErr != nil {
|
||||
errMsg := "修改任务详情状态失败: " + updateTbDelTaskDetailsStatusErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
|
||||
// UpdateTbDelTaskProgress 修改淘宝任务进度
|
||||
func UpdateTbDelTaskProgress(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.UpdateTbDelTaskProgressValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断任务是否存在
|
||||
_, getTaskErr := serviceMysql.GetDelTaskByTaskId(dataVal.TaskID)
|
||||
if getTaskErr != nil {
|
||||
if errors.Is(getTaskErr, sql.ErrNoRows) || getTaskErr.Error() == "record not found" {
|
||||
errMsg := "任务不存在: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
errMsg := "获取任务失败: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 dataVal.Num 转为 int
|
||||
numInt, err := strconv.Atoi(dataVal.Num)
|
||||
if err != nil {
|
||||
errMsg := "进度值格式错误"
|
||||
tool.Error(httpMsg, errMsg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
updateTbDelTaskProgressErr := serviceMysql.UpdateDelTaskProgress(dataVal.TaskID, numInt)
|
||||
if updateTbDelTaskProgressErr != nil {
|
||||
errMsg := "修改任务进度失败: " + updateTbDelTaskProgressErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
|
||||
// UpdateTbDelTaskStatus 修改淘宝任务状态
|
||||
func UpdateTbDelTaskStatus(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.UpdateTbDelTaskStatusValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断任务是否存在
|
||||
_, getTaskErr := serviceMysql.GetDelTaskByTaskId(dataVal.TaskID)
|
||||
if getTaskErr != nil {
|
||||
if errors.Is(getTaskErr, sql.ErrNoRows) || getTaskErr.Error() == "record not found" {
|
||||
errMsg := "任务不存在: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
errMsg := "获取任务失败: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 将 dataVal.Status 转为 int
|
||||
statusInt, err := strconv.Atoi(dataVal.Status)
|
||||
if err != nil {
|
||||
errMsg := "状态值格式错误"
|
||||
tool.Error(httpMsg, errMsg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
updateTbDelTaskStatusErr := serviceMysql.UpdateDelTaskStatusByTaskId(dataVal.TaskID, statusInt)
|
||||
if updateTbDelTaskStatusErr != nil {
|
||||
errMsg := "修改任务状态失败: " + updateTbDelTaskStatusErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, "")
|
||||
|
||||
}
|
||||
|
||||
// GetTbDelTaskDetailsWait 获取指定任务待执行的任务详情
|
||||
func GetTbDelTaskDetailsWait(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetDelTaskDetailValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
delTaskArr, total, getDelTaskByPageErr := serviceMysql.GetDelTaskDetailByPage(page, size, dataVal.TaskId, 0)
|
||||
if getDelTaskByPageErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataArr := []map[string]interface{}{}
|
||||
for _, v := range delTaskArr {
|
||||
datas := map[string]interface{}{
|
||||
"task_id": v.TaskID,
|
||||
"isbn": v.Isbn,
|
||||
"book_name": v.BookName,
|
||||
"goods_id": v.GoodsID,
|
||||
"status": v.Status,
|
||||
"create_at": v.CreateAt,
|
||||
}
|
||||
dataArr = append(dataArr, datas)
|
||||
}
|
||||
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": dataArr,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
}
|
||||
|
||||
// GetTbDelTaskByTaskId 根据任务id 查询任务
|
||||
func GetTbDelTaskByTaskId(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.TaskIdValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断任务是否存在
|
||||
task, getTaskErr := serviceMysql.GetDelTaskByTaskId(dataVal.TaskID)
|
||||
if getTaskErr != nil {
|
||||
if errors.Is(getTaskErr, sql.ErrNoRows) || getTaskErr.Error() == "record not found" {
|
||||
errMsg := "任务不存在: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
errMsg := "获取任务失败: " + getTaskErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
datas := map[string]interface{}{
|
||||
"task_id": task.TaskID,
|
||||
"shop_id": task.ShopID,
|
||||
"task_type": task.TaskType,
|
||||
"status": task.Status,
|
||||
"task_count": task.TaskCount,
|
||||
"task_count_over": task.TaskCountOver,
|
||||
"stop_at": task.StopAt,
|
||||
"create_at": task.CreateAt,
|
||||
}
|
||||
|
||||
tool.Success(httpMsg, datas)
|
||||
}
|
||||
510
controller/export.go
Normal file
510
controller/export.go
Normal file
@ -0,0 +1,510 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"planA/modules/logs"
|
||||
"planA/rep"
|
||||
"planA/service"
|
||||
"planA/tool"
|
||||
_type "planA/type"
|
||||
"planA/validator"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// GetExportTask 导出任务列表
|
||||
func GetExportTask(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, GetExportValidatorErr := validator.GetExportValidator(data)
|
||||
if GetExportValidatorErr != nil {
|
||||
tool.Error(httpMsg, GetExportValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
|
||||
read := rep.CreateDbFactoryRead()
|
||||
records, total, getTaskRecordsListErr := read.GetTaskExportList(page, size, "")
|
||||
if getTaskRecordsListErr != nil {
|
||||
errMsg := getTaskRecordsListErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var dataTaskAll []map[string]interface{}
|
||||
for _, v := range records {
|
||||
complete, getExportFileProgressErr := service.GetExportFileProgress(v.TaskId)
|
||||
if getExportFileProgressErr != nil {
|
||||
errMsg := "获取任务进度失败: " + getExportFileProgressErr.Error()
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
continue
|
||||
}
|
||||
taskExportdata := map[string]interface{}{
|
||||
"task_id": v.TaskId,
|
||||
"shop_name": v.ShopName,
|
||||
"status": v.Status,
|
||||
"total": v.Total,
|
||||
"file_url": v.FileUrl,
|
||||
"complete_at": v.CompleteAt.Time,
|
||||
"create_at": v.CreateAt,
|
||||
"complete": complete,
|
||||
}
|
||||
dataTaskAll = append(dataTaskAll, taskExportdata)
|
||||
}
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": dataTaskAll,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
}
|
||||
|
||||
// GetExportTaskByUserId 导出任务列表-用户
|
||||
func GetExportTaskByUserId(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, GetExportByUserIdValidatorErr := validator.GetExportByUserIdValidator(data)
|
||||
if GetExportByUserIdValidatorErr != nil {
|
||||
tool.Error(httpMsg, GetExportByUserIdValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
page, size := tool.SetPage(dataVal.Page, dataVal.Size)
|
||||
|
||||
read := rep.CreateDbFactoryRead()
|
||||
records, total, getTaskRecordsListErr := read.GetTaskExportList(page, size, dataVal.UserID)
|
||||
if getTaskRecordsListErr != nil {
|
||||
errMsg := getTaskRecordsListErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
dataTaskAll := []map[string]interface{}{}
|
||||
for _, v := range records {
|
||||
complete, getExportFileProgressErr := service.GetExportFileProgress(v.TaskId)
|
||||
if errors.Is(getExportFileProgressErr, redis.Nil) {
|
||||
complete = int(v.Total)
|
||||
} else if getExportFileProgressErr != nil {
|
||||
errMsg := "获取任务进度失败: " + getExportFileProgressErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
taskExportdata := map[string]interface{}{
|
||||
"task_id": v.TaskId,
|
||||
"shop_name": v.ShopName,
|
||||
"status": v.Status,
|
||||
"total": v.Total,
|
||||
"file_url": v.FileUrl,
|
||||
"complete_at": v.CompleteAt.Time,
|
||||
"create_at": v.CreateAt,
|
||||
"complete": complete,
|
||||
}
|
||||
dataTaskAll = append(dataTaskAll, taskExportdata)
|
||||
}
|
||||
dataRet := map[string]interface{}{
|
||||
"page": page,
|
||||
"size": size,
|
||||
"total": total,
|
||||
"list": dataTaskAll,
|
||||
}
|
||||
tool.Success(httpMsg, dataRet)
|
||||
}
|
||||
|
||||
// ExportTaskDetail 根据任务 id导出任务详情
|
||||
func ExportTaskDetail(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, GetExportDetailValidatorErr := validator.GetExportDetailValidator(data)
|
||||
if GetExportDetailValidatorErr != nil {
|
||||
tool.Error(httpMsg, GetExportDetailValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
read := rep.CreateDbFactoryRead()
|
||||
|
||||
//查询是任务信息
|
||||
taskRecord, getTaskRecordsByTaskIDErr := read.GetTaskRecordsByTaskId(dataVal.TaskID)
|
||||
if getTaskRecordsByTaskIDErr != nil {
|
||||
errMsg := fmt.Sprintf("获取任务信息失败 %v", getTaskRecordsByTaskIDErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if taskRecord.IsExport == 1 {
|
||||
errMsg := "任务已导出过,请在下载中心查看"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
//获取任务详情总数
|
||||
total, GetBodyOverCount := service.GetBodyOverCount(dataVal.TaskID)
|
||||
if GetBodyOverCount != nil {
|
||||
errMsg := fmt.Sprintf("获取任务详情总数失败 %v", GetBodyOverCount)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
mysqlWrite, sqliteWrite := rep.CreateDbFactoryWrite()
|
||||
//查询导出任务是存在
|
||||
taskExport, getTaskExportByTaskIdErr := read.GetTaskExportByTaskId(dataVal.TaskID)
|
||||
if getTaskExportByTaskIdErr != nil {
|
||||
errMsg := fmt.Sprintf("获取任务信息失败 %v", getTaskExportByTaskIdErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if taskExport.Id == 0 {
|
||||
//创建一条导出任务
|
||||
var status int64
|
||||
var fileUrl string
|
||||
mysqlCreateTaskExportErr := mysqlWrite.CreateTaskExport(_type.TaskExportDTO{
|
||||
UserId: taskRecord.UserId,
|
||||
ShopId: taskRecord.ShopId,
|
||||
TaskId: taskRecord.TaskId,
|
||||
ShopName: taskRecord.ShopName,
|
||||
FileUrl: fileUrl,
|
||||
Status: status,
|
||||
Total: total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if mysqlCreateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("写入任务信息失败 %v", mysqlCreateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteCreateTaskExportErr := sqliteWrite.CreateTaskExport(_type.TaskExportDTO{
|
||||
UserId: taskRecord.UserId,
|
||||
ShopId: taskRecord.ShopId,
|
||||
TaskId: taskRecord.TaskId,
|
||||
ShopName: taskRecord.ShopName,
|
||||
FileUrl: fileUrl,
|
||||
Status: status,
|
||||
Total: total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if sqLiteCreateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("写入任务信息失败 %v", sqLiteCreateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
newTotal := taskExport.Total + total
|
||||
// 如果数据存在 清空完成时间 并且 修改任务总数量
|
||||
mysqlUpdateTaskExportErr := mysqlWrite.UpdateTaskExport(_type.TaskExportDTO{
|
||||
Id: taskExport.Id,
|
||||
UserId: taskExport.UserId,
|
||||
ShopId: taskExport.ShopId,
|
||||
TaskId: taskExport.TaskId,
|
||||
ShopName: taskExport.ShopName,
|
||||
FileUrl: taskExport.FileUrl,
|
||||
Status: taskExport.Status,
|
||||
Total: newTotal,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if mysqlUpdateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务信息失败 %v", mysqlUpdateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteUpdateTaskExportErr := sqliteWrite.UpdateTaskExport(_type.TaskExportDTO{
|
||||
Id: taskExport.Id,
|
||||
UserId: taskExport.UserId,
|
||||
ShopId: taskExport.ShopId,
|
||||
TaskId: taskExport.TaskId,
|
||||
ShopName: taskExport.ShopName,
|
||||
FileUrl: taskExport.FileUrl,
|
||||
Status: taskExport.Status,
|
||||
Total: newTotal,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if sqLiteUpdateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务信息失败 %v", sqLiteUpdateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
//修改任务导出状态
|
||||
mysqlUpdateTaskRecordsErr := mysqlWrite.UpdateTaskRecords(_type.TaskRecordsDTO{
|
||||
Id: taskRecord.Id,
|
||||
UserId: taskRecord.UserId,
|
||||
ShopId: taskRecord.ShopId,
|
||||
TaskId: taskRecord.TaskId,
|
||||
ShopName: taskRecord.ShopName,
|
||||
IsExport: 1,
|
||||
TaskType: taskRecord.TaskType,
|
||||
})
|
||||
if mysqlUpdateTaskRecordsErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务导出状态失败 %v", mysqlUpdateTaskRecordsErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteUpdateTaskRecordsErr := sqliteWrite.UpdateTaskRecords(_type.TaskRecordsDTO{
|
||||
Id: taskRecord.Id,
|
||||
UserId: taskRecord.UserId,
|
||||
ShopId: taskRecord.ShopId,
|
||||
TaskId: taskRecord.TaskId,
|
||||
ShopName: taskRecord.ShopName,
|
||||
IsExport: 1,
|
||||
TaskType: taskRecord.TaskType,
|
||||
})
|
||||
if sqLiteUpdateTaskRecordsErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务导出状态失败 %v", sqLiteUpdateTaskRecordsErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
go ExportCSV(dataVal.TaskID, total, taskRecord.TaskType)
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
|
||||
// ExportTaskDetailByUserId 根据任务 id导出任务详情-用户
|
||||
func ExportTaskDetailByUserId(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
|
||||
// 验证表单
|
||||
dataVal, GetExportDetailValidatorErr := validator.GetExportDetailByUserIdValidator(data)
|
||||
if GetExportDetailValidatorErr != nil {
|
||||
tool.Error(httpMsg, GetExportDetailValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
read := rep.CreateDbFactoryRead()
|
||||
|
||||
//查询任务信息
|
||||
task, getTaskRecordsByTaskIdErr := read.GetTaskRecordsByTaskId(dataVal.TaskID)
|
||||
if getTaskRecordsByTaskIdErr != nil {
|
||||
errMsg := fmt.Sprintf("获取任务信息失败 %v", getTaskRecordsByTaskIdErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证用户
|
||||
if dataVal.UserID != fmt.Sprintf("%v", task.UserId) {
|
||||
errMsg := "用户验证失败"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if task.IsExport == 1 {
|
||||
errMsg := "任务已导出过,请在下载中心查看"
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
//获取任务详情总数
|
||||
total, GetBodyOverCount := service.GetBodyOverCount(dataVal.TaskID)
|
||||
if GetBodyOverCount != nil {
|
||||
errMsg := fmt.Sprintf("获取任务详情总数失败 %v", GetBodyOverCount)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
mysqlWrite, sqliteWrite := rep.CreateDbFactoryWrite()
|
||||
//查询导出任务是存在
|
||||
taskExport, getTaskExportByTaskIdErr := read.GetTaskExportByTaskId(dataVal.TaskID)
|
||||
if getTaskExportByTaskIdErr != nil {
|
||||
errMsg := fmt.Sprintf("获取任务信息失败 %v", getTaskExportByTaskIdErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if taskExport.Id == 0 {
|
||||
//向导出任务表写入一条数据
|
||||
mysqlCreateTaskExportErr := mysqlWrite.CreateTaskExport(_type.TaskExportDTO{
|
||||
UserId: task.UserId,
|
||||
ShopId: task.ShopId,
|
||||
TaskId: dataVal.TaskID,
|
||||
ShopName: task.ShopName,
|
||||
FileUrl: "",
|
||||
Status: 0,
|
||||
Total: total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if mysqlCreateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("写入任务信息失败 %v", mysqlCreateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteCreateTaskExport := sqliteWrite.CreateTaskExport(_type.TaskExportDTO{
|
||||
UserId: task.UserId,
|
||||
ShopId: task.ShopId,
|
||||
TaskId: dataVal.TaskID,
|
||||
ShopName: task.ShopName,
|
||||
FileUrl: "",
|
||||
Status: 0,
|
||||
Total: total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if sqLiteCreateTaskExport != nil {
|
||||
errMsg := fmt.Sprintf("写入任务信息失败 %v", sqLiteCreateTaskExport)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 如果数据存在 清空完成时间 并且 修改任务总数量
|
||||
mysqlUpdateTaskExportErr := mysqlWrite.UpdateTaskExport(_type.TaskExportDTO{
|
||||
Id: taskExport.Id,
|
||||
UserId: taskExport.UserId,
|
||||
ShopId: taskExport.ShopId,
|
||||
TaskId: taskExport.TaskId,
|
||||
ShopName: taskExport.ShopName,
|
||||
FileUrl: taskExport.FileUrl,
|
||||
Status: taskExport.Status,
|
||||
Total: taskExport.Total + total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if mysqlUpdateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务信息失败 %v", mysqlUpdateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteUpdateTaskExportErr := sqliteWrite.UpdateTaskExport(_type.TaskExportDTO{
|
||||
Id: taskExport.Id,
|
||||
UserId: taskExport.UserId,
|
||||
ShopId: taskExport.ShopId,
|
||||
TaskId: taskExport.TaskId,
|
||||
ShopName: taskExport.ShopName,
|
||||
FileUrl: taskExport.FileUrl,
|
||||
Status: taskExport.Status,
|
||||
Total: taskExport.Total + total,
|
||||
CompleteAt: sql.NullTime{},
|
||||
})
|
||||
if sqLiteUpdateTaskExportErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务信息失败 %v", sqLiteUpdateTaskExportErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//修改任务导出状态
|
||||
mysqlUpdateTaskExportStatusErr := mysqlWrite.UpdateTaskExportStatus(dataVal.TaskID, 1, "")
|
||||
if mysqlUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务导出状态失败 %v", mysqlUpdateTaskExportStatusErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sqLiteUpdateTaskExportStatusErr := sqliteWrite.UpdateTaskExportStatus(dataVal.TaskID, 1, "")
|
||||
if sqLiteUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("修改任务导出状态失败 %v", sqLiteUpdateTaskExportStatusErr)
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
go ExportCSV(dataVal.TaskID, total, task.TaskType)
|
||||
tool.Success(httpMsg, "")
|
||||
}
|
||||
|
||||
// ExportCSV 导出CSV
|
||||
// taskId 任务id
|
||||
// total 总数
|
||||
// taskType 任务类型
|
||||
// ExportCSV 导出CSV
|
||||
// taskId 任务id
|
||||
// total 总数
|
||||
func ExportCSV(taskId string, total int64, taskType int64) {
|
||||
// 定义每次获取的数量
|
||||
batchSize := 1000
|
||||
csvFileName := fmt.Sprintf("%v.csv", taskId)
|
||||
|
||||
// 定义导出目录
|
||||
exportDir := "file/export"
|
||||
// 检查并创建目录(如果不存在)
|
||||
err := os.MkdirAll(exportDir, 0755)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("创建目录失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
// 拼接完整的文件路径
|
||||
fullPath := filepath.Join(exportDir, csvFileName)
|
||||
|
||||
// 检查文件是否已存在
|
||||
fileExists := false
|
||||
if _, err := os.Stat(fullPath); err == nil {
|
||||
fileExists = true
|
||||
fmt.Printf("文件已存在: %s,将在末尾追加数据\n", fullPath)
|
||||
} else if !os.IsNotExist(err) {
|
||||
// 其他错误(如权限问题)
|
||||
errMsg := fmt.Sprintf("检查文件状态失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化偏移量
|
||||
page := 1
|
||||
// 标记是否是第一次写入(用于写入CSV表头)
|
||||
// 如果文件已存在,则不需要写入表头
|
||||
isFirstWrite := !fileExists
|
||||
|
||||
mysqlWrite, sqliteWrite := rep.CreateDbFactoryWrite()
|
||||
|
||||
// 更新任务导出状态-导出中
|
||||
mysqlUpdateTaskExportStatusErr := mysqlWrite.UpdateTaskExportStatus(taskId, 1, "")
|
||||
if mysqlUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("更新任务导出状态失败: %v", mysqlUpdateTaskExportStatusErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
}
|
||||
sqLiteUpdateTaskExportStatusErr := sqliteWrite.UpdateTaskExportStatus(taskId, 1, "")
|
||||
if sqLiteUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("更新任务导出状态失败: %v", sqLiteUpdateTaskExportStatusErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
}
|
||||
|
||||
// 循环获取并写入数据
|
||||
for {
|
||||
// 每次获取 batchSize条数据
|
||||
dataBatch, _, err := service.GetBodyOverDataByBatch(taskId, page, batchSize)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("获取任务详情批次数据失败 page:%d, err:%v", page, err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
fmt.Printf("数据长度: %v", len(dataBatch))
|
||||
// 没有数据了,退出循环
|
||||
if len(dataBatch) == 0 {
|
||||
// 导出完成
|
||||
mysqlUpdateTaskExportStatusErr = mysqlWrite.UpdateTaskExportStatus(taskId, 2, fullPath)
|
||||
if mysqlUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("更新任务导出状态失败: %v", mysqlUpdateTaskExportStatusErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
}
|
||||
sqLiteUpdateTaskExportStatusErr = sqliteWrite.UpdateTaskExportStatus(taskId, 2, fullPath)
|
||||
if sqLiteUpdateTaskExportStatusErr != nil {
|
||||
errMsg := fmt.Sprintf("更新任务导出状态失败: %v", sqLiteUpdateTaskExportStatusErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
}
|
||||
|
||||
// 清空body_over
|
||||
clearBodyOverErr := service.ClearBodyOver(taskId)
|
||||
if clearBodyOverErr != nil {
|
||||
errMsg := fmt.Sprintf("清空body_over失败: %v", clearBodyOverErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// 追加写入CSV文件
|
||||
// 注意:AppendToCSV函数需要修改以支持文件存在时的追加模式
|
||||
if writeErr := AppendToCSV(fullPath, dataBatch, isFirstWrite, taskId, taskType); writeErr != nil {
|
||||
errMsg := fmt.Sprintf("写入CSV文件失败 page:%d, err:%v", page, writeErr)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
// 第一次写入后标记为false(后续不再写表头)
|
||||
if isFirstWrite {
|
||||
isFirstWrite = false
|
||||
}
|
||||
|
||||
// 更新偏移量
|
||||
page++
|
||||
}
|
||||
}
|
||||
36
controller/shop.go
Normal file
36
controller/shop.go
Normal file
@ -0,0 +1,36 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"planA/service"
|
||||
"planA/tool"
|
||||
toolPdd "planA/tool/pdd"
|
||||
"planA/validator"
|
||||
)
|
||||
|
||||
// GetShopInfo 查询店铺信息
|
||||
func GetShopInfo(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.GetShopInfoValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 查询店铺数据
|
||||
shopDataStr, err := service.GetTaskShop(dataVal.ShopId)
|
||||
if err != nil {
|
||||
errMsg := "获取店铺数据失败: shopId " + dataVal.ShopId + " " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// 解析 json数据
|
||||
shopData, err := toolPdd.ParseShopData(shopDataStr)
|
||||
if err != nil {
|
||||
errMsg := "解析店铺数据失败:" + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, shopData)
|
||||
|
||||
}
|
||||
1752
controller/task.go
Normal file
1752
controller/task.go
Normal file
File diff suppressed because it is too large
Load Diff
85
controller/uploadImg.go
Normal file
85
controller/uploadImg.go
Normal file
@ -0,0 +1,85 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"planA/initialization/golabl"
|
||||
planBTypeModules "planA/planB/type/modules"
|
||||
"planA/service"
|
||||
"planA/tool"
|
||||
toolPdd "planA/tool/pdd"
|
||||
"planA/validator"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ImgUploadToPdd 上传图片到拼多多
|
||||
func ImgUploadToPdd(httpMsg http.ResponseWriter, data *http.Request) {
|
||||
// 验证表单
|
||||
dataVal, createTaskValidatorErr := validator.ImgUploadToPddValidator(data)
|
||||
if createTaskValidatorErr != nil {
|
||||
tool.Error(httpMsg, createTaskValidatorErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// 查询店铺数据
|
||||
shopDataStr, err := service.GetTaskShop(dataVal.ShopId)
|
||||
if err != nil {
|
||||
errMsg := "获取店铺数据失败: shopId " + dataVal.ShopId + " " + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析 json数据
|
||||
shopData, err := toolPdd.ParseShopData(shopDataStr)
|
||||
if err != nil {
|
||||
errMsg := "解析店铺数据失败:" + err.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
token := shopData.Shop.Token
|
||||
e, calleErr := callExe(dataVal.ImgUrl, token)
|
||||
if calleErr != nil {
|
||||
errMsg := "调用E程序失败: " + calleErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
//判断字符串 e 是否包含错误
|
||||
if strings.Contains(e, "错误") {
|
||||
errMsg := "调用E程序失败: " + e
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var pddImg planBTypeModules.GoodsImageUploadResponse
|
||||
|
||||
// 解析 JSON字符串
|
||||
unmarshalErr := json.Unmarshal([]byte(e), &pddImg)
|
||||
if unmarshalErr != nil {
|
||||
errMsg := "解析E程序返回数据失败: " + unmarshalErr.Error()
|
||||
tool.Error(httpMsg, errMsg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tool.Success(httpMsg, pddImg.GoodsImageUploadResponse.ImageURL)
|
||||
}
|
||||
|
||||
// 调用E程序
|
||||
func callExe(imgUrl string, token string) (string, error) {
|
||||
// 调用exe,传入两个参数
|
||||
cmd := exec.Command(golabl.Config.FileUrl.EFileName, imgUrl, token)
|
||||
|
||||
// 执行命令并获取输出
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// 如果命令执行失败,获取错误信息
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
return "", fmt.Errorf("程序执行失败,退出码: %d, 错误信息: %s",
|
||||
exitError.ExitCode(), string(exitError.Stderr))
|
||||
}
|
||||
return "", fmt.Errorf("执行失败: %v", err)
|
||||
}
|
||||
|
||||
// 返回exe打印的数据(去除首尾空白字符)
|
||||
result := strings.TrimSpace(string(output))
|
||||
return result, nil
|
||||
}
|
||||
75
go.mod
Normal file
75
go.mod
Normal file
@ -0,0 +1,75 @@
|
||||
module planA
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/minio/minio-go/v7 v7.1.0
|
||||
github.com/panjf2000/ants/v2 v2.11.4
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
golang.org/x/time v0.14.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/gorm v1.31.1
|
||||
modernc.org/sqlite v1.46.1
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.12.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // 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/crc32 v1.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/tinylib/msgp v1.6.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
modernc.org/libc v1.67.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
209
go.sum
Normal file
209
go.sum
Normal file
@ -0,0 +1,209 @@
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
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-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/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.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
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/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
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/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.99 h1:2vH/byrwUkIpFQFOilvTfaUpvAX3fEFhEzO+DR3DlCE=
|
||||
github.com/minio/minio-go/v7 v7.0.99/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8=
|
||||
github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/panjf2000/ants/v2 v2.11.4 h1:UJQbtN1jIcI5CYNocTj0fuAUYvsLjPoYi0YuhqV/Y48=
|
||||
github.com/panjf2000/ants/v2 v2.11.4/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
|
||||
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
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/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
|
||||
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
18
initialization/c/c.go
Normal file
18
initialization/c/c.go
Normal file
@ -0,0 +1,18 @@
|
||||
package c
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/initialization/golabl"
|
||||
"planA/tool/process"
|
||||
)
|
||||
|
||||
// RunC 运行C代码
|
||||
func RunC() {
|
||||
if golabl.Config.Server.IsC {
|
||||
//启动 C程序
|
||||
if err := process.RunCProgram(); err != nil {
|
||||
fmt.Println("启动C程序失败:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
108
initialization/config/config.go
Normal file
108
initialization/config/config.go
Normal file
@ -0,0 +1,108 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"planA/initialization/golabl"
|
||||
_configDll "planA/modules/config"
|
||||
"planA/tool"
|
||||
_type "planA/type"
|
||||
)
|
||||
|
||||
var (
|
||||
gDir string
|
||||
)
|
||||
|
||||
// Init 初始化
|
||||
// @param dir string 配置文件目录
|
||||
// @return _type.Config 配置文件信息
|
||||
// @return error 错误信息
|
||||
func Init(dir string) error {
|
||||
gDir = dir
|
||||
// 判断 ctx 是否取消
|
||||
checkContextErr := tool.CheckContext(golabl.Ctx)
|
||||
// 判断 结果
|
||||
if checkContextErr != nil {
|
||||
// 返回 且 返回错误
|
||||
return checkContextErr
|
||||
}
|
||||
//读取配置文件
|
||||
var config _type.Config
|
||||
dll, initConfigDLLErr := _configDll.InitConfigDLL()
|
||||
if initConfigDLLErr != nil {
|
||||
return initConfigDLLErr
|
||||
}
|
||||
configJson, ReadConfigFileErr := dll.ReadConfigFile(dir, "config.yaml")
|
||||
if ReadConfigFileErr != nil {
|
||||
return fmt.Errorf("读取配置文件失败:%v", ReadConfigFileErr)
|
||||
}
|
||||
jsonUnmarshalErr := json.Unmarshal([]byte(configJson), &config)
|
||||
if jsonUnmarshalErr != nil {
|
||||
return fmt.Errorf("解析配置文件失败:%v", jsonUnmarshalErr)
|
||||
}
|
||||
golabl.Config = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPddClient 获取拼多多配置
|
||||
// @return _type.PddConfig 拼多多配置
|
||||
// @return error 错误信息
|
||||
func GetPddClient() (_type.PddConfig, error) {
|
||||
//读取配置文件
|
||||
var config _type.Config
|
||||
dll, initConfigDLLErr := _configDll.InitConfigDLL()
|
||||
if initConfigDLLErr != nil {
|
||||
return _type.PddConfig{}, initConfigDLLErr
|
||||
}
|
||||
configJson, ReadConfigFileErr := dll.ReadConfigFile(gDir, "config.yaml")
|
||||
if ReadConfigFileErr != nil {
|
||||
return _type.PddConfig{}, fmt.Errorf("读取配置文件失败:%v", ReadConfigFileErr)
|
||||
}
|
||||
jsonUnmarshalErr := json.Unmarshal([]byte(configJson), &config)
|
||||
if jsonUnmarshalErr != nil {
|
||||
return _type.PddConfig{}, fmt.Errorf("解析配置文件失败:%v", jsonUnmarshalErr)
|
||||
}
|
||||
return config.PddConfig, nil
|
||||
}
|
||||
|
||||
// GetFileUrlConfig 获取文件路径配置
|
||||
// @return _type.FileUrl 文件路径配置
|
||||
// @return error 错误信息
|
||||
func GetFileUrlConfig() (_type.FileUrl, error) {
|
||||
//读取配置文件
|
||||
var config _type.Config
|
||||
dll, initConfigDLLErr := _configDll.InitConfigDLL()
|
||||
if initConfigDLLErr != nil {
|
||||
return _type.FileUrl{}, initConfigDLLErr
|
||||
}
|
||||
configJson, ReadConfigFileErr := dll.ReadConfigFile(gDir, "config.yaml")
|
||||
if ReadConfigFileErr != nil {
|
||||
return _type.FileUrl{}, fmt.Errorf("读取配置文件失败:%v", ReadConfigFileErr)
|
||||
}
|
||||
jsonUnmarshalErr := json.Unmarshal([]byte(configJson), &config)
|
||||
if jsonUnmarshalErr != nil {
|
||||
return _type.FileUrl{}, fmt.Errorf("解析配置文件失败:%v", jsonUnmarshalErr)
|
||||
}
|
||||
return config.FileUrl, nil
|
||||
}
|
||||
|
||||
// GetAliveConfig 获取存活状态配置
|
||||
// @return _type.Alive 存活状态配置
|
||||
// @return error 错误信息
|
||||
func GetAliveConfig() (_type.Alive, error) {
|
||||
//读取配置文件
|
||||
var config _type.Config
|
||||
dll, initConfigDLLErr := _configDll.InitConfigDLL()
|
||||
if initConfigDLLErr != nil {
|
||||
return _type.Alive{}, initConfigDLLErr
|
||||
}
|
||||
configJson, ReadConfigFileErr := dll.ReadConfigFile(gDir, "config.yaml")
|
||||
if ReadConfigFileErr != nil {
|
||||
return _type.Alive{}, fmt.Errorf("读取配置文件失败:%v", ReadConfigFileErr)
|
||||
}
|
||||
jsonUnmarshalErr := json.Unmarshal([]byte(configJson), &config)
|
||||
if jsonUnmarshalErr != nil {
|
||||
return _type.Alive{}, fmt.Errorf("解析配置文件失败:%v", jsonUnmarshalErr)
|
||||
}
|
||||
return config.Alive, nil
|
||||
}
|
||||
124
initialization/cron/cron.go
Normal file
124
initialization/cron/cron.go
Normal file
@ -0,0 +1,124 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/modules/logs"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
// Init 定时器初始化
|
||||
func Init() {
|
||||
c := cron.New(cron.WithSeconds()) // 支持秒级别的精度
|
||||
// 每日执行删除sqlite过期记录
|
||||
_, delSqlIteErr := c.AddFunc("0 0 0 * * ?", func() {
|
||||
DeleteOldSkuWatermarkImage() //删除过期的 sku水印图片
|
||||
DeleteOldWatermarkImage() //删除过期的水印图片
|
||||
DeleteOldExportFile() //删除过期的导出文件
|
||||
DeleteOldRedis() //删除 redis中过期数据
|
||||
DeleteOldRecords() //删除task_record过期记录
|
||||
DeleteOldExport() //删除task_export过期记录
|
||||
DeleteZipFile() //删除 zip文件
|
||||
DeleteDelTaskAndDelTaskDetails() //删除任务
|
||||
})
|
||||
if delSqlIteErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 每日执行删除sqlite过期记录 失败")
|
||||
return
|
||||
}
|
||||
//心跳检测 10秒
|
||||
_, heartbeatErr := c.AddFunc("0/10 * * * * ?", func() {
|
||||
CheckBannedWordSubstitutionUrlAlive() // 违禁词替换心跳
|
||||
CheckMysqlAlive() // mysql 心跳
|
||||
CheckRedisAlive() // redis 心跳
|
||||
CheckPddAlive() // 拼多多心跳
|
||||
CheckCreateTaskNoticeUrlAlive() // 创建任务通知心跳
|
||||
CheckXyBannedWord() // 闲鱼违禁词
|
||||
return
|
||||
})
|
||||
if heartbeatErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 心跳检测 失败")
|
||||
return
|
||||
}
|
||||
// 60秒钟检测一次
|
||||
_, bErr := c.AddFunc("0/60 * * * * ?", func() {
|
||||
B()
|
||||
})
|
||||
if bErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 B 函数 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 每日执行删除过期日志文件
|
||||
_, delLogErr := c.AddFunc("0 0 0 * * ?", func() {
|
||||
DeleteOldLog("logs\\debug")
|
||||
DeleteOldLog("logs\\info")
|
||||
DeleteOldLog("logs\\warning")
|
||||
DeleteOldLog("logs\\error")
|
||||
DeleteOldLog("logs\\success")
|
||||
})
|
||||
if delLogErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 删除过期日志文件 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 启动删除任务
|
||||
_, executeDelTaskErr := c.AddFunc("0/10 * * * * ?", func() {
|
||||
ExecuteDelTask()
|
||||
})
|
||||
if executeDelTaskErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 启动删除任务 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 30分钟执行一次
|
||||
_, verifyTokenErr := c.AddFunc("0 0/30 * * * ?", func() {
|
||||
VerifyToken()
|
||||
})
|
||||
if verifyTokenErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 30分钟执行一次 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除指定目录的文件夹
|
||||
_, delDirFolderErr := c.AddFunc("0 0 0 * * ?", func() {
|
||||
DeleteKfzTempImg()
|
||||
})
|
||||
if delDirFolderErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 删除指定目录的文件夹 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 五秒执行一次
|
||||
_, runFErr := c.AddFunc("0/5 * * * * ?", func() {
|
||||
runFErr := RunF()
|
||||
if runFErr != nil {
|
||||
fmt.Println(runFErr)
|
||||
logs.LoggingMiddleware("error", "定时任务 启动planF.exe 启动失败")
|
||||
return
|
||||
}
|
||||
})
|
||||
if runFErr != nil {
|
||||
logs.LoggingMiddleware("error", "定时任务 启动planF.exe 启动失败")
|
||||
return
|
||||
}
|
||||
|
||||
//// 备份 body_backup到硬盘 - 每分钟执行一次,使用锁防止并发(挪到C.exe)
|
||||
//_, backupBodyBackupErr := c.AddFunc("0 * * * * ?", func() {
|
||||
// BackupBodyBackup()
|
||||
//})
|
||||
//if backupBodyBackupErr != nil {
|
||||
// logs.LoggingMiddleware("error", "定时任务 备份 body_backup到硬盘 启动失败")
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//// 每天上午9点压缩昨天csv文件(挪到C.exe)
|
||||
//_, zipBackupFileErr := c.AddFunc("0 0 9 * * ?", func() {
|
||||
// ZipBackupFile()
|
||||
//})
|
||||
//if zipBackupFileErr != nil {
|
||||
// logs.LoggingMiddleware("error", "定时任务 zipBackupFile 启动失败")
|
||||
// return
|
||||
//}
|
||||
|
||||
c.Start() // 启动调度器(非阻塞)
|
||||
}
|
||||
841
initialization/cron/task.go
Normal file
841
initialization/cron/task.go
Normal file
@ -0,0 +1,841 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"planA/controlState/serviceAlive"
|
||||
"planA/controller"
|
||||
"planA/initialization/config"
|
||||
"planA/initialization/golabl"
|
||||
"planA/modules/logs"
|
||||
"planA/modules/pdd"
|
||||
"planA/rep"
|
||||
"planA/service"
|
||||
"planA/service/mysql"
|
||||
"planA/tool"
|
||||
toolPdd "planA/tool/pdd"
|
||||
"planA/tool/process"
|
||||
_type "planA/type"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeleteOldExportFile 删除N天前的导出文件
|
||||
func DeleteOldExportFile() {
|
||||
read := rep.CreateDbFactoryRead()
|
||||
lite, getTaskExportOldListErr := read.GetTaskExportOldList()
|
||||
if getTaskExportOldListErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取SQLite中N天前的记录失败:"+getTaskExportOldListErr.Error())
|
||||
return
|
||||
}
|
||||
for _, v := range lite {
|
||||
removeErr := os.Remove(v.FileUrl)
|
||||
if removeErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "删除文件失败:"+removeErr.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldRecords 删除 task_records 表中N天前的记录
|
||||
func DeleteOldRecords() {
|
||||
mysqlWrite, sqliteWrite := rep.CreateDbFactoryWrite()
|
||||
mysqlDeleteTaskRecordsOldDataErr := mysqlWrite.DeleteTaskRecordsOldData()
|
||||
if mysqlDeleteTaskRecordsOldDataErr != nil {
|
||||
errMsg := fmt.Sprintf("删除task_records表中N天前的记录失败: %v", mysqlDeleteTaskRecordsOldDataErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
sqLiteDeleteTaskRecordsOldDataErr := sqliteWrite.DeleteTaskRecordsOldData()
|
||||
if sqLiteDeleteTaskRecordsOldDataErr != nil {
|
||||
errMsg := fmt.Sprintf("删除task_records表中N天前的记录失败: %v", sqLiteDeleteTaskRecordsOldDataErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldExport 删除 task_export 表中N天前的记录
|
||||
func DeleteOldExport() {
|
||||
mysqlWrite, sqliteWrite := rep.CreateDbFactoryWrite()
|
||||
mysqlDeleteTaskExportOldDataErr := mysqlWrite.DeleteTaskExportOldData()
|
||||
if mysqlDeleteTaskExportOldDataErr != nil {
|
||||
errMsg := fmt.Sprintf("删除task_export表中N天前的记录失败: %v", mysqlDeleteTaskExportOldDataErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
sqliteDeleteTaskExportOldDataErr := sqliteWrite.DeleteTaskExportOldData()
|
||||
if sqliteDeleteTaskExportOldDataErr != nil {
|
||||
errMsg := fmt.Sprintf("删除task_export表中N天前的记录失败: %v", sqliteDeleteTaskExportOldDataErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CheckMysqlAlive mysql心跳
|
||||
func CheckMysqlAlive() {
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
mysql.GetTaskRecordsByTaskId("1")
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("mysql", elapsedMs)
|
||||
}
|
||||
|
||||
// CheckRedisAlive redis心跳
|
||||
func CheckRedisAlive() {
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
service.GetTaskBookPing()
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("redis", elapsedMs)
|
||||
}
|
||||
|
||||
// CheckPddAlive 拼多多心跳
|
||||
func CheckPddAlive() {
|
||||
token := ""
|
||||
//获取系统规定拼多多 token
|
||||
//urlConfig, _ := config.GetFileUrlConfig()
|
||||
//_, token, HttpGetRequestErr := tool.HttpGetRequest(urlConfig.PddTokenUrl)
|
||||
//if HttpGetRequestErr != nil {
|
||||
// logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取系统规定拼多多 token失败:"+HttpGetRequestErr.Error())
|
||||
// return
|
||||
//}
|
||||
|
||||
pddDll, initPddSOErr := pdd.InitPddDll()
|
||||
if initPddSOErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "初始化拼多多dll文件失败:"+initPddSOErr.Error())
|
||||
return
|
||||
}
|
||||
client, err := config.GetPddClient()
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取拼多多配置失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
_, pddTimeGetErr := pddDll.PddTimeGet(client.ClientId, client.ClientSecret, token)
|
||||
if pddTimeGetErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取拼多多系统时间失败:"+pddTimeGetErr.Error())
|
||||
return
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("pdd", elapsedMs)
|
||||
}
|
||||
|
||||
// CheckCreateTaskNoticeUrlAlive 价软件提交数据通知接口心跳
|
||||
func CheckCreateTaskNoticeUrlAlive() {
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
controller.TaskNoticeRequest("ping")
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("通知取出bodyOver接口", elapsedMs)
|
||||
}
|
||||
|
||||
// CheckBannedWordSubstitutionUrlAlive 违禁词接口心跳
|
||||
func CheckBannedWordSubstitutionUrlAlive() {
|
||||
|
||||
urlConfig, _ := config.GetFileUrlConfig()
|
||||
bannerWordDataReq := map[string]string{
|
||||
"isbn": "9787508618388",
|
||||
"bookName": "麦迪逊大道之王:大卫·奥格威转",
|
||||
"author": "[美]肯尼斯·罗曼",
|
||||
"publisher": "中信出版社",
|
||||
"shopId": "2029141110649929729",
|
||||
"replaceMark": "1",
|
||||
}
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
tool.HttpBannedWordSubstitution(urlConfig.BannedWordSubstitutionUrl, bannerWordDataReq)
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("违禁词替换接口", elapsedMs)
|
||||
}
|
||||
|
||||
// CheckXyBannedWord 闲鱼违禁词
|
||||
func CheckXyBannedWord() {
|
||||
url := golabl.Config.FileUrl.XYBannedWordSubstitutionUrl
|
||||
|
||||
//计算心跳时间
|
||||
start := time.Now()
|
||||
// 发送 GET 请求
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Printf("请求失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应体
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("读取响应失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
elapsedMs := int(elapsed.Milliseconds()) //将time.Duration类型转换为int类型的毫秒
|
||||
//设置状态
|
||||
serviceAlive.SetServiceAlive("闲鱼违禁词", elapsedMs)
|
||||
|
||||
// 解析 JSON
|
||||
var healthResp _type.HealthResponse
|
||||
err = json.Unmarshal(body, &healthResp)
|
||||
if err != nil {
|
||||
fmt.Printf("解析 JSON 失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断 code 是否为 200
|
||||
if healthResp.Code != 200 {
|
||||
serviceAlive.SetServiceAliveWithMsg("闲鱼违禁词", elapsedMs, healthResp.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldRedis 删除redisN天前的数据
|
||||
func DeleteOldRedis() {
|
||||
read := rep.CreateDbFactoryRead()
|
||||
list, getTaskRecordsOldListtErr := read.GetTaskRecordsOldList()
|
||||
if getTaskRecordsOldListtErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取task_export中N天前的记录失败:"+getTaskRecordsOldListtErr.Error())
|
||||
return
|
||||
}
|
||||
for _, v := range list {
|
||||
err := service.DelTask(v.TaskId)
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "删除任务失败:"+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// B 程序守护
|
||||
func B() {
|
||||
read := rep.CreateDbFactorySqliteRead()
|
||||
//查询task_records中24小时内的所有数据
|
||||
records, getTaskRecords24HourErr := read.GetTaskRecords24Hour()
|
||||
if getTaskRecords24HourErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取所有任务记录失败:"+getTaskRecords24HourErr.Error())
|
||||
return
|
||||
}
|
||||
for _, v := range records {
|
||||
//获取 header 信息
|
||||
header, getTaskHeaderErr := service.GetTaskHeader(v.TaskId)
|
||||
if getTaskHeaderErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取header 信息失败:"+getTaskHeaderErr.Error())
|
||||
continue
|
||||
}
|
||||
// 不能是淘宝的
|
||||
if header.Status != 0 && header.ShopType != "6" {
|
||||
// 启动 B程序
|
||||
_, runTaskWorkerErr := process.RunTaskWorker(v.TaskId)
|
||||
if runTaskWorkerErr != nil {
|
||||
//logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "启动B程序失败:"+runTaskWorkerErr.Error())
|
||||
continue
|
||||
}
|
||||
fmt.Println("守护进程成功启动任务B程序的窗口 任务ID:" + v.TaskId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldLog 删除日志N天以上的日志文件
|
||||
func DeleteOldLog(dir string) {
|
||||
// 配置参数
|
||||
pattern := `^[^-]+-[a-z]+-(\d{4}-\d{2}-\d{2})(?:-\d{2})?\.log$` // 匹配两种格式:ERROR-task-2026-03-23-04.log 和 ERROR-task-2026-03-23.log
|
||||
retentionDays := 3 // 保留天数
|
||||
|
||||
// 计算截止时间
|
||||
cutoffTime := time.Now().AddDate(0, 0, -retentionDays)
|
||||
|
||||
// 编译正则表达式
|
||||
regex, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("无效的正则表达式模式: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 遍历目录
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("无法读取目录: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
var cleanedCount int
|
||||
var errors []string
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
filename := entry.Name()
|
||||
|
||||
// 检查文件名是否匹配模式并提取日期
|
||||
matches := regex.FindStringSubmatch(filename)
|
||||
if len(matches) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析文件名中的日期
|
||||
dateStr := matches[1] // 格式: 2026-03-23
|
||||
fileDate, err := time.Parse("2006-01-02", dateStr)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Sprintf("解析日期失败 %s: %v", filename, err))
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查文件日期是否早于截止时间
|
||||
if fileDate.Before(cutoffTime) {
|
||||
filePath := filepath.Join(dir, filename)
|
||||
// 删除文件
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("删除失败 %s: %v", filename, err))
|
||||
} else {
|
||||
cleanedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 输出清理结果
|
||||
if cleanedCount > 0 || len(errors) > 0 {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_INFO, fmt.Sprintf("清理完成: 删除了 %d 个文件", cleanedCount))
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("清理过程中遇到 %d 个错误", len(errors)))
|
||||
for _, errMsg := range errors {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldWatermarkImage 删除N天以上的水印图片
|
||||
func DeleteOldWatermarkImage() {
|
||||
// 目标根目录(你提供的目录)
|
||||
rootDir := `img\watermark`
|
||||
|
||||
// 计算需要删除的截止时间:当前时间 - N天
|
||||
days := golabl.Config.Server.DataDay
|
||||
expireTime := time.Now().AddDate(0, 0, -days)
|
||||
|
||||
// 遍历根目录
|
||||
err := filepath.Walk(rootDir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法访问目录 %s: %v", path, err)
|
||||
}
|
||||
|
||||
// 只处理一级子文件夹(不递归)
|
||||
if path == rootDir {
|
||||
return nil
|
||||
}
|
||||
if !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析文件夹名称为日期(格式:2006-01-02)
|
||||
dirName := f.Name()
|
||||
dirTime, err := time.Parse("2006-01-02", dirName)
|
||||
if err != nil {
|
||||
// 不是日期格式的文件夹跳过
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断是否超过N天
|
||||
if dirTime.Before(expireTime) {
|
||||
// 删除文件夹(包括里面所有内容)
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法删除目录 %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 只处理一级子目录,不递归深入
|
||||
return filepath.SkipDir
|
||||
})
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("删除N天以上的水印图片失败: %v", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteOldSkuWatermarkImage 删除N天以上的sku水印图片
|
||||
func DeleteOldSkuWatermarkImage() {
|
||||
// 目标根目录(你提供的目录)
|
||||
rootDir := `img\skuwatermark`
|
||||
|
||||
// 计算需要删除的截止时间:当前时间 - N天
|
||||
days := golabl.Config.Server.DataDay
|
||||
expireTime := time.Now().AddDate(0, 0, -days)
|
||||
|
||||
// 遍历根目录
|
||||
err := filepath.Walk(rootDir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法访问目录 %s: %v", path, err)
|
||||
}
|
||||
|
||||
// 只处理一级子文件夹(不递归)
|
||||
if path == rootDir {
|
||||
return nil
|
||||
}
|
||||
if !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析文件夹名称为日期(格式:2006-01-02)
|
||||
dirName := f.Name()
|
||||
dirTime, err := time.Parse("2006-01-02", dirName)
|
||||
if err != nil {
|
||||
// 不是日期格式的文件夹跳过
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断是否超过N天
|
||||
if dirTime.Before(expireTime) {
|
||||
// 删除文件夹(包括里面所有内容)
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法删除目录 %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 只处理一级子目录,不递归深入
|
||||
return filepath.SkipDir
|
||||
})
|
||||
if err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("删除N天以上的水印图片失败: %v", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// BackupBodyBackup 备份 body_backup到硬盘
|
||||
func BackupBodyBackup() {
|
||||
|
||||
// 定义导出目录
|
||||
csvUrl := golabl.Config.FileUrl.BackupUrl
|
||||
fmt.Println("路径:" + csvUrl)
|
||||
// 获取所有任务数据
|
||||
read := rep.CreateDbFactoryRead()
|
||||
list, getAllTaskErr := read.GetAllTask()
|
||||
if getAllTaskErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "获取所有任务数据失败:"+getAllTaskErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
|
||||
dateDir := v.CreateAt.Format("2006-01-02") // 按日期分组
|
||||
// 构建完整的目录路径
|
||||
taskCsvUrl := filepath.Join(csvUrl, dateDir)
|
||||
|
||||
// 获取 backup数据长度
|
||||
backupLen, getBodyBackupLenErr := service.GetBodyBackupLen(v.TaskId)
|
||||
if getBodyBackupLenErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取任务 %s 的backup长度失败:%v", v.TaskId, getBodyBackupLenErr))
|
||||
continue // 跳过当前任务,继续处理下一个
|
||||
}
|
||||
if backupLen == 0 {
|
||||
// 如果 backup中没有数据则跳过
|
||||
continue
|
||||
}
|
||||
|
||||
csvFileName := fmt.Sprintf("%v.csv", v.TaskId)
|
||||
// 检查并创建目录(如果不存在)
|
||||
err := os.MkdirAll(taskCsvUrl, 0755)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("创建目录失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
// 拼接完整的文件路径
|
||||
fullPath := filepath.Join(taskCsvUrl, csvFileName)
|
||||
|
||||
// 判断文件是否存在,决定是创建新文件还是追加写入
|
||||
var file *os.File
|
||||
if _, err := os.Stat(fullPath); err == nil {
|
||||
// 文件存在,以追加模式打开
|
||||
file, err = os.OpenFile(fullPath, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("打开文件失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
continue
|
||||
}
|
||||
} else if os.IsNotExist(err) {
|
||||
// 文件不存在,创建新文件
|
||||
file, err = os.Create(fullPath)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("创建文件失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// 其他错误(如权限问题)
|
||||
errMsg := fmt.Sprintf("检查文件状态失败: %v", err)
|
||||
fmt.Println(errMsg)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
continue
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 创建 CSV写入器
|
||||
writer := csv.NewWriter(file)
|
||||
defer writer.Flush()
|
||||
|
||||
// 循环获取并写入数据
|
||||
for i := 0; i < int(backupLen); i++ {
|
||||
// 获取 backup数据
|
||||
one, getBodyBackupOneErr := service.GetBodyBackupOne(v.TaskId)
|
||||
if getBodyBackupOneErr != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取任务 %s 的body_backup数据失败:%v", v.TaskId, getBodyBackupOneErr))
|
||||
break // 跳出当前循环,继续下一个任务
|
||||
}
|
||||
|
||||
// 将数据写入到CSV文件的一行(A列)
|
||||
// 假设 one 是字符串类型,如果是结构体需要根据实际字段调整
|
||||
record := []string{one} // 如果 one 不是字符串,需要转换,例如 fmt.Sprintf("%v", one)
|
||||
|
||||
if err := writer.Write(record); err != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("写入CSV数据失败:%v", err))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_INFO, fmt.Sprintf("任务 %s 的数据已成功写入文件:%s", v.TaskId, fullPath))
|
||||
}
|
||||
}
|
||||
|
||||
// ZipBackupFile 压缩backup文件
|
||||
func ZipBackupFile() {
|
||||
csvUrl := golabl.Config.FileUrl.BackupUrl
|
||||
// 获取昨天的日期
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||
// 拼接完整的文件路径
|
||||
taskCsvUrl := filepath.Join(csvUrl, yesterday)
|
||||
|
||||
// 检查源目录是否存在
|
||||
srcInfo, err := os.Stat(taskCsvUrl)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("目录不存在: %s", taskCsvUrl)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果不是目录,则直接返回
|
||||
if !srcInfo.IsDir() {
|
||||
log.Printf("路径不是目录: %s", taskCsvUrl)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建zip文件
|
||||
zipFileName := taskCsvUrl + ".zip"
|
||||
zipFile, err := os.Create(zipFileName)
|
||||
if err != nil {
|
||||
log.Printf("创建zip文件失败: %v", err)
|
||||
return
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
// 创建zip writer
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// 遍历目录并添加文件
|
||||
err = filepath.Walk(taskCsvUrl, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 跳过目录本身
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取相对路径作为zip内的文件名
|
||||
relPath, err := filepath.Rel(taskCsvUrl, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建zip中的文件头
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Name = filepath.Join(yesterday, relPath) // 保留目录结构
|
||||
header.Method = zip.Deflate
|
||||
|
||||
// 创建zip中的文件
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 打开并复制源文件
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(writer, file)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("压缩失败: %v", err)
|
||||
os.Remove(zipFileName) // 删除不完整的zip文件
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("压缩成功: %s", zipFileName)
|
||||
|
||||
// 压缩成功后删除原目录
|
||||
err = os.RemoveAll(taskCsvUrl)
|
||||
if err != nil {
|
||||
log.Printf("删除原目录失败: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("成功删除原目录: %s", taskCsvUrl)
|
||||
}
|
||||
|
||||
// DeleteZipFile 删除zip文件
|
||||
func DeleteZipFile() {
|
||||
zipDir := golabl.Config.FileUrl.BackupUrl
|
||||
day := golabl.Config.Server.DataDay
|
||||
|
||||
// 获取当前时间
|
||||
now := time.Now()
|
||||
// 计算截止时间(当天0点减去指定天数)
|
||||
cutoffTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -day)
|
||||
|
||||
// 读取目录
|
||||
entries, err := os.ReadDir(zipDir)
|
||||
if err != nil {
|
||||
fmt.Printf("读取目录失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
deletedCount := 0
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
fileName := entry.Name()
|
||||
if !strings.HasSuffix(fileName, ".zip") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 从文件名解析日期(格式:2026-04-10.zip)
|
||||
dateStr := strings.TrimSuffix(fileName, ".zip")
|
||||
fileDate, err := time.Parse("2006-01-02", dateStr)
|
||||
if err != nil {
|
||||
// 如果文件名不符合日期格式,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果文件日期早于或等于截止时间,则删除
|
||||
if !fileDate.After(cutoffTime) {
|
||||
filePath := filepath.Join(zipDir, fileName)
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("删除文件失败 %s: %v\n", filePath, err)
|
||||
} else {
|
||||
fmt.Printf("已删除文件: %s\n", filePath)
|
||||
deletedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("共删除 %d 个%d天前的zip文件\n", deletedCount, day)
|
||||
}
|
||||
|
||||
// ExecuteDelTask 查询del_task 表中待执行
|
||||
func ExecuteDelTask() {
|
||||
delTask, err := mysql.GetDelTask()
|
||||
if err != nil {
|
||||
fmt.Println("查询del_task 表中待执行失败:", err)
|
||||
}
|
||||
for _, v := range delTask {
|
||||
//如果是暂停中的任务,将任务状态修改为执行中
|
||||
if *v.Status == 2 {
|
||||
// 要求 v.PauseAt 不能等于 nil 并且 v.PauseAt 必须大于当前时间24小时
|
||||
if v.PauseAt != nil && !time.Now().After(v.PauseAt.Add(24*time.Hour)) {
|
||||
continue
|
||||
}
|
||||
//修改任务状态
|
||||
err := mysql.UpdateDelTaskStatus(v.ID)
|
||||
if err != nil {
|
||||
fmt.Println("修改任务状态失败:", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
//启动任务
|
||||
_, err := process.RunDprogram(*v.TaskID)
|
||||
if err != nil {
|
||||
fmt.Println("启动任务失败:", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteDelTaskAndDelTaskDetails 清理删除任务与删除任务详情过期的数据
|
||||
func DeleteDelTaskAndDelTaskDetails() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("DeleteDelTaskAndDelTaskDetails panic: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
task, getExpiredDelTaskErr := mysql.GetExpiredDelTask()
|
||||
if getExpiredDelTaskErr != nil {
|
||||
errMsg := fmt.Sprintf("查询过期的删除任务失败:%v", getExpiredDelTaskErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
fmt.Println(errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range task {
|
||||
// 检查必要字段是否为 nil
|
||||
if v.TaskType == nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_WARNING, fmt.Sprintf("任务记录中 TaskType 为 nil,跳过处理 ID: %d", v.ID))
|
||||
continue
|
||||
}
|
||||
|
||||
if v.TaskID == nil {
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_WARNING, fmt.Sprintf("任务记录中 TaskID 为 nil,跳过处理 ID: %d", v.ID))
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理任务类型 2 或 3
|
||||
if *v.TaskType == 2 || *v.TaskType == 3 {
|
||||
// 删除 header 与 footer
|
||||
delTaskErr := service.DelTask(*v.TaskID)
|
||||
if delTaskErr != nil {
|
||||
errMsg := fmt.Sprintf("删除任务失败 TaskID: %s, Error: %v", *v.TaskID, delTaskErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
fmt.Println(errMsg)
|
||||
// 注意:这里用 continue 而不是 return,避免一个任务失败影响其他任务
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 删除删除任务明细表
|
||||
deleteDelTaskDetailErr := mysql.DeleteDelTaskDetail(*v.TaskID)
|
||||
if deleteDelTaskDetailErr != nil {
|
||||
errMsg := fmt.Sprintf("删除删除任务明细表失败 TaskID: %s, Error: %v", *v.TaskID, deleteDelTaskDetailErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
fmt.Println(errMsg)
|
||||
continue
|
||||
}
|
||||
|
||||
// 删除任务
|
||||
deleteDelTaskByIdErr := mysql.DeleteDelTaskById(v.ID)
|
||||
if deleteDelTaskByIdErr != nil {
|
||||
errMsg := fmt.Sprintf("删除任务失败 ID: %d, Error: %v", v.ID, deleteDelTaskByIdErr)
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_ERROR, errMsg)
|
||||
fmt.Println(errMsg)
|
||||
continue
|
||||
}
|
||||
|
||||
logs.LoggingMiddleware(logs.LOG_LEVEL_INFO, fmt.Sprintf("成功清理任务 ID: %d, TaskID: %s", v.ID, *v.TaskID))
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyToken 验证token过期
|
||||
func VerifyToken() {
|
||||
list, getPddTokenListErr := service.GetPddTokenList()
|
||||
if getPddTokenListErr != nil {
|
||||
fmt.Println("获取token列表失败:", getPddTokenListErr)
|
||||
return
|
||||
}
|
||||
pddDll, initPddSOErr := pdd.InitPddDll()
|
||||
if initPddSOErr != nil {
|
||||
fmt.Println("初始化pdd.so失败:", initPddSOErr)
|
||||
return
|
||||
}
|
||||
for _, v := range list {
|
||||
//使用类目预测接口测试Token 是否有效
|
||||
buildPddGoodsOuterCatMappingGetErr := toolPdd.BuildPddGoodsOuterCatMappingGet(pddDll, v.Token)
|
||||
if buildPddGoodsOuterCatMappingGetErr != nil {
|
||||
if buildPddGoodsOuterCatMappingGetErr.Error() == "拼多多Token已过期" {
|
||||
//fmt.Printf("token 过期的店铺 %v 店铺id %v\n", v.ShopName, v.ID)
|
||||
reqData := map[string]string{
|
||||
"shopId": v.ID,
|
||||
}
|
||||
_, submitFormDataErr := tool.SubmitFormData(golabl.Config.FileUrl.UpdateTokenUrl, reqData)
|
||||
if submitFormDataErr != nil {
|
||||
fmt.Println("提交表单数据失败:", submitFormDataErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteKfzTempImg 删除本地临时的孔夫子图片
|
||||
func DeleteKfzTempImg() {
|
||||
err := tool.CleanOldFolders(golabl.Config.FileUrl.KfzImgTempUrl, 3)
|
||||
if err != nil {
|
||||
fmt.Println("删除本地临时的孔夫子图片失败:", err)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////*********检查F程序启动***********/////////////////////////
|
||||
|
||||
func RunF() error {
|
||||
exeName := golabl.Config.FileUrl.FFileName
|
||||
running, err := isProcessRunning(exeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("检查进程状态出错: %v\n", err)
|
||||
}
|
||||
|
||||
if !running {
|
||||
runFProgramErr := process.RunFProgram()
|
||||
if runFProgramErr != nil {
|
||||
return runFProgramErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func isProcessRunning(exePath string) (bool, error) {
|
||||
exeName := filepath.Base(exePath)
|
||||
|
||||
// 方法1:使用 tasklist(适用于Windows)
|
||||
cmd := exec.Command("tasklist", "/FI", fmt.Sprintf("IMAGENAME eq %s", exeName), "/NH", "/FO", "CSV")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// 检查是否是"未找到进程"的错误
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if exitErr.ExitCode() == 1 {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
outputStr := string(output)
|
||||
// 如果输出包含进程名且不包含"No tasks",则认为进程在运行
|
||||
return strings.Contains(outputStr, exeName) &&
|
||||
!strings.Contains(outputStr, "INFO: No tasks"), nil
|
||||
}
|
||||
|
||||
/////////////////*********检查F程序启动***********/////////////////////////
|
||||
28
initialization/golabl/golabl.go
Normal file
28
initialization/golabl/golabl.go
Normal file
@ -0,0 +1,28 @@
|
||||
package golabl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_type "planA/type"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/mux"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
Ctx context.Context
|
||||
Config _type.Config
|
||||
MysqlDb *gorm.DB
|
||||
PsiMysqlDb *gorm.DB
|
||||
RedisDbA *redis.Client
|
||||
RedisDbB *redis.Client
|
||||
RedisDbC *redis.Client
|
||||
RedisDbD *redis.Client
|
||||
SqliteDb *sql.DB
|
||||
Router = mux.NewRouter()
|
||||
RedisExp = time.Duration(Config.Server.RedisExp) * time.Hour
|
||||
Validator *validator.Validate
|
||||
)
|
||||
102
initialization/init.go
Normal file
102
initialization/init.go
Normal file
@ -0,0 +1,102 @@
|
||||
package initialization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"planA/initialization/c"
|
||||
"planA/initialization/config"
|
||||
"planA/initialization/cron"
|
||||
"planA/initialization/golabl"
|
||||
"planA/initialization/middle"
|
||||
"planA/initialization/mysql"
|
||||
"planA/initialization/redis"
|
||||
"planA/initialization/router"
|
||||
"planA/initialization/sqLite"
|
||||
"planA/initialization/validator"
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
//初始化上下文
|
||||
golabl.Ctx = context.Background()
|
||||
// 初始化配置
|
||||
configErr := config.Init("")
|
||||
if configErr != nil {
|
||||
return fmt.Errorf("初始化配置失败: %v", configErr)
|
||||
}
|
||||
// 初始化 mysql
|
||||
mysqlErr := mysql.Init()
|
||||
if mysqlErr != nil {
|
||||
return fmt.Errorf("初始化mysql失败: %v", mysqlErr)
|
||||
}
|
||||
// 初始化 redis
|
||||
redisErr := redis.Init()
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化redis失败: %v", redisErr)
|
||||
}
|
||||
// 初始化 sqlite
|
||||
sqliteErr := sqLite.Init()
|
||||
if sqliteErr != nil {
|
||||
return fmt.Errorf("初始化sqlite失败: %v", sqliteErr)
|
||||
}
|
||||
// 初始化验证器
|
||||
validator.Init()
|
||||
// 初始化定时任务(非阻塞,因此不需要返回错误)
|
||||
cron.Init()
|
||||
//初始化中间件
|
||||
middle.Init()
|
||||
//初始化路由
|
||||
router.Init()
|
||||
//运行 C程序
|
||||
c.RunC()
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Server 启动服务
|
||||
func Server() {
|
||||
// 从配置获取端口并启动服务
|
||||
port := ":" + golabl.Config.Server.Port
|
||||
fmt.Printf("服务器启动在 http://localhost%s\n", port)
|
||||
// 打印所有可用端点(控制台输出)
|
||||
printAvailableEndpoints()
|
||||
// 启动HTTP服务,如果失败则记录致命错误
|
||||
log.Fatal(http.ListenAndServe(port, golabl.Router))
|
||||
}
|
||||
|
||||
// printAvailableEndpoints 打印所有可用的API端点
|
||||
func printAvailableEndpoints() {
|
||||
fmt.Println("\n========== 可用API端点 ==========")
|
||||
|
||||
fmt.Println("\n【任务管理】")
|
||||
fmt.Println(" POST /task/create - 创建新任务")
|
||||
fmt.Println(" GET /task/pause/{id} - 暂停任务")
|
||||
fmt.Println(" GET /task/resume/{id} - 恢复任务")
|
||||
fmt.Println(" GET /task/stop/{id} - 停止任务")
|
||||
fmt.Println(" GET /task/over/{id} - 完成任务")
|
||||
fmt.Println(" GET /task/get - 获取任务列表(支持查询参数)")
|
||||
fmt.Println(" GET /task/getByUserId - 根据用户ID获取任务")
|
||||
fmt.Println(" POST /task/setTaskBody - 设置任务内容")
|
||||
fmt.Println(" GET /task/b - 运行B程序")
|
||||
|
||||
fmt.Println("\n【任务导出】")
|
||||
fmt.Println(" GET /task/export/exportTaskDetail/{id} - 导出指定任务详情")
|
||||
fmt.Println(" GET /task/export/exportTaskDetail/{userId}/{id} - 导出指定用户的指定任务详情")
|
||||
fmt.Println(" GET /task/export/get - 获取所有导出任务列表")
|
||||
fmt.Println(" GET /task/export/get/{userId} - 获取指定用户的导出任务列表")
|
||||
|
||||
fmt.Println("\n【商品任务】")
|
||||
fmt.Println(" POST /task/goods/add - 添加商品任务")
|
||||
fmt.Println(" GET /task/goods/get/{id} - 获取指定商品任务详情")
|
||||
fmt.Println(" PUT /task/goods/set/{id} - 更新指定商品任务")
|
||||
fmt.Println(" DELETE /task/goods/del/{id} - 删除指定商品任务")
|
||||
|
||||
fmt.Println("\n【系统工具】")
|
||||
fmt.Println(" GET /alive/get - 获取服务存活状态列表")
|
||||
fmt.Println(" GET /health - 健康检查")
|
||||
fmt.Println(" GET /export/ - 导出文件下载服务")
|
||||
fmt.Println(" GET / - 服务欢迎页")
|
||||
|
||||
fmt.Println("\n=====================================")
|
||||
}
|
||||
25
initialization/middle/cors.go
Normal file
25
initialization/middle/cors.go
Normal file
@ -0,0 +1,25 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Cors 跨域中间件
|
||||
func Cors(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 设置CORS头
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH")
|
||||
|
||||
// 处理OPTIONS请求
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusNoContent) // 204
|
||||
return
|
||||
}
|
||||
|
||||
// 处理正常请求
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
378
initialization/middle/logs.go
Normal file
378
initialization/middle/logs.go
Normal file
@ -0,0 +1,378 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
_myLogs "planA/modules/logs"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoggingMiddleware 中间自动记录
|
||||
func LoggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 初始化日志
|
||||
if err := _myLogs.InitializeLogger("logs"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := _myLogs.SetLogTaskType("task"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录请求开始时间
|
||||
startTime := time.Now()
|
||||
|
||||
// 收集基本信息
|
||||
clientIP := getClientIP(r)
|
||||
userAgent := r.UserAgent()
|
||||
referer := r.Referer()
|
||||
|
||||
// 记录请求信息(但不立即打印,等待收集完数据)
|
||||
baseMsg := fmt.Sprintf(
|
||||
"Request: %s %s | ClientIP: %s | User-Agent: %s | Referer: %s",
|
||||
r.Method,
|
||||
r.URL.Path,
|
||||
clientIP,
|
||||
userAgent,
|
||||
referer,
|
||||
)
|
||||
|
||||
// 处理不同类型的请求数据
|
||||
var requestData string
|
||||
var requestBody []byte
|
||||
|
||||
// 如果是需要记录数据的请求方法
|
||||
if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
contentLength := r.ContentLength
|
||||
|
||||
// 根据 Content-Type 处理不同的数据格式
|
||||
if strings.Contains(contentType, "multipart/form-data") {
|
||||
// 处理 multipart/form-data
|
||||
requestData = processMultipartFormData(r, baseMsg)
|
||||
} else if strings.Contains(contentType, "application/x-www-form-urlencoded") {
|
||||
// 处理表单数据
|
||||
requestData = processFormData(r)
|
||||
} else if strings.Contains(contentType, "application/json") ||
|
||||
strings.Contains(contentType, "text/plain") ||
|
||||
strings.Contains(contentType, "application/xml") {
|
||||
// 处理 JSON、文本等
|
||||
requestData = processBodyData(r, &requestBody)
|
||||
} else {
|
||||
// 其他类型
|
||||
requestData = fmt.Sprintf("Content-Type: %s, Content-Length: %d", contentType, contentLength)
|
||||
}
|
||||
} else if r.Method == "GET" || r.Method == "HEAD" {
|
||||
// GET 请求参数
|
||||
requestData = fmt.Sprintf("Query: %s", r.URL.RawQuery)
|
||||
}
|
||||
|
||||
// 组合完整的日志消息
|
||||
fullMsg := baseMsg
|
||||
if requestData != "" {
|
||||
fullMsg += " | " + requestData
|
||||
}
|
||||
|
||||
// 记录请求头信息(可选)
|
||||
headers := []string{"Authorization", "Accept", "Accept-Encoding"}
|
||||
for _, header := range headers {
|
||||
if value := r.Header.Get(header); value != "" {
|
||||
fullMsg += fmt.Sprintf(" | %s: %s", header, sanitizeHeaderValue(header, value))
|
||||
}
|
||||
}
|
||||
|
||||
// 记录请求信息
|
||||
if err := _myLogs.LogInfo(fullMsg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果读取了请求体,需要恢复它
|
||||
if requestBody != nil {
|
||||
// 重新设置请求体
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||
}
|
||||
|
||||
// 使用 ResponseWriter 包装器
|
||||
crw := &captureResponseWriter{
|
||||
ResponseWriter: w,
|
||||
statusCode: 200,
|
||||
}
|
||||
|
||||
// 处理请求
|
||||
next.ServeHTTP(crw, r)
|
||||
|
||||
// 记录响应信息
|
||||
duration := time.Since(startTime)
|
||||
responseMsg := fmt.Sprintf(
|
||||
"Response: %s %s | Status: %d | Duration: %v | Size: %d",
|
||||
r.Method,
|
||||
r.URL.Path,
|
||||
crw.statusCode,
|
||||
duration,
|
||||
crw.size,
|
||||
)
|
||||
|
||||
_myLogs.LogInfo(responseMsg)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理 multipart/form-data
|
||||
func processMultipartFormData(r *http.Request, baseMsg string) string {
|
||||
// 保存原始请求体
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error reading body: %v", err)
|
||||
}
|
||||
|
||||
// 恢复请求体供后续使用
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
|
||||
// 解析 multipart
|
||||
reader := bytes.NewReader(body)
|
||||
boundary := extractBoundary(r.Header.Get("Content-Type"))
|
||||
if boundary == "" {
|
||||
return fmt.Sprintf("Content-Type: multipart/form-data (no boundary)")
|
||||
}
|
||||
|
||||
mr := multipart.NewReader(reader, boundary)
|
||||
|
||||
var formData []string
|
||||
sensitiveFields := []string{"password", "token", "secret", "key", "file"}
|
||||
|
||||
for {
|
||||
part, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
formData = append(formData, fmt.Sprintf("Parse error: %v", err))
|
||||
break
|
||||
}
|
||||
|
||||
fieldName := part.FormName()
|
||||
fileName := part.FileName()
|
||||
|
||||
if fileName != "" {
|
||||
// 这是文件上传
|
||||
formData = append(formData, fmt.Sprintf("%s: [FILE] %s (size unknown)", fieldName, fileName))
|
||||
} else {
|
||||
// 这是普通字段
|
||||
partData, _ := io.ReadAll(part)
|
||||
value := string(partData)
|
||||
|
||||
// 检查是否是敏感字段
|
||||
isSensitive := false
|
||||
for _, sensitive := range sensitiveFields {
|
||||
if strings.Contains(strings.ToLower(fieldName), sensitive) {
|
||||
isSensitive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isSensitive {
|
||||
formData = append(formData, fmt.Sprintf("%s: [FILTERED]", fieldName))
|
||||
} else {
|
||||
// 限制长度,避免日志过大
|
||||
if len(value) > 100 {
|
||||
value = value[:100] + "...(truncated)"
|
||||
}
|
||||
formData = append(formData, fmt.Sprintf("%s: %s", fieldName, value))
|
||||
}
|
||||
}
|
||||
part.Close()
|
||||
}
|
||||
|
||||
if len(formData) > 0 {
|
||||
return fmt.Sprintf("FormData: %s", strings.Join(formData, ", "))
|
||||
}
|
||||
|
||||
return "FormData: (empty)"
|
||||
}
|
||||
|
||||
// 从 Content-Type 提取 boundary
|
||||
func extractBoundary(contentType string) string {
|
||||
parts := strings.Split(contentType, "boundary=")
|
||||
if len(parts) > 1 {
|
||||
return strings.Trim(parts[1], "\" ;")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 处理普通表单数据
|
||||
func processFormData(r *http.Request) string {
|
||||
// 复制请求以便解析
|
||||
r2 := r.Clone(r.Context())
|
||||
if err := r2.ParseForm(); err != nil {
|
||||
return fmt.Sprintf("Form parse error: %v", err)
|
||||
}
|
||||
|
||||
var formData []string
|
||||
sensitiveFields := []string{"password", "token", "secret", "key"}
|
||||
|
||||
for key, values := range r2.Form {
|
||||
isSensitive := false
|
||||
for _, sensitive := range sensitiveFields {
|
||||
if strings.Contains(strings.ToLower(key), sensitive) {
|
||||
isSensitive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isSensitive {
|
||||
formData = append(formData, fmt.Sprintf("%s: [FILTERED]", key))
|
||||
} else {
|
||||
valueStr := strings.Join(values, ",")
|
||||
if len(valueStr) > 100 {
|
||||
valueStr = valueStr[:100] + "...(truncated)"
|
||||
}
|
||||
formData = append(formData, fmt.Sprintf("%s: %s", key, valueStr))
|
||||
}
|
||||
}
|
||||
|
||||
if len(formData) > 0 {
|
||||
return fmt.Sprintf("Form: %s", strings.Join(formData, ", "))
|
||||
}
|
||||
|
||||
return "Form: (empty)"
|
||||
}
|
||||
|
||||
// 处理请求体数据(JSON、文本等)
|
||||
func processBodyData(r *http.Request, bodyBuf *[]byte) string {
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
contentLength := r.ContentLength
|
||||
|
||||
// 读取请求体
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Content-Type: %s, Content-Length: %d, Error reading: %v",
|
||||
contentType, contentLength, err)
|
||||
}
|
||||
|
||||
// 保存到缓冲区,以便后续恢复
|
||||
*bodyBuf = body
|
||||
|
||||
// 如果是 JSON,尝试美化输出
|
||||
if strings.Contains(contentType, "application/json") && len(body) > 0 {
|
||||
var js map[string]interface{}
|
||||
if err := json.Unmarshal(body, &js); err == nil {
|
||||
// 过滤敏感字段
|
||||
js = sanitizeJSON(js)
|
||||
|
||||
// 转换为字符串,限制长度
|
||||
jsonStr, _ := json.Marshal(js)
|
||||
if len(jsonStr) > 200 {
|
||||
jsonStr = jsonStr[:200]
|
||||
return fmt.Sprintf("JSON: %s...(truncated)", string(jsonStr))
|
||||
}
|
||||
return fmt.Sprintf("JSON: %s", string(jsonStr))
|
||||
}
|
||||
}
|
||||
|
||||
// 普通文本
|
||||
if len(body) > 0 {
|
||||
// 限制长度
|
||||
bodyStr := string(body)
|
||||
if len(bodyStr) > 200 {
|
||||
bodyStr = bodyStr[:200] + "...(truncated)"
|
||||
}
|
||||
return fmt.Sprintf("Body: %s", bodyStr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Content-Type: %s, Content-Length: %d", contentType, contentLength)
|
||||
}
|
||||
|
||||
// 过滤 JSON 中的敏感字段
|
||||
func sanitizeJSON(data map[string]interface{}) map[string]interface{} {
|
||||
sensitiveFields := []string{"password", "token", "secret", "key", "creditCard", "ssn"}
|
||||
|
||||
for key, value := range data {
|
||||
keyLower := strings.ToLower(key)
|
||||
|
||||
// 检查是否是敏感字段
|
||||
isSensitive := false
|
||||
for _, sensitive := range sensitiveFields {
|
||||
if strings.Contains(keyLower, sensitive) {
|
||||
isSensitive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isSensitive {
|
||||
data[key] = "[FILTERED]"
|
||||
} else if subMap, ok := value.(map[string]interface{}); ok {
|
||||
// 递归处理嵌套对象
|
||||
data[key] = sanitizeJSON(subMap)
|
||||
} else if arr, ok := value.([]interface{}); ok {
|
||||
// 处理数组
|
||||
for i, item := range arr {
|
||||
if subMap, ok := item.(map[string]interface{}); ok {
|
||||
arr[i] = sanitizeJSON(subMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 获取客户端真实 IP(保持不变)
|
||||
func getClientIP(r *http.Request) string {
|
||||
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
|
||||
return strings.Split(ip, ",")[0]
|
||||
}
|
||||
if ip := r.Header.Get("X-Real-IP"); ip != "" {
|
||||
return ip
|
||||
}
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// 清理头信息(保持不变)
|
||||
func sanitizeHeaderValue(header, value string) string {
|
||||
if strings.ToLower(header) == "authorization" {
|
||||
parts := strings.Split(value, " ")
|
||||
if len(parts) > 1 {
|
||||
return parts[0] + " [FILTERED]"
|
||||
}
|
||||
return "[FILTERED]"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// ResponseWriter 包装器(保持不变)
|
||||
type captureResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
size int64
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (crw *captureResponseWriter) WriteHeader(statusCode int) {
|
||||
if !crw.wroteHeader {
|
||||
crw.statusCode = statusCode
|
||||
crw.wroteHeader = true
|
||||
crw.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (crw *captureResponseWriter) Write(b []byte) (int, error) {
|
||||
if !crw.wroteHeader {
|
||||
crw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err := crw.ResponseWriter.Write(b)
|
||||
crw.size += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (crw *captureResponseWriter) Unwrap() http.ResponseWriter {
|
||||
return crw.ResponseWriter
|
||||
}
|
||||
11
initialization/middle/middle.go
Normal file
11
initialization/middle/middle.go
Normal file
@ -0,0 +1,11 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"planA/initialization/golabl"
|
||||
)
|
||||
|
||||
// Init 初始化中间件
|
||||
func Init() {
|
||||
golabl.Router.Use(Cors) //跨域
|
||||
golabl.Router.Use(Response) //响应
|
||||
}
|
||||
12
initialization/middle/response.go
Normal file
12
initialization/middle/response.go
Normal file
@ -0,0 +1,12 @@
|
||||
package middle
|
||||
|
||||
import "net/http"
|
||||
|
||||
func Response(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 全局设置 Content-Type 为 application/json
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
// 调用下一个处理函数(核心业务逻辑)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
66
initialization/middle/sign.go
Normal file
66
initialization/middle/sign.go
Normal file
@ -0,0 +1,66 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"planA/tool"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Sign(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 1. 读取请求体并备份
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "读取请求失败", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
// 2. 获取 boundary
|
||||
var boundary string
|
||||
ct := r.Header.Get("Content-Type")
|
||||
if strings.Contains(ct, "multipart/form-data") {
|
||||
for _, p := range strings.Split(ct, ";") {
|
||||
p := strings.TrimSpace(p)
|
||||
if strings.HasPrefix(p, "boundary=") {
|
||||
boundary = strings.TrimPrefix(p, "boundary=")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 解析所有字段,同名字段拼接成一个字符串(验签用)
|
||||
paramMap := make(map[string]string)
|
||||
if boundary != "" {
|
||||
reader := multipart.NewReader(bytes.NewBuffer(bodyBytes), boundary)
|
||||
for {
|
||||
part, err := reader.NextPart()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
name := part.FormName()
|
||||
val, _ := io.ReadAll(part)
|
||||
// 同名字段拼接(关键!!!)
|
||||
paramMap[name] += string(val)
|
||||
part.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 验签
|
||||
sign := paramMap["sign"]
|
||||
if sign == "" {
|
||||
tool.Error(w, "签名不能为空", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !tool.VerifySign(paramMap) {
|
||||
tool.Error(w, "签名失败", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 放行
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
175
initialization/mysql/mysql.go
Normal file
175
initialization/mysql/mysql.go
Normal file
@ -0,0 +1,175 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/initialization/golabl"
|
||||
"time"
|
||||
|
||||
mysqlModle "planA/type/mysql"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// Init 初始化数据库连接
|
||||
// @return error 错误信息
|
||||
func Init() error {
|
||||
//mysql
|
||||
mysqlDBInitErr := mysqlDBInit()
|
||||
if mysqlDBInitErr != nil {
|
||||
return mysqlDBInitErr
|
||||
}
|
||||
//psiMysqlInit
|
||||
psiMysqlInitErr := psiMysqlInit()
|
||||
if psiMysqlInitErr != nil {
|
||||
return psiMysqlInitErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mysqlDBInit
|
||||
func mysqlDBInit() error {
|
||||
|
||||
// 1. 获取mysql配置
|
||||
mysqlConfig := golabl.Config.MysqlConfig
|
||||
|
||||
// 2. 配置 DSN
|
||||
dsn := fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
mysqlConfig.User,
|
||||
mysqlConfig.Password,
|
||||
mysqlConfig.Host,
|
||||
mysqlConfig.Port,
|
||||
mysqlConfig.DBName,
|
||||
)
|
||||
|
||||
// 3. 配置 GORM 连接选项
|
||||
|
||||
logLevel := logger.Silent
|
||||
switch mysqlConfig.Loglevel {
|
||||
case "info":
|
||||
logLevel = logger.Info
|
||||
case "warn":
|
||||
logLevel = logger.Warn
|
||||
case "error":
|
||||
logLevel = logger.Error
|
||||
case "silent":
|
||||
logLevel = logger.Silent
|
||||
}
|
||||
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logLevel), //日志级别
|
||||
DisableForeignKeyConstraintWhenMigrating: true, //不创建外键约束
|
||||
}
|
||||
|
||||
// 4. 连接数据库
|
||||
db, openErr := gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
|
||||
// 5. 获取底层 sql.DB,配置连接池
|
||||
sqlDB, dbErr := db.DB()
|
||||
if dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
// 连接池优化 + 保活配置
|
||||
sqlDB.SetMaxOpenConns(mysqlConfig.MaxOpenConns)
|
||||
sqlDB.SetMaxIdleConns(mysqlConfig.MaxIdleConns)
|
||||
sqlDB.SetConnMaxIdleTime(mysqlConfig.ConnMaxIdleTime * time.Minute)
|
||||
sqlDB.SetConnMaxLifetime(mysqlConfig.ConnMaxLifetime * time.Hour)
|
||||
|
||||
// 5. 验证连接
|
||||
if dbPingErr := sqlDB.Ping(); dbPingErr != nil {
|
||||
return dbPingErr
|
||||
}
|
||||
|
||||
// 6. 迁移表结构
|
||||
if migrateErr := Migrate(db); migrateErr != nil {
|
||||
return migrateErr
|
||||
}
|
||||
|
||||
// 7. 保存db实例
|
||||
golabl.MysqlDb = db
|
||||
return nil
|
||||
}
|
||||
|
||||
// Migrate 迁移表
|
||||
func Migrate(db *gorm.DB) error {
|
||||
// task_records表
|
||||
if err := mysqlModle.MigrateTaskRecords(db); err != nil {
|
||||
return err
|
||||
}
|
||||
// task_export表
|
||||
if err := mysqlModle.MigrateTaskExport(db); err != nil {
|
||||
return err
|
||||
}
|
||||
// del_task表
|
||||
if err := mysqlModle.MigrateDelTask(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// psiMysql
|
||||
func psiMysqlInit() error {
|
||||
|
||||
// 1. 获取mysql配置
|
||||
mysqlConfig := golabl.Config.PsiMysqlConfig
|
||||
|
||||
// 2. 配置 DSN
|
||||
dsn := fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
mysqlConfig.User,
|
||||
mysqlConfig.Password,
|
||||
mysqlConfig.Host,
|
||||
mysqlConfig.Port,
|
||||
mysqlConfig.DBName,
|
||||
)
|
||||
|
||||
// 3. 配置 GORM 连接选项
|
||||
|
||||
logLevel := logger.Silent
|
||||
switch mysqlConfig.Loglevel {
|
||||
case "info":
|
||||
logLevel = logger.Info
|
||||
case "warn":
|
||||
logLevel = logger.Warn
|
||||
case "error":
|
||||
logLevel = logger.Error
|
||||
case "silent":
|
||||
logLevel = logger.Silent
|
||||
}
|
||||
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logLevel), //日志级别
|
||||
DisableForeignKeyConstraintWhenMigrating: true, //不创建外键约束
|
||||
}
|
||||
|
||||
// 4. 连接数据库
|
||||
db, openErr := gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
|
||||
// 5. 获取底层 sql.DB,配置连接池
|
||||
sqlDB, dbErr := db.DB()
|
||||
if dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
// 连接池优化 + 保活配置
|
||||
sqlDB.SetMaxOpenConns(mysqlConfig.MaxOpenConns)
|
||||
sqlDB.SetMaxIdleConns(mysqlConfig.MaxIdleConns)
|
||||
sqlDB.SetConnMaxIdleTime(mysqlConfig.ConnMaxIdleTime * time.Minute)
|
||||
sqlDB.SetConnMaxLifetime(mysqlConfig.ConnMaxLifetime * time.Hour)
|
||||
|
||||
// 5. 验证连接
|
||||
if dbPingErr := sqlDB.Ping(); dbPingErr != nil {
|
||||
return dbPingErr
|
||||
}
|
||||
|
||||
// 7. 保存db实例
|
||||
golabl.PsiMysqlDb = db
|
||||
return nil
|
||||
}
|
||||
78
initialization/redis/redis.go
Normal file
78
initialization/redis/redis.go
Normal file
@ -0,0 +1,78 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/initialization/golabl"
|
||||
_type "planA/type"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// Init 初始化Redis连接
|
||||
// @return error 错误信息
|
||||
func Init() error {
|
||||
|
||||
// 1. 获取redis配置
|
||||
redisConfig := golabl.Config.RedisConfig
|
||||
redisClientA, redisErr := NewRedisClient(redisConfig[0])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[0].Addr, redisConfig[0].DB, redisErr)
|
||||
}
|
||||
golabl.RedisDbA = redisClientA
|
||||
|
||||
// Redis B - Redis实例
|
||||
redisClientB, redisErr := NewRedisClient(redisConfig[1])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[1].Addr, redisConfig[1].DB, redisErr)
|
||||
}
|
||||
golabl.RedisDbB = redisClientB
|
||||
|
||||
// Redis C - Redis实例
|
||||
redisClientC, redisErr := NewRedisClient(redisConfig[2])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[2].Addr, redisConfig[2].DB, redisErr)
|
||||
}
|
||||
golabl.RedisDbC = redisClientC
|
||||
|
||||
// Redis D - Redis实例
|
||||
redisClientD, redisErr := NewRedisClient(redisConfig[6])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[6].Addr, redisConfig[6].DB, redisErr)
|
||||
}
|
||||
golabl.RedisDbD = redisClientD
|
||||
|
||||
//设置默认过期时间
|
||||
golabl.RedisExp = time.Duration(golabl.Config.Server.RedisExp) * time.Hour
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRedisClient 创建redis 客户端
|
||||
// @param config redis配置
|
||||
// @return *redis.Client redis客户端
|
||||
// @return error 错误信息
|
||||
func NewRedisClient(config _type.RedisConfig) (*redis.Client, error) {
|
||||
ctx := golabl.Ctx
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: config.Addr, // 连接地址
|
||||
Password: config.Password, // 密码
|
||||
DB: config.DB, // 数据库
|
||||
PoolSize: config.PoolSize, // 连接池大小
|
||||
PoolTimeout: time.Duration(config.PoolTimeout), // 连接池超时时间
|
||||
ReadTimeout: time.Duration(config.ReadTimeout), // 读取超时
|
||||
WriteTimeout: time.Duration(config.WriteTimeout), // 写入超时
|
||||
DialTimeout: time.Duration(config.DialTimeout), // 连接超时
|
||||
IdleTimeout: time.Duration(config.IdleTimeout), // 空闲超时
|
||||
MinIdleConns: config.MinIdleConns, // 最小空闲连接数
|
||||
IdleCheckFrequency: time.Duration(config.IdleCheckFrequency), // 空闲检查频率
|
||||
MaxRetries: config.MaxRetries, // 最大重试次数
|
||||
MaxRetryBackoff: time.Duration(config.MaxRetryBackoff), // 最大重试间隔
|
||||
MinRetryBackoff: time.Duration(config.MinRetryBackoff), // 最小重试间隔
|
||||
})
|
||||
// 测试连接
|
||||
_, err := rdb.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
return rdb, err
|
||||
}
|
||||
return rdb, nil
|
||||
}
|
||||
17
initialization/router/router.go
Normal file
17
initialization/router/router.go
Normal file
@ -0,0 +1,17 @@
|
||||
package router
|
||||
|
||||
import "planA/router"
|
||||
|
||||
// Init 初始化路由
|
||||
func Init() {
|
||||
router.DefaultInit()
|
||||
router.TaskInit()
|
||||
router.TaskExportInit()
|
||||
router.StaticInit()
|
||||
router.AdmiinInir()
|
||||
router.Alive()
|
||||
router.DelTaskInit()
|
||||
router.UploadImgInit()
|
||||
router.ShopInit()
|
||||
router.BodyInit()
|
||||
}
|
||||
46
initialization/sqLite/sqLite.go
Normal file
46
initialization/sqLite/sqLite.go
Normal file
@ -0,0 +1,46 @@
|
||||
package sqLite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"planA/initialization/golabl"
|
||||
sqLiteServer "planA/service/sqLite"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// Init 初始化sqlIte连接
|
||||
// @return error 错误信息
|
||||
func Init() error {
|
||||
// 1. 打开数据库
|
||||
db, err := sql.Open("sqlite", "./taskDb.db")
|
||||
if err != nil {
|
||||
return errors.New("打开sqLite数据库失败:" + err.Error())
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return errors.New("无法连接到sqLite数据库:" + err.Error())
|
||||
}
|
||||
golabl.SqliteDb = db
|
||||
// 自动创建表
|
||||
if err := CreateTable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTable 自动建表
|
||||
func CreateTable() error {
|
||||
createTaskIdTabErr := sqLiteServer.CreateTaskIdTab()
|
||||
if createTaskIdTabErr != nil {
|
||||
return fmt.Errorf("自动创建表失败: %v", createTaskIdTabErr)
|
||||
}
|
||||
createTaskExportTabErr := sqLiteServer.CreateTaskExportTab()
|
||||
if createTaskExportTabErr != nil {
|
||||
return fmt.Errorf("自动创建表失败: %v", createTaskExportTabErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
11
initialization/validator/validator.go
Normal file
11
initialization/validator/validator.go
Normal file
@ -0,0 +1,11 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"planA/initialization/golabl"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
golabl.Validator = validator.New()
|
||||
}
|
||||
17
main.go
Normal file
17
main.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/initialization"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化
|
||||
err := initialization.Init()
|
||||
if err != nil {
|
||||
fmt.Println("初始化失败:", err)
|
||||
return
|
||||
}
|
||||
//启动服务
|
||||
initialization.Server()
|
||||
}
|
||||
BIN
modules/config/config.dll
Normal file
BIN
modules/config/config.dll
Normal file
Binary file not shown.
74
modules/config/conifg.go
Normal file
74
modules/config/conifg.go
Normal file
@ -0,0 +1,74 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ConfigDLL 配置文件读取DLL结构
|
||||
type ConfigDLL struct {
|
||||
dll *syscall.DLL
|
||||
readConfigFile *syscall.Proc // 读取配置文件
|
||||
getVersion *syscall.Proc // 获取版本信息
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// InitConfigDLL 初始化ConfigDLL
|
||||
func InitConfigDLL() (*ConfigDLL, error) {
|
||||
dllPath := filepath.Join("modules/config/", "config.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("config DLL 不存在: %s", dllPath)
|
||||
}
|
||||
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||
return nil, fmt.Errorf("加载config DLL 失败: %s", err)
|
||||
} else {
|
||||
return &ConfigDLL{
|
||||
dll: dll,
|
||||
readConfigFile: dll.MustFindProc("ReadConfigFile"),
|
||||
getVersion: dll.MustFindProc("GetVersion"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// cStr 获取C字符串
|
||||
func (m *ConfigDLL) cStr(p uintptr) string {
|
||||
if p == 0 {
|
||||
return ""
|
||||
}
|
||||
b := []byte{}
|
||||
for i := uintptr(0); ; i++ {
|
||||
c := *(*byte)(unsafe.Pointer(p + i))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
}
|
||||
s := string(b)
|
||||
if m.freeCString != nil {
|
||||
m.freeCString.Call(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ReadConfigFile 读取配置文件
|
||||
func (m *ConfigDLL) ReadConfigFile(filePath, fileName string) (string, error) {
|
||||
proc, err := m.dll.FindProc("ReadConfigFile")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ReadConfigFile: %v", err)
|
||||
}
|
||||
|
||||
filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
||||
fileNamePtr, _ := syscall.BytePtrFromString(fileName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(filePathPtr)),
|
||||
uintptr(unsafe.Pointer(fileNamePtr)),
|
||||
)
|
||||
|
||||
result := m.cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
BIN
modules/image/image.dll
Normal file
BIN
modules/image/image.dll
Normal file
Binary file not shown.
107
modules/image/image.go
Normal file
107
modules/image/image.go
Normal file
@ -0,0 +1,107 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"planA/planB/config"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gImageDll *ImageDLL
|
||||
)
|
||||
|
||||
// ImageDLL 图片工具DLL结构
|
||||
type ImageDLL struct {
|
||||
Dll *syscall.DLL
|
||||
AddWatermarkFromURLEx *syscall.Proc // 打水印
|
||||
}
|
||||
|
||||
// InitImageDll 初始化 imageDLL
|
||||
func InitImageDll() (*ImageDLL, error) {
|
||||
fileConfig, getDllFileConfigErr := config.GetFileUrlConfig()
|
||||
if getDllFileConfigErr != nil {
|
||||
return nil, getDllFileConfigErr
|
||||
}
|
||||
dllPath := filepath.Join(fileConfig.ImageDll, "image.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("Image DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载Image DLL 失败: %s", err)
|
||||
}
|
||||
gImageDll = &ImageDLL{
|
||||
Dll: dll,
|
||||
AddWatermarkFromURLEx: dll.MustFindProc("AddWatermarkFromURLEx"),
|
||||
}
|
||||
return gImageDll, nil
|
||||
}
|
||||
|
||||
// WatermarkConfig 添加水印
|
||||
type WatermarkConfig struct {
|
||||
SourceImageURL string // 源图片URL地址
|
||||
WatermarkURL string // 水印图片URL地址
|
||||
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轴偏移量
|
||||
YOffset int // Y轴偏移量
|
||||
Timeout int // 下载超时时间(秒),默认30秒
|
||||
OutputFormat string // 输出格式: "jpeg", "png", "auto"(默认auto,根据源图片格式)auto
|
||||
JPEGQuality int // JPEG质量 (1-100),默认95
|
||||
}
|
||||
|
||||
// AddWatermarkFromURLExs 添加水印
|
||||
func (m *ImageDLL) AddWatermarkFromURLExs(sourceImageUrl, watermarkUrl string) (string, error) {
|
||||
|
||||
watermarkConfig := WatermarkConfig{
|
||||
SourceImageURL: sourceImageUrl,
|
||||
WatermarkURL: watermarkUrl,
|
||||
Position: "center",
|
||||
Opacity: 1.0,
|
||||
Scale: 1.0,
|
||||
TileSpacing: 50,
|
||||
Timeout: 30,
|
||||
OutputFormat: "jpeg",
|
||||
JPEGQuality: 95,
|
||||
}
|
||||
watermarkConfigJson, err := json.Marshal(watermarkConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||||
}
|
||||
|
||||
proc, err := m.Dll.FindProc("AddWatermarkFromURLEx")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 AddWatermarkFromURLEx: %v", err)
|
||||
}
|
||||
watermarkConfigJsonPtr, _ := syscall.BytePtrFromString(string(watermarkConfigJson))
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(watermarkConfigJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
395
modules/logs/dll.go
Normal file
395
modules/logs/dll.go
Normal file
@ -0,0 +1,395 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"planA/initialization/golabl"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
LOG_LEVEL_DEBUG = "DEBUG"
|
||||
LOG_LEVEL_INFO = "INFO"
|
||||
LOG_LEVEL_WARNING = "WARNING"
|
||||
LOG_LEVEL_ERROR = "ERROR"
|
||||
LOG_LEVEL_SUCCESS = "SUCCESS"
|
||||
)
|
||||
|
||||
// LoggerDLL 封装 logger.dll 操作
|
||||
type LoggerDLL struct {
|
||||
dll *syscall.LazyDLL
|
||||
createLogger *syscall.LazyProc
|
||||
createContext *syscall.LazyProc
|
||||
logInfo *syscall.LazyProc
|
||||
logError *syscall.LazyProc
|
||||
logWarning *syscall.LazyProc
|
||||
logSuccess *syscall.LazyProc
|
||||
freeString *syscall.LazyProc
|
||||
closeAllLoggers *syscall.LazyProc
|
||||
}
|
||||
|
||||
// LoggerConfig logger配置结构
|
||||
type LoggerConfig struct {
|
||||
LogDir string `json:"log_dir"`
|
||||
SplitType int `json:"split_type"`
|
||||
RotateType int `json:"rotate_type"`
|
||||
MaxSize int64 `json:"max_size"`
|
||||
MaxCount int `json:"max_count"`
|
||||
Level int `json:"level"`
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
DefaultTaskType string `json:"default_task_type"`
|
||||
}
|
||||
|
||||
var loggerDLLInstance *LoggerDLL
|
||||
var loggerHandle string
|
||||
var loggerContextHandle string
|
||||
|
||||
// ensureLoggerDLL 确保logger DLL已加载
|
||||
func ensureLoggerDLL() (*LoggerDLL, error) {
|
||||
if loggerDLLInstance != nil {
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// 检查是否在Windows平台
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil, fmt.Errorf("logger DLL only supported on Windows platform")
|
||||
}
|
||||
|
||||
dllPath := filepath.Join(golabl.Config.FileUrl.LogDll, "logger.dll")
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
// 尝试从当前目录查找
|
||||
if _, err := os.Stat("logger.dll"); err == nil {
|
||||
dllPath = "logger.dll"
|
||||
} else {
|
||||
return nil, fmt.Errorf("logger DLL not found at %s", dllPath)
|
||||
}
|
||||
}
|
||||
|
||||
dll := syscall.NewLazyDLL(dllPath)
|
||||
|
||||
loggerDLLInstance = &LoggerDLL{
|
||||
dll: dll,
|
||||
createLogger: dll.NewProc("CreateLogger"),
|
||||
createContext: dll.NewProc("CreateContextWithTaskType"),
|
||||
logInfo: dll.NewProc("LogInfo"),
|
||||
logError: dll.NewProc("LogError"),
|
||||
logWarning: dll.NewProc("LogWarning"),
|
||||
logSuccess: dll.NewProc("LogSuccess"),
|
||||
freeString: dll.NewProc("FreeString"),
|
||||
closeAllLoggers: dll.NewProc("CloseAllLoggers"),
|
||||
}
|
||||
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// InitializeLogger 初始化logger
|
||||
func InitializeLogger(logDir string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 确保日志目录存在
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("创建日志目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建logger配置
|
||||
config := LoggerConfig{
|
||||
LogDir: logDir,
|
||||
SplitType: 2, // SplitByDay
|
||||
RotateType: 0, // RotateBySize
|
||||
MaxSize: 100 * 1024 * 1024, // 100MB
|
||||
MaxCount: 10,
|
||||
Level: 1, // LevelInfo - 只显示INFO及以上级别的日志
|
||||
EnableCaller: true,
|
||||
DefaultTaskType: "main",
|
||||
}
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 调用CreateLogger
|
||||
configPtr, _ := syscall.BytePtrFromString(string(configJSON))
|
||||
ret, _, _ := m.createLogger.Call(uintptr(unsafe.Pointer(configPtr)))
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger失败")
|
||||
}
|
||||
|
||||
// 获取logger句柄
|
||||
handle := cStr(ret)
|
||||
loggerHandle = handle
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
// 创建默认上下文
|
||||
return createLoggerContext("main")
|
||||
}
|
||||
|
||||
// createLoggerContext 创建带任务类型的logger上下文
|
||||
func createLoggerContext(taskType string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerHandle == "" {
|
||||
return fmt.Errorf("logger未初始化")
|
||||
}
|
||||
|
||||
handlePtr, _ := syscall.BytePtrFromString(loggerHandle)
|
||||
taskTypePtr, _ := syscall.BytePtrFromString(taskType)
|
||||
|
||||
ret, _, _ := m.createContext.Call(
|
||||
uintptr(unsafe.Pointer(handlePtr)),
|
||||
uintptr(unsafe.Pointer(taskTypePtr)),
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger上下文失败")
|
||||
}
|
||||
|
||||
// 获取上下文句柄
|
||||
loggerContextHandle = cStr(ret)
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogTaskType 设置当前日志任务类型
|
||||
func SetLogTaskType(taskType string) error {
|
||||
return createLoggerContext(taskType)
|
||||
}
|
||||
|
||||
// LogInfo 记录信息日志
|
||||
func LogInfo(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logInfo.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogError 记录错误日志
|
||||
func LogError(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logError.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogWarning 记录警告日志
|
||||
func LogWarning(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logWarning.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogSuccess 记录成功日志
|
||||
func LogSuccess(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logSuccess.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseLogger 关闭logger
|
||||
func CloseLogger() error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, _, _ := m.closeAllLoggers.Call()
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("关闭logger失败")
|
||||
}
|
||||
|
||||
m.freeString.Call(ret)
|
||||
|
||||
loggerHandle = ""
|
||||
loggerContextHandle = ""
|
||||
loggerDLLInstance = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLoggerHandle 获取当前logger句柄(用于外部调用)
|
||||
func GetLoggerHandle() string {
|
||||
return loggerContextHandle
|
||||
}
|
||||
|
||||
// IsLoggerInitialized 检查logger是否已初始化
|
||||
func IsLoggerInitialized() bool {
|
||||
return loggerHandle != "" && loggerContextHandle != ""
|
||||
}
|
||||
|
||||
// SetConsoleOutput 设置控制台输出开关
|
||||
func SetConsoleOutput(enabled bool) {
|
||||
if enabled {
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
} else {
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithLevel 带级别的日志记录,可以精确控制显示
|
||||
func LogWithLevel(level, message string, showConsole bool) {
|
||||
if !IsLoggerInitialized() {
|
||||
return
|
||||
}
|
||||
|
||||
switch level {
|
||||
case "ERROR":
|
||||
LogError(message)
|
||||
case "WARNING":
|
||||
LogWarning(message)
|
||||
case "SUCCESS":
|
||||
LogSuccess(message)
|
||||
case "INFO":
|
||||
LogInfo(message)
|
||||
default:
|
||||
LogInfo(message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogOnlyFile 仅写入文件,不输出到控制台
|
||||
func LogOnlyFile(level, message string) {
|
||||
// 临时禁用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
LogWithLevel(level, message, false)
|
||||
}
|
||||
|
||||
// LogConsoleAndFile 同时输出到控制台和文件
|
||||
func LogConsoleAndFile(level, message string) {
|
||||
// 临时启用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
LogWithLevel(level, message, true)
|
||||
}
|
||||
|
||||
// LoggingMiddleware 记录日志
|
||||
func LoggingMiddleware(level string, str string) {
|
||||
initializeLoggerErr := InitializeLogger("logs")
|
||||
if initializeLoggerErr != nil {
|
||||
fmt.Println("初始化日志失败:", initializeLoggerErr)
|
||||
return
|
||||
}
|
||||
setLogTaskTypeErr := SetLogTaskType("task")
|
||||
if setLogTaskTypeErr != nil {
|
||||
fmt.Println("设置日志任务类型失败:", setLogTaskTypeErr)
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case level == LOG_LEVEL_ERROR:
|
||||
fmt.Println(str)
|
||||
logErrorErr := LogError(str)
|
||||
if logErrorErr != nil {
|
||||
fmt.Println("记录错误日志失败:", logErrorErr)
|
||||
return
|
||||
}
|
||||
case level == LOG_LEVEL_WARNING:
|
||||
logWarningErr := LogWarning(str)
|
||||
if logWarningErr != nil {
|
||||
fmt.Println("记录警告日志失败:", logWarningErr)
|
||||
return
|
||||
}
|
||||
case level == LOG_LEVEL_SUCCESS:
|
||||
logSuccessErr := LogSuccess(str)
|
||||
if logSuccessErr != nil {
|
||||
fmt.Println("记录成功日志失败:", logSuccessErr)
|
||||
return
|
||||
}
|
||||
default:
|
||||
logInfoErr := LogInfo(str)
|
||||
if logInfoErr != nil {
|
||||
fmt.Println("记录信息日志失败:", logInfoErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
modules/logs/logger.dll
Normal file
BIN
modules/logs/logger.dll
Normal file
Binary file not shown.
602
modules/logs/logger.md
Normal file
602
modules/logs/logger.md
Normal file
@ -0,0 +1,602 @@
|
||||
# logger.dll 使用教程
|
||||
## 1. 创建DLL工具实例
|
||||
### 加载DLL文件
|
||||
```gotemplate
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// LoggerDLL 封装 logger.dll 操作
|
||||
type LoggerDLL struct {
|
||||
dll *syscall.LazyDLL
|
||||
createLogger *syscall.LazyProc
|
||||
createContext *syscall.LazyProc
|
||||
logInfo *syscall.LazyProc
|
||||
logError *syscall.LazyProc
|
||||
logWarning *syscall.LazyProc
|
||||
logSuccess *syscall.LazyProc
|
||||
freeString *syscall.LazyProc
|
||||
closeAllLoggers *syscall.LazyProc
|
||||
}
|
||||
|
||||
// LoggerConfig logger配置结构
|
||||
type LoggerConfig struct {
|
||||
LogDir string `json:"log_dir"`
|
||||
SplitType int `json:"split_type"`
|
||||
RotateType int `json:"rotate_type"`
|
||||
MaxSize int64 `json:"max_size"`
|
||||
MaxCount int `json:"max_count"`
|
||||
Level int `json:"level"`
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
DefaultTaskType string `json:"default_task_type"`
|
||||
}
|
||||
|
||||
var loggerDLLInstance *LoggerDLL
|
||||
var loggerHandle string
|
||||
var loggerContextHandle string
|
||||
|
||||
// ensureLoggerDLL 确保logger DLL已加载
|
||||
func ensureLoggerDLL() (*LoggerDLL, error) {
|
||||
if loggerDLLInstance != nil {
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// 检查是否在Windows平台
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil, fmt.Errorf("logger DLL only supported on Windows platform")
|
||||
}
|
||||
|
||||
// logger.dll 位于 dll/logger.dll
|
||||
//dllPath := filepath.Join("modules", "logs", "logger.dll")
|
||||
dllPath := "D:\\www\\wwwroot\\planA\\modules\\logs\\logger.dll"
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
// 尝试从当前目录查找
|
||||
if _, err := os.Stat("logger.dll"); err == nil {
|
||||
dllPath = "logger.dll"
|
||||
} else {
|
||||
return nil, fmt.Errorf("logger DLL not found at %s", dllPath)
|
||||
}
|
||||
}
|
||||
|
||||
dll := syscall.NewLazyDLL(dllPath)
|
||||
|
||||
loggerDLLInstance = &LoggerDLL{
|
||||
dll: dll,
|
||||
createLogger: dll.NewProc("CreateLogger"),
|
||||
createContext: dll.NewProc("CreateContextWithTaskType"),
|
||||
logInfo: dll.NewProc("LogInfo"),
|
||||
logError: dll.NewProc("LogError"),
|
||||
logWarning: dll.NewProc("LogWarning"),
|
||||
logSuccess: dll.NewProc("LogSuccess"),
|
||||
freeString: dll.NewProc("FreeString"),
|
||||
closeAllLoggers: dll.NewProc("CloseAllLoggers"),
|
||||
}
|
||||
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// InitializeLogger 初始化logger
|
||||
func InitializeLogger(logDir string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 确保日志目录存在
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("创建日志目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建logger配置
|
||||
config := LoggerConfig{
|
||||
LogDir: logDir,
|
||||
SplitType: 1, // SplitByDay
|
||||
RotateType: 0, // RotateBySize
|
||||
MaxSize: 100 * 1024 * 1024, // 100MB
|
||||
MaxCount: 10,
|
||||
Level: 1, // LevelInfo - 只显示INFO及以上级别的日志
|
||||
EnableCaller: true,
|
||||
DefaultTaskType: "main",
|
||||
}
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 调用CreateLogger
|
||||
configPtr, _ := syscall.BytePtrFromString(string(configJSON))
|
||||
ret, _, _ := m.createLogger.Call(uintptr(unsafe.Pointer(configPtr)))
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger失败")
|
||||
}
|
||||
|
||||
// 获取logger句柄
|
||||
handle := cStr(ret)
|
||||
loggerHandle = handle
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
// 创建默认上下文
|
||||
return createLoggerContext("main")
|
||||
}
|
||||
|
||||
// createLoggerContext 创建带任务类型的logger上下文
|
||||
func createLoggerContext(taskType string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerHandle == "" {
|
||||
return fmt.Errorf("logger未初始化")
|
||||
}
|
||||
|
||||
handlePtr, _ := syscall.BytePtrFromString(loggerHandle)
|
||||
taskTypePtr, _ := syscall.BytePtrFromString(taskType)
|
||||
|
||||
ret, _, _ := m.createContext.Call(
|
||||
uintptr(unsafe.Pointer(handlePtr)),
|
||||
uintptr(unsafe.Pointer(taskTypePtr)),
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger上下文失败")
|
||||
}
|
||||
|
||||
// 获取上下文句柄
|
||||
loggerContextHandle = cStr(ret)
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogTaskType 设置当前日志任务类型
|
||||
func SetLogTaskType(taskType string) error {
|
||||
return createLoggerContext(taskType)
|
||||
}
|
||||
|
||||
// LogInfo 记录信息日志
|
||||
func LogInfo(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logInfo.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogError 记录错误日志
|
||||
func LogError(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logError.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogWarning 记录警告日志
|
||||
func LogWarning(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logWarning.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogSuccess 记录成功日志
|
||||
func LogSuccess(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logSuccess.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseLogger 关闭logger
|
||||
func CloseLogger() error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, _, _ := m.closeAllLoggers.Call()
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("关闭logger失败")
|
||||
}
|
||||
|
||||
m.freeString.Call(ret)
|
||||
|
||||
loggerHandle = ""
|
||||
loggerContextHandle = ""
|
||||
loggerDLLInstance = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLoggerHandle 获取当前logger句柄(用于外部调用)
|
||||
func GetLoggerHandle() string {
|
||||
return loggerContextHandle
|
||||
}
|
||||
|
||||
// IsLoggerInitialized 检查logger是否已初始化
|
||||
func IsLoggerInitialized() bool {
|
||||
return loggerHandle != "" && loggerContextHandle != ""
|
||||
}
|
||||
|
||||
// SetConsoleOutput 设置控制台输出开关
|
||||
func SetConsoleOutput(enabled bool) {
|
||||
if enabled {
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
} else {
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithLevel 带级别的日志记录,可以精确控制显示
|
||||
func LogWithLevel(level, message string, showConsole bool) {
|
||||
if !IsLoggerInitialized() {
|
||||
return
|
||||
}
|
||||
|
||||
switch level {
|
||||
case "ERROR":
|
||||
LogError(message)
|
||||
case "WARNING":
|
||||
LogWarning(message)
|
||||
case "SUCCESS":
|
||||
LogSuccess(message)
|
||||
case "INFO":
|
||||
LogInfo(message)
|
||||
default:
|
||||
LogInfo(message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogOnlyFile 仅写入文件,不输出到控制台
|
||||
func LogOnlyFile(level, message string) {
|
||||
// 临时禁用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
LogWithLevel(level, message, false)
|
||||
}
|
||||
|
||||
// LogConsoleAndFile 同时输出到控制台和文件
|
||||
func LogConsoleAndFile(level, message string) {
|
||||
// 临时启用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
LogWithLevel(level, message, true)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# 接口详情
|
||||
## 创建日志器--CreateLogger
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CreateLogger(configJSON)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| configJSON | string | 是 | 配置信息JSON字符串 |
|
||||
#### 配置JSON结构
|
||||
```json
|
||||
{
|
||||
"log_dir": "/path/to/logs",
|
||||
"split_type": 0,
|
||||
"rotate_type": 0,
|
||||
"max_size": 104857600,
|
||||
"max_count": 30,
|
||||
"level": 1,
|
||||
"enable_caller": true,
|
||||
"default_task_type": "main"
|
||||
}
|
||||
```
|
||||
#### 参数说明:
|
||||
```text
|
||||
log_dir: 日志目录路径
|
||||
split_type: 分片方式(0=按月,1=按天,2=按小时,3=按分钟,4=按秒)
|
||||
rotate_type: 轮转方式(0=按大小,1=按数量)
|
||||
max_size: 最大文件大小(字节),仅在rotate_type=0时有效
|
||||
max_count: 最大文件数量,仅在rotate_type=1时有效
|
||||
level: 日志级别(0=SUCCESS,1=INFO,2=WARNING,3=ERROR)
|
||||
enable_caller: 是否启用调用者信息
|
||||
default_task_type: 默认任务类型
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"错误: 创建日志目录失败: permission denied"
|
||||
```
|
||||
|
||||
## 创建带任务类型的上下文--CreateContextWithTaskType
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CreateContextWithTaskType(loggerHandle, taskType)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
| taskType | string | 是 | 任务类型 |
|
||||
### 响应示例
|
||||
```json
|
||||
"ctx_1645497600000000000"
|
||||
```
|
||||
#### 错误响应示例
|
||||
```json
|
||||
"错误: 无效的logger句柄"
|
||||
```
|
||||
|
||||
## 记录信息日志--LogInfo
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogInfo(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录错误日志--LogError
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogError(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录警告日志--LogWarning
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogWarning(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录成功日志--LogSuccess
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogSuccess(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 获取日志条目--GetLogs
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetLogs(loggerHandle, configJSON)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
| configJSON | string | 是 | 查询配置JSON |
|
||||
#### 查询配置JSON结构
|
||||
```json
|
||||
{
|
||||
"level": 1,
|
||||
"task_type": "main",
|
||||
"start_time": "2024-01-01 00:00:00",
|
||||
"end_time": "2024-01-31 23:59:59",
|
||||
"max_entries": 1000
|
||||
}
|
||||
```
|
||||
#### 参数说明:
|
||||
```text
|
||||
level: 日志级别(-1表示所有级别)
|
||||
task_type: 任务类型(空字符串表示所有任务类型)
|
||||
start_time: 开始时间(格式: 2006-01-02 15:04:05)
|
||||
end_time: 结束时间(格式: 2006-01-02 15:04:05)
|
||||
max_entries: 最大返回条目数(0表示使用默认值1000)
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"count": 125,
|
||||
"entries": [
|
||||
{
|
||||
"timestamp": "2024-01-15 10:30:45.123",
|
||||
"level": "INFO",
|
||||
"task_type": "main",
|
||||
"caller": "logger.go:256",
|
||||
"message": "系统启动完成"
|
||||
},
|
||||
{
|
||||
"timestamp": "2024-01-15 10:31:15.456",
|
||||
"level": "ERROR",
|
||||
"task_type": "backup",
|
||||
"caller": "backup.go:89",
|
||||
"message": "备份文件失败: 磁盘空间不足"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{"error": "无效的logger句柄"}
|
||||
```
|
||||
|
||||
## 获取日志文件列表--GetLogFiles
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetLogFiles(loggerHandle)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"count": 8,
|
||||
"files": [
|
||||
{
|
||||
"level": "INFO",
|
||||
"task_type": "main",
|
||||
"file_name": "INFO-main-2024-01.logs",
|
||||
"file_size": 1048576,
|
||||
"mod_time": "2024-01-15 10:30:45"
|
||||
},
|
||||
{
|
||||
"level": "ERROR",
|
||||
"task_type": "backup",
|
||||
"file_name": "ERROR-backup-2024-01.logs",
|
||||
"file_size": 51200,
|
||||
"mod_time": "2024-01-15 10:31:15"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{"error": "无效的logger句柄"}
|
||||
```
|
||||
|
||||
## 获取版本信息--GetVersion
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetVersion()
|
||||
```
|
||||
### 请求参数
|
||||
```text
|
||||
无参数
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"v1"
|
||||
```
|
||||
|
||||
## 关闭所有日志器--CloseAllLoggers
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CloseAllLoggers()
|
||||
```
|
||||
### 请求参数
|
||||
```text
|
||||
无参数
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"成功关闭所有logger"
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
"关闭了5个logger,其中1个出错,最后错误: close file error"
|
||||
```
|
||||
|
||||
## 释放C字符串内存--FreeString
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.FreeString(str)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| str | string | 是 | 需要释放的字符串 |
|
||||
BIN
modules/pdd/pdd.dll
Normal file
BIN
modules/pdd/pdd.dll
Normal file
Binary file not shown.
223
modules/pdd/pdd.go
Normal file
223
modules/pdd/pdd.go
Normal file
@ -0,0 +1,223 @@
|
||||
package pdd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"planA/initialization/golabl"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gPddDll *PddDLL
|
||||
)
|
||||
|
||||
// PddResponse 定义完整的响应结构(包含成功和失败两种情况)
|
||||
type PddResponse struct {
|
||||
SuccessResponse *PddSuccessResponse `json:"outer_cat_mapping_get_response,omitempty"`
|
||||
ErrorResponse *PddErrorResponse `json:"error_response,omitempty"`
|
||||
}
|
||||
type PddSuccessResponse struct {
|
||||
OuterCatMappingGetResponse PddCategoryMappingResponse `json:"outer_cat_mapping_get_response"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// PddDLL 拼多多工具DLL结构
|
||||
type PddDLL struct {
|
||||
Dll *syscall.DLL
|
||||
pddGoodsOuterCatMappingGet *syscall.Proc // 类目预测
|
||||
freeCString *syscall.Proc // 释放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
|
||||
}
|
||||
|
||||
// InitPddDll 初始化 pddDLL
|
||||
func InitPddDll() (*PddDLL, error) {
|
||||
dllPath := filepath.Join(golabl.Config.FileUrl.PddDll, "pdd.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("pdd DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
|
||||
}
|
||||
gPddDll = &PddDLL{
|
||||
Dll: dll,
|
||||
pddGoodsOuterCatMappingGet: dll.MustFindProc("PddGoodsOuterCatMappingGet"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}
|
||||
return gPddDll, nil
|
||||
}
|
||||
|
||||
// PddGoodsOuterCatMappingGet 类目预测
|
||||
func (m *PddDLL) PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsOuterCatMappingGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsOuterCatMappingGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
outerCatIdPtr, _ := syscall.BytePtrFromString(outerCatId)
|
||||
outerCatNamePtr, _ := syscall.BytePtrFromString(outerCatName)
|
||||
outerGoodsNamePtr, _ := syscall.BytePtrFromString(outerGoodsName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatIdPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatNamePtr)),
|
||||
uintptr(unsafe.Pointer(outerGoodsNamePtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsAdd 商品新增
|
||||
func (m *PddDLL) PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddGoodsAdd")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsAdd: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsAddJsonPtr, _ := syscall.BytePtrFromString(goodsAddJson)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsAddJsonPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// PddGoodsSpecIdGet 生成商家自定义的规格
|
||||
func (m *PddDLL) PddGoodsSpecIdGet(clientId, clientSecret, accessToken, parentSpecId, specName string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsSpecIdGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsSpecIdGet: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
parentSpecIdPtr, _ := syscall.BytePtrFromString(parentSpecId)
|
||||
specNamePtr, _ := syscall.BytePtrFromString(specName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(parentSpecIdPtr)),
|
||||
uintptr(unsafe.Pointer(specNamePtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsCommitDetailGet 获取商品提交的商品详情
|
||||
func (m *PddDLL) PddGoodsCommitDetailGet(clientId, clientSecret, accessToken, goodsCommitId, goodsId string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsCommitDetailGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsCommitDetailGet: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsCommitIdPtr, _ := syscall.BytePtrFromString(goodsCommitId)
|
||||
goodsIdPtr, _ := syscall.BytePtrFromString(goodsId)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsCommitIdPtr)),
|
||||
uintptr(unsafe.Pointer(goodsIdPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddTimeGet 获取拼多多系统时间
|
||||
func (m *PddDLL) PddTimeGet(clientId, clientSecret, accessToken string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddTimeGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsCommitDetailGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsImageUpload 上传图片
|
||||
func (m *PddDLL) PddGoodsImageUpload(clientId, clientSecret, accessToken, imgBase64 string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsImageUpload")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsImageUpload: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
imgBase64Ptr, _ := syscall.BytePtrFromString(imgBase64)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(imgBase64Ptr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
863
modules/pdd/pdd.md
Normal file
863
modules/pdd/pdd.md
Normal file
@ -0,0 +1,863 @@
|
||||
# pdd.dll 使用教程
|
||||
## 1.创建DLL工具实例
|
||||
### 加载DLL文件
|
||||
```gotemplate
|
||||
// PddDLL 拼多多工具DLL结构
|
||||
type pddDLL struct {
|
||||
dll *syscall.DLL
|
||||
pddGoodsOuterCatMappingGet *syscall.Proc // 类目预测
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// <初始化pddDLL></初始化pddDLL>
|
||||
func InitPddDLL() (*pddDLL, error) {
|
||||
dllPath := filepath.Join("dll", "pdd.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("pdd DLL 不存在: %s", dllPath)
|
||||
}
|
||||
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
|
||||
} else {
|
||||
return &pddDLL{
|
||||
dll: dll,
|
||||
pddGoodsOuterCatMappingGet: dll.MustFindProc("PddGoodsOuterCatMappingGet"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
dll, err := InitPddDLL()
|
||||
```
|
||||
|
||||
### 获取C字符串
|
||||
```gotemplate
|
||||
// cStr 获取C字符串
|
||||
func (m *pddDLL) cStr(p uintptr) string {
|
||||
if p == 0 {
|
||||
return ""
|
||||
}
|
||||
b := []byte{}
|
||||
for i := uintptr(0); ; i++ {
|
||||
c := *(*byte)(unsafe.Pointer(p + i))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
}
|
||||
s := string(b)
|
||||
if m.freeCString != nil {
|
||||
m.freeCString.Call(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 使用dll函数示例
|
||||
```gotemplate
|
||||
// 类目预测
|
||||
func (m *pddDLL) PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||||
proc, err := m.dll.FindProc("PddGoodsOuterCatMappingGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsOuterCatMappingGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
outerCatIdPtr, _ := syscall.BytePtrFromString(outerCatId)
|
||||
outerCatNamePtr, _ := syscall.BytePtrFromString(outerCatName)
|
||||
outerGoodsNamePtr, _ := syscall.BytePtrFromString(outerGoodsName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatIdPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatNamePtr)),
|
||||
uintptr(unsafe.Pointer(outerGoodsNamePtr)),
|
||||
)
|
||||
|
||||
result := m.cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
```
|
||||
|
||||
# 接口详情
|
||||
## 1. 类目预测--PddGoodsOuterCatMappingGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| outerCatId | string | 是 | 外部平台类目ID |
|
||||
| outerCatName | string | 是 | 外部平台类目名称 |
|
||||
| outerGoodsName | string | 是 | 外部商品名称 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"outer_cat_mapping_get_response": {
|
||||
"cat_id2": 16028,
|
||||
"cat_id3": 16031,
|
||||
"cat_id1": 15543,
|
||||
"request_id": "17666480184871649",
|
||||
"cat_id4": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 快递公司查看--PddLogisticsCompaniesGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddLogisticsCompaniesGet(clientId, clientSecret)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"logistics_companies_get_response": {
|
||||
"logistics_companies": [
|
||||
{
|
||||
"available": 1,
|
||||
"code": "SF",
|
||||
"id": 1,
|
||||
"logistics_company": "顺丰速运"
|
||||
},
|
||||
{
|
||||
"available": 1,
|
||||
"code": "STO",
|
||||
"id": 2,
|
||||
"logistics_company": "申通快递"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. erp打单信息同步--PddErpOrderSync
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
||||
orderSn, orderState, waybillNo)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| logisticsId | string | 是 | 物流公司ID |
|
||||
| orderSn | string | 是 | 拼多多订单号 |
|
||||
| orderState | string | 是 | 订单状态 |
|
||||
| waybillNo | string | 是 | 运单号 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"erp_order_sync_response": {
|
||||
"is_success": true,
|
||||
"request_id": "17666480184871650"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 拼多多订单同步--PddOrderSynchronization
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| logisticsCompany | string | 是 | 物流公司名称 |
|
||||
| logisticsOnlineSendJson | string | 是 | 拼多多订单同步json字符串 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"erp_order_sync_response": {
|
||||
"is_success": true,
|
||||
"request_id": "17666480184871651"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 商品图片上传接口--PddGoodsImgUpload
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsImgUpload(clientId, clientSecret, accessToken, filePath)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| filePath | string | 是 | 图片文件路径 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_img_upload_response": {
|
||||
"image_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"request_id": "17666480184871652"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 商品新增接口--PddGoodsAdd
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| goodsAddJson | string | 是 | 商品信息JSON字符串 |
|
||||
#### 商品信息JSON结构示例
|
||||
```json
|
||||
{
|
||||
"goods_name": "测试商品",
|
||||
"goods_desc": "商品描述",
|
||||
"cat_id": 20111,
|
||||
"goods_type": 1,
|
||||
"market_price": 9900,
|
||||
"is_folt": false,
|
||||
"is_pre_sale": false,
|
||||
"is_refundable": true,
|
||||
"shipment_limit_second": 86400,
|
||||
"cost_template_id": 10001,
|
||||
"image_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"carousel_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"detail_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"sku_list": [
|
||||
{
|
||||
"out_sku_sn": "SKU001",
|
||||
"price": 8900,
|
||||
"quantity": 100,
|
||||
"spec_id_list": "1001:10001",
|
||||
"sku_properties": [
|
||||
{
|
||||
"ref_pid": 1001,
|
||||
"value": "红色",
|
||||
"vid": 10001,
|
||||
"punit": "个"
|
||||
}
|
||||
],
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 10,
|
||||
"multi_price": 8500,
|
||||
"thumb_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"weight": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_add_response": {
|
||||
"goods_id": 123456789,
|
||||
"goods_name": "测试商品",
|
||||
"goods_sn": "G202501200001",
|
||||
"request_id": "17666480184871653"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 联合拼多多图片上传的商品新增--SelfPddGoodsAdd
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.SelfPddGoodsAdd(clientId, clientSecret, accessToken, filePath, goodsAddJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| filePath | string | 是 | 图片文件路径 |
|
||||
| goodsAddJson | string | 是 | 商品信息JSON字符串(不需包含image_url)|
|
||||
#### 接口说明
|
||||
此接口为组合接口,内部执行以下步骤:
|
||||
1.上传商品主图文件到拼多多服务器
|
||||
2.获取图片URL并自动填充到商品信息中
|
||||
3.调用商品新增接口创建商品
|
||||
#### 商品信息JSON结构示例
|
||||
```json
|
||||
{
|
||||
"goods_name": "测试商品",
|
||||
"goods_desc": "商品描述",
|
||||
"cat_id": 20111,
|
||||
"goods_type": 1,
|
||||
"market_price": 9900,
|
||||
"is_folt": false,
|
||||
"is_pre_sale": false,
|
||||
"is_refundable": true,
|
||||
"shipment_limit_second": 86400,
|
||||
"cost_template_id": 10001,
|
||||
"image_url": "",
|
||||
"carousel_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"detail_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"sku_list": [
|
||||
{
|
||||
"out_sku_sn": "SKU001",
|
||||
"price": 8900,
|
||||
"quantity": 100,
|
||||
"spec_id_list": "1001:10001",
|
||||
"sku_properties": [
|
||||
{
|
||||
"ref_pid": 1001,
|
||||
"value": "红色",
|
||||
"vid": 10001,
|
||||
"punit": "个"
|
||||
}
|
||||
],
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 10,
|
||||
"multi_price": 8500,
|
||||
"thumb_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"weight": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_add_response": {
|
||||
"goods_id": 123456790,
|
||||
"goods_name": "测试商品",
|
||||
"goods_sn": "G202501200002",
|
||||
"request_id": "17666480184871654"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 批量数据解密脱敏接口--PddOpenDecryptMaskBatch
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, reqJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| reqJson | string | 是 | 信息JSON字符串 |
|
||||
#### 信息JSON结构示例
|
||||
```json
|
||||
[
|
||||
{
|
||||
"data_tag": "251229-272441044622514",
|
||||
"encrypted_data": "~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"
|
||||
}
|
||||
]
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"open_decrypt_mask_batch_response": {
|
||||
"data_decrypt_list": [
|
||||
{
|
||||
"data_tag": "str",
|
||||
"data_type": 0,
|
||||
"decrypted_data": "str",
|
||||
"encrypted_data": "str",
|
||||
"error_code": 0,
|
||||
"error_msg": "str"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 生成商家自定义的规格--PddGoodsSpecIdGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsSpecIdGet(clientId, clientSecret, accessToken, parentSpecId, specName)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| parentSpecId | string | 是 | 拼多多标准规格ID |
|
||||
| specName | string | 是 | 商家编辑的规格值,如颜色规格下设置白色属性 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_spec_id_get_response": {
|
||||
"parent_spec_id": 0,
|
||||
"spec_id": 0,
|
||||
"spec_name": "str"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 修改商品SKU价格--PddGoodsSkuPriceUpdate
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsSkuPriceUpdate(clientId, clientSecret, accessToken, request)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| request | string | 是 | 价格更新请求JSON字符串 |
|
||||
#### 请求JSON结构
|
||||
```json
|
||||
{
|
||||
"goods_id": "必填,商品id,类型为LONG",
|
||||
"ignore_edit_warn": "非必填,是否获取商品发布警告信息,默认为忽略,类型为BOOLEAN",
|
||||
"market_price": "非必填,参考价(单位分),类型为LONG",
|
||||
"market_price_in_yuan": "非必填,参考价(单位元),类型为STRING",
|
||||
"sku_price_list": [
|
||||
{
|
||||
"group_price": "非必填,拼团购买价格(单位分),类型为LONG",
|
||||
"is_onsale": "非必填,sku上架状态,0-已下架,1-上架中,类型为INTEGER",
|
||||
"single_price": "非必填,单独购买价格(单位分),类型为LONG",
|
||||
"sku_id": "必填,sku标识,类型为LONG"
|
||||
}
|
||||
],
|
||||
"sync_goods_operate": "非必填,提交后上架状态,0:上架,1:保持原样,类型为INTEGER",
|
||||
"two_pieces_discount": "非必填,满2件折扣,可选范围0-100,0表示取消,95表示95折,设置需先查询规则接口获取实际可填范围,类型为INTEGER"
|
||||
}
|
||||
```
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_update_sku_price_response": {
|
||||
"goods_commit_id": 0,
|
||||
"is_success": true
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 商品库存更新接口--PddGoodsQuantityUpdate
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsQuantityUpdate(clientId, clientSecret, accessToken, request)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|---------------------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| request | string | 是 | 库存更新请求JSON字符串 |
|
||||
#### 请求JSON结构 request 字符串
|
||||
```json
|
||||
{
|
||||
"force_update": "非必填,是否强制更新,仅update_type=1(全量更新)时有效,默认值false;force_update=false时,quantity不能小于预扣库存;force_update=true时,代表强制更新,当quantity<预扣库存时,不报错,直接将quantity清0,类型为BOOLEAN",
|
||||
"goods_id": "必填,商品id,类型为LONG",
|
||||
"outer_id": "非必填,sku商家编码,类型为STRING",
|
||||
"quantity": "必填,库存修改值。当全量更新库存时,quantity必须为大于等于0的正整数;当增量更新库存时,quantity为整数,可小于等于0。若增量更新时传入的库存为负数,则负数与实际库存之和不能小于0。比如当前实际库存为1,传入增量更新quantity=-1,库存改为0,类型为LONG",
|
||||
"sku_id": "非必填,sku_id和outer_id必填一个,类型为LONG",
|
||||
"update_type": "非必填,库存更新方式,可选。1为全量更新,2为增量更新。如果不填,默认为全量更新,类型为INTEGER"
|
||||
}
|
||||
```
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_quantity_update_response": {
|
||||
"is_success": false
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 获取商品信息接口 -- OutPddAuthGetCommitDetailt
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthGetCommitDetailt(goodsCommitId, goodsId, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| goodsCommitId | string | 是 | 商品提交ID |
|
||||
| goodsId | string | 是 | 商品ID |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 获取商品详情信息接口 -- OutPddAuthGetGoodsDetail
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthGetGoodsDetail(goodsId, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| goodsId | string | 是 | 商品ID |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"bad_fruit_claim": 0,
|
||||
"buy_limit": 999999,
|
||||
"carousel_gallery_list": [
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/4539f740-331b-4687-aa00-5c96855de6cd.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/b0e89e39-c97b-475d-9be2-f1909e30acb5.jpeg"
|
||||
],
|
||||
"cat_id": 15678,
|
||||
"cost_template_id": 655688447565777,
|
||||
"country_id": 0,
|
||||
"customer_num": 2,
|
||||
"customs": "",
|
||||
"detail_gallery_list": [
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/b691c104-baf8-42b2-97e2-b7258113114b.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/53e6f7ff-d15e-4e8f-8625-e293717ca1e4.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/ecff591d-32a6-42c9-ba5a-6a42829092a8.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/7034f8a0-5d88-49f8-a96f-608abb8cac80.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/e10c2b6c-d4de-4fdd-8d48-f0a334735e9a.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/c19358fb-0a4d-49ad-bcc8-b2980e938064.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/1deeb9c0-7212-432b-a309-f774db6e1adb.jpeg"
|
||||
],
|
||||
"goods_desc": "书名:金属工艺学 下 第6版,作者:'邓文英,宋力宏主编',ISBN:9787040456295,出版社:高等教育出版社",
|
||||
"goods_id": 770621582375,
|
||||
"goods_name": "金属工艺学 下 第6版 邓文英,宋力宏主编 高等教育出版社 978",
|
||||
"goods_property_list": [
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 425,
|
||||
"template_pid": 401030,
|
||||
"vid": 0,
|
||||
"vvalue": "9787040456295"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 876,
|
||||
"template_pid": 401029,
|
||||
"vid": 0,
|
||||
"vvalue": "金属工艺学 下 第6版"
|
||||
},
|
||||
{
|
||||
"punit": "页",
|
||||
"ref_pid": 692,
|
||||
"template_pid": 401032,
|
||||
"vid": 0,
|
||||
"vvalue": "157"
|
||||
},
|
||||
{
|
||||
"punit": "元",
|
||||
"ref_pid": 879,
|
||||
"template_pid": 401034,
|
||||
"vid": 0,
|
||||
"vvalue": "24.70"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 882,
|
||||
"template_pid": 401037,
|
||||
"vid": 0,
|
||||
"vvalue": "邓文英,宋力宏主编"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 880,
|
||||
"template_pid": 401035,
|
||||
"vid": 483761,
|
||||
"vvalue": "高等教育出版社"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 888,
|
||||
"template_pid": 401043,
|
||||
"vid": 0,
|
||||
"vvalue": "平装"
|
||||
}
|
||||
],
|
||||
"goods_type": 1,
|
||||
"image_url": "",
|
||||
"invoice_status": 0,
|
||||
"is_customs": 0,
|
||||
"is_folt": 0,
|
||||
"is_group_pre_sale": 0,
|
||||
"is_pre_sale": 0,
|
||||
"is_refundable": 1,
|
||||
"is_sku_pre_sale": 0,
|
||||
"market_price": 5948,
|
||||
"order_limit": 999999,
|
||||
"outer_goods_id": "9787040456295",
|
||||
"oversea_type": 0,
|
||||
"pre_sale_time": 0,
|
||||
"privacy_delivery": 0,
|
||||
"quan_guo_lian_bao": 0,
|
||||
"second_hand": 1,
|
||||
"shipment_limit_second": 172800,
|
||||
"sku_list": [
|
||||
{
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 999999,
|
||||
"multi_price": 1487,
|
||||
"out_sku_sn": "9787040456295",
|
||||
"price": 1587,
|
||||
"quantity": 0,
|
||||
"reserve_quantity": 0,
|
||||
"sku_id": 1753931570290,
|
||||
"sku_pre_sale_time": 0,
|
||||
"spec": [
|
||||
{
|
||||
"parent_id": 1216,
|
||||
"parent_name": "尺寸",
|
||||
"spec_id": 27632894279,
|
||||
"spec_name": "单本 无附赠 超七天不退换"
|
||||
}
|
||||
],
|
||||
"thumb_url": "https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
|
||||
"weight": 500
|
||||
}
|
||||
],
|
||||
"status": 4,
|
||||
"tiny_name": "金属工艺学 下 第6",
|
||||
"two_pieces_discount": 96,
|
||||
"video_gallery": [],
|
||||
"warehouse": "",
|
||||
"warm_tips": "",
|
||||
"zhi_huan_bu_xiu": 0
|
||||
}
|
||||
```
|
||||
|
||||
## 生成自定义规格接口 -- OutPddAuthSetSpec
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthSetSpec(specTypeId, specName, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| specTypeId | int | 是 | 规格类型ID |
|
||||
| specName | string | 是 | 规格名称 |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"parentSpecId": 3820,
|
||||
"specName": "全新",
|
||||
"specId": 1080396526
|
||||
}
|
||||
```
|
||||
|
||||
## 修改价格接口 -- OutPddAuthUpdatePrice
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthUpdatePrice(jsonData)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------|
|
||||
| jsonData | int | 是 | 价格修改信息JSON字符串 |
|
||||
### 响应参数
|
||||
```json
|
||||
[
|
||||
{
|
||||
"success": true,
|
||||
"msg": "操作成功"
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"msg": "操作失败"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 修改库存接口 -- OutPddAuthUpdateStock
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthUpdateStock(jsonData)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------|
|
||||
| jsonData | int | 是 | 价格修改信息JSON字符串 |
|
||||
### 响应参数
|
||||
```json
|
||||
[
|
||||
{
|
||||
"success": true,
|
||||
"msg": "操作成功"
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"msg": "操作失败"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 12.释放C字符串内存--FreeCString
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.FreeCString(str)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| str | string | 是 | 需要释放的字符串 |
|
||||
BIN
modules/xianYu/address.xlsx
Normal file
BIN
modules/xianYu/address.xlsx
Normal file
Binary file not shown.
25
modules/xianYu/config.ini
Normal file
25
modules/xianYu/config.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[app]
|
||||
AppId = 1228288260261189
|
||||
AppSecret = aq9gAwrwp6WGZkMRqKIXmnu2c2uCm82k
|
||||
Domain = https://open.goofish.pro
|
||||
[http]
|
||||
Addr = 127.0.0.1:53368
|
||||
[categoryListRequest]
|
||||
Path = /api/open/product/category/list
|
||||
ItemBizType: 2
|
||||
SpBizType: 24
|
||||
[batchCreatRequest]
|
||||
Path = /api/open/product/batchCreate
|
||||
[file]
|
||||
TxtPath = modules/xianYu/productCategory.txt
|
||||
ExcelPath = modules/xianYu/address.xlsx
|
||||
SheetName = Result
|
||||
[redis]
|
||||
Password = Long6166@@
|
||||
Addr = 127.0.0.1:6379
|
||||
Db = 5
|
||||
[tokenBucket]
|
||||
BucketKeyPrefix = "token_bucket_"
|
||||
TokenPerSecond = 10
|
||||
BucketSize = 100
|
||||
Delay = 100
|
||||
284
modules/xianYu/productCategory.txt
Normal file
284
modules/xianYu/productCategory.txt
Normal file
@ -0,0 +1,284 @@
|
||||
cbf4e2ec8f2013d31921b9e373cead75:电视剧
|
||||
cbf4e2ec8f2013d3267e0a01017d9f44:电影
|
||||
cbf4e2ec8f2013d36f38848189966e7d:生活
|
||||
cbf4e2ec8f2013d3ac899d2620c5df2b:成人教育音像
|
||||
cbf4e2ec8f2013d3acde29f76907b07f:动画
|
||||
cbf4e2ec8f2013d3e10cfa39bf43dc0f:儿童教育音像
|
||||
d14d229692616168b108d382c4e6ea42:废品回收
|
||||
d816d18aa66dfb3d1921b9e373cead75:励志成长
|
||||
dbaba36adf47af96b108d382c4e6ea42:不干胶标签
|
||||
e59460ef9961e2bd28d88a08a19453dc:古典吉他
|
||||
e59460ef9961e2bda7f7e02f36b0b49a:电箱吉他
|
||||
86cddebb2de0815c1921b9e373cead75:桌面文件柜
|
||||
86cddebb2de0815c267e0a01017d9f44:资料册
|
||||
86cddebb2de0815c6f38848189966e7d:镇纸
|
||||
86cddebb2de0815ca7f7e02f36b0b49a:文件袋
|
||||
86cddebb2de0815cacde29f76907b07f:文房墨汁
|
||||
86cddebb2de0815ce10cfa39bf43dc0f:文房四宝套装
|
||||
879b743300e7a58137b3d33c282f2081:古筝
|
||||
8bd8d9724880b84d28d88a08a19453dc:学习笔记
|
||||
a457d6fc43c609bdac899d2620c5df2b:单据收据
|
||||
a457d6fc43c609bdacde29f76907b07f:印台
|
||||
a9ef3505c7fe4b661921b9e373cead75:勾线笔
|
||||
a9ef3505c7fe4b66a7f7e02f36b0b49a:电子阅览器/电纸书
|
||||
ab78823bfd3c7134b108d382c4e6ea42:经济管理
|
||||
ac69f9982deabde1acde29f76907b07f:民谣吉他
|
||||
ac69f9982deabde1e10cfa39bf43dc0f:架子鼓
|
||||
b12c1c13a8dc3b2b6f38848189966e7d:POP广告纸
|
||||
b12c1c13a8dc3b2ba7f7e02f36b0b49a:修正贴
|
||||
b12c1c13a8dc3b2bac899d2620c5df2b:学生用印
|
||||
b12c1c13a8dc3b2bacde29f76907b07f:名片
|
||||
b2b61c32fc4c904428d88a08a19453dc:背胶证件照
|
||||
b3b713b29220947237b3d33c282f2081:台历
|
||||
4c49139fe1b6ae4aac899d2620c5df2b:童书育儿
|
||||
4fecb084c468ed626f38848189966e7d:黑板
|
||||
5042edcbd2cc4b94ac899d2620c5df2b:生活百科
|
||||
621bd460d751e0fc37b3d33c282f2081:订书机
|
||||
701ed8603d74ee60b108d382c4e6ea42:报纸
|
||||
722d38201b9c8cba267e0a01017d9f44:社科心理
|
||||
7912befd7e1215d11921b9e373cead75:挂历
|
||||
7dba397e41d08d4937b3d33c282f2081:拆信刀
|
||||
7eb776b01814cc6e1921b9e373cead75:教材教辅
|
||||
22e1d81dc4cf3a25a7f7e02f36b0b49a:图书
|
||||
2dfa3034d88aedcc1921b9e373cead75:期刊/杂志
|
||||
31329c43789fae0437b3d33c282f2081:戏曲综艺
|
||||
31329c43789fae04a7f7e02f36b0b49a:音乐唱片/专辑
|
||||
322a73805c38995f6f38848189966e7d:宝珠笔
|
||||
3cdbae6d47df9251a7f7e02f36b0b49a:电子资料
|
||||
22d3cfff678abab1e10cfa39bf43dc0f:握笔器
|
||||
b7fd03d456abe3011921b9e373cead75:活页替芯
|
||||
b7fd03d456abe301b108d382c4e6ea42:索引纸
|
||||
b7fd03d456abe301e10cfa39bf43dc0f:拍纸本
|
||||
c230ba4ca293f3b528d88a08a19453dc:马克笔
|
||||
c230ba4ca293f3b5a7f7e02f36b0b49a:钢笔
|
||||
c230ba4ca293f3b5ac899d2620c5df2b:铅笔
|
||||
c3c6e8d1d63c0618b108d382c4e6ea42:文学/小说
|
||||
c58d3dbcff05e404acde29f76907b07f:笔筒
|
||||
eac1d67ece5fa9b16f38848189966e7d:钢琴
|
||||
ee8603696d446e931921b9e373cead75:电钢琴
|
||||
06d80b131d7b0b616f38848189966e7d:毛笔
|
||||
0e28c0f1f1e57eb1ac899d2620c5df2b:地图
|
||||
0f75076039b85f74267e0a01017d9f44:计算器
|
||||
0f75076039b85f7428d88a08a19453dc:尺
|
||||
0f75076039b85f746f38848189966e7d:板擦
|
||||
0f75076039b85f74b108d382c4e6ea42:算盘
|
||||
11c38799bd389b3828d88a08a19453dc:漫画书籍
|
||||
ac69f9982deabde1a7f7e02f36b0b49a:上弦器
|
||||
83f9286d1ea41056ac899d2620c5df2b:其他吉他配件
|
||||
e59460ef9961e2bd1921b9e373cead75:变调夹
|
||||
83f9286d1ea4105637b3d33c282f2081:古典吉他弦
|
||||
e59460ef9961e2bdacde29f76907b07f:吉他单块效果器
|
||||
83f9286d1ea41056267e0a01017d9f44:吉他效果器配件
|
||||
83f9286d1ea41056b108d382c4e6ea42:吉他电源
|
||||
ac69f9982deabde1267e0a01017d9f44:吉他综合效果器
|
||||
e59460ef9961e2bdb108d382c4e6ea42:吉他背包琴盒
|
||||
83f9286d1ea4105628d88a08a19453dc:吉他背带
|
||||
83f9286d1ea410561921b9e373cead75:吉他连接线
|
||||
e59460ef9961e2bd6f38848189966e7d:吊架
|
||||
ac69f9982deabde1ac899d2620c5df2b:弦枕
|
||||
ac69f9982deabde11921b9e373cead75:弦柱
|
||||
e59460ef9961e2bd267e0a01017d9f44:拨片
|
||||
ac69f9982deabde128d88a08a19453dc:拾音器
|
||||
83f9286d1ea41056e10cfa39bf43dc0f:曼陀铃弦
|
||||
83f9286d1ea410566f38848189966e7d:民谣吉他弦
|
||||
ac69f9982deabde137b3d33c282f2081:清洁保护品
|
||||
83f9286d1ea41056a7f7e02f36b0b49a:滑棒指套
|
||||
e59460ef9961e2bde10cfa39bf43dc0f:电吉他弦
|
||||
e59460ef9961e2bdac899d2620c5df2b:背带钮
|
||||
83f9286d1ea41056acde29f76907b07f:脚凳
|
||||
ac69f9982deabde1b108d382c4e6ea42:调音器
|
||||
e59460ef9961e2bd37b3d33c282f2081:电吉他
|
||||
c6d5c9e68467b108ac899d2620c5df2b:哑鼓垫
|
||||
c6d5c9e68467b10837b3d33c282f2081:镲片
|
||||
c6d5c9e68467b10828d88a08a19453dc:鼓凳
|
||||
ac69f9982deabde16f38848189966e7d:鼓刷
|
||||
c6d5c9e68467b108b108d382c4e6ea42:鼓架镲架
|
||||
c6d5c9e68467b108a7f7e02f36b0b49a:鼓棒鼓锤
|
||||
1cac27c660d7b098b108d382c4e6ea42:唢呐
|
||||
1cac27c660d7b098267e0a01017d9f44:埙
|
||||
f22578f0c6a8eaa5267e0a01017d9f44:尺八
|
||||
f22578f0c6a8eaa51921b9e373cead75:巴乌
|
||||
1cac27c660d7b09828d88a08a19453dc:笙
|
||||
f22578f0c6a8eaa5acde29f76907b07f:笛子
|
||||
f22578f0c6a8eaa5e10cfa39bf43dc0f:管子
|
||||
1cac27c660d7b0981921b9e373cead75:箫
|
||||
1cac27c660d7b098a7f7e02f36b0b49a:芦笙
|
||||
1cac27c660d7b09837b3d33c282f2081:葫芦丝
|
||||
f22578f0c6a8eaa56f38848189966e7d:葫芦笙
|
||||
1cac27c660d7b098ac899d2620c5df2b:陶笛
|
||||
879b743300e7a581acde29f76907b07f:三弦
|
||||
1cac27c660d7b098e10cfa39bf43dc0f:冬不拉
|
||||
1cac27c660d7b0986f38848189966e7d:古琴
|
||||
1cac27c660d7b098acde29f76907b07f:弹布尔
|
||||
879b743300e7a581e10cfa39bf43dc0f:扬琴
|
||||
879b743300e7a5816f38848189966e7d:月琴
|
||||
879b743300e7a58128d88a08a19453dc:柳琴
|
||||
879b743300e7a5811921b9e373cead75:热瓦普
|
||||
879b743300e7a581b108d382c4e6ea42:琵琶
|
||||
879b743300e7a581ac899d2620c5df2b:秦琴
|
||||
879b743300e7a581a7f7e02f36b0b49a:箜篌
|
||||
879b743300e7a581267e0a01017d9f44:阮
|
||||
a2eba09f5b889a7c28d88a08a19453dc:中胡
|
||||
7d61e938542f6790b108d382c4e6ea42:二胡
|
||||
7d61e938542f6790267e0a01017d9f44:京二胡
|
||||
7d61e938542f6790acde29f76907b07f:京胡
|
||||
7d61e938542f679028d88a08a19453dc:低音胡
|
||||
a2eba09f5b889a7c37b3d33c282f2081:四胡
|
||||
a2eba09f5b889a7cb108d382c4e6ea42:坠琴
|
||||
7d61e938542f6790a7f7e02f36b0b49a:板胡
|
||||
a2eba09f5b889a7ca7f7e02f36b0b49a:椰胡
|
||||
7d61e938542f679037b3d33c282f2081:艾捷克
|
||||
7d61e938542f67901921b9e373cead75:革胡
|
||||
7d61e938542f67906f38848189966e7d:马头琴
|
||||
7d61e938542f6790e10cfa39bf43dc0f:马骨胡
|
||||
7d61e938542f6790ac899d2620c5df2b:高胡
|
||||
882b39ff0db2dd0037b3d33c282f2081:军镲
|
||||
00a32e7ff35aaf9e267e0a01017d9f44:大钹
|
||||
00a32e7ff35aaf9ee10cfa39bf43dc0f:大铙
|
||||
00a32e7ff35aaf9eacde29f76907b07f:大顶钹
|
||||
00a32e7ff35aaf9e1921b9e373cead75:川钹
|
||||
00a32e7ff35aaf9e6f38848189966e7d:广钹
|
||||
882b39ff0db2dd00a7f7e02f36b0b49a:快板
|
||||
882b39ff0db2dd0028d88a08a19453dc:拍板
|
||||
00a32e7ff35aaf9eb108d382c4e6ea42:梆子
|
||||
882b39ff0db2dd001921b9e373cead75:水镲
|
||||
882b39ff0db2dd00b108d382c4e6ea42:碰钟
|
||||
882b39ff0db2dd00acde29f76907b07f:秧歌镲
|
||||
882b39ff0db2dd00e10cfa39bf43dc0f:腰鼓镲
|
||||
882b39ff0db2dd00ac899d2620c5df2b:萨巴依
|
||||
882b39ff0db2dd00267e0a01017d9f44:铜书板
|
||||
00a32e7ff35aaf9eac899d2620c5df2b:镲锅
|
||||
0ea61a801ba323c1267e0a01017d9f44:堂鼓
|
||||
00a32e7ff35aaf9e28d88a08a19453dc:战鼓
|
||||
0ea61a801ba323c11921b9e373cead75:排鼓
|
||||
0ea61a801ba323c1b108d382c4e6ea42:板鼓
|
||||
00a32e7ff35aaf9e37b3d33c282f2081:秧歌鼓
|
||||
0ea61a801ba323c1e10cfa39bf43dc0f:细腰鼓
|
||||
00a32e7ff35aaf9ea7f7e02f36b0b49a:腰鼓
|
||||
0ea61a801ba323c1ac899d2620c5df2b:花盆鼓
|
||||
0ea61a801ba323c16f38848189966e7d:象脚鼓
|
||||
0ea61a801ba323c1acde29f76907b07f:铜鼓
|
||||
a7133eb411b587cf1921b9e373cead75:空灵鼓/无忧鼓
|
||||
0ea61a801ba323c1a7f7e02f36b0b49a:云锣
|
||||
a2eba09f5b889a7c267e0a01017d9f44:京锣
|
||||
a2eba09f5b889a7cac899d2620c5df2b:低音锣
|
||||
a2eba09f5b889a7cacde29f76907b07f:开道锣
|
||||
a2eba09f5b889a7ce10cfa39bf43dc0f:手锣
|
||||
0ea61a801ba323c137b3d33c282f2081:武锣
|
||||
0ea61a801ba323c128d88a08a19453dc:舟山锣
|
||||
a2eba09f5b889a7c6f38848189966e7d:苏锣
|
||||
a2eba09f5b889a7c1921b9e373cead75:虎音锣
|
||||
33a0daa5d89d68fa1921b9e373cead75:宣纸
|
||||
b12c1c13a8dc3b2b1921b9e373cead75:吊牌
|
||||
b12c1c13a8dc3b2be10cfa39bf43dc0f:自封袋
|
||||
b12c1c13a8dc3b2b267e0a01017d9f44:贺卡明信片
|
||||
22d3cfff678abab11921b9e373cead75:书皮
|
||||
b12c1c13a8dc3b2b37b3d33c282f2081:修正带
|
||||
b12c1c13a8dc3b2b28d88a08a19453dc:修正液
|
||||
22d3cfff678abab1a7f7e02f36b0b49a:削笔器
|
||||
22d3cfff678abab128d88a08a19453dc:可爱印泥
|
||||
b12c1c13a8dc3b2bb108d382c4e6ea42:学生书包
|
||||
22d3cfff678abab1acde29f76907b07f:文具套装
|
||||
22d3cfff678abab1267e0a01017d9f44:文具盒
|
||||
22d3cfff678abab16f38848189966e7d:橡皮
|
||||
22d3cfff678abab1b108d382c4e6ea42:练字帖
|
||||
22d3cfff678abab1ac899d2620c5df2b:视力保护器
|
||||
dbaba36adf47af9637b3d33c282f2081:笔袋
|
||||
54e552aa1c9b2cbcacde29f76907b07f:彩泥橡皮泥
|
||||
bf164bd2e8dd8cebb108d382c4e6ea42:便条照片夹
|
||||
bf164bd2e8dd8ceb28d88a08a19453dc:便签盒座
|
||||
bf164bd2e8dd8cebe10cfa39bf43dc0f:卡套证件套
|
||||
bf164bd2e8dd8ceb6f38848189966e7d:名片册
|
||||
86cddebb2de0815c37b3d33c282f2081:名片盒
|
||||
bf164bd2e8dd8cebacde29f76907b07f:快劳夹
|
||||
86cddebb2de0815c28d88a08a19453dc:文件夹
|
||||
86cddebb2de0815cb108d382c4e6ea42:文件架
|
||||
bf164bd2e8dd8ceb1921b9e373cead75:档案盒
|
||||
bf164bd2e8dd8cebac899d2620c5df2b:档案袋
|
||||
86cddebb2de0815cac899d2620c5df2b:相册
|
||||
1ad9ac4511bbb8646f38848189966e7d:笔插
|
||||
bf164bd2e8dd8ceba7f7e02f36b0b49a:笔架
|
||||
bf164bd2e8dd8ceb267e0a01017d9f44:风琴包
|
||||
d665d5e1347fa192a7f7e02f36b0b49a:地球仪
|
||||
bf164bd2e8dd8ceb37b3d33c282f2081:展板
|
||||
d665d5e1347fa192267e0a01017d9f44:教学仪器器材
|
||||
d665d5e1347fa1921921b9e373cead75:教鞭
|
||||
bb9bba251ee78e59267e0a01017d9f44:旗帜
|
||||
d665d5e1347fa19237b3d33c282f2081:提示牌
|
||||
d665d5e1347fa192b108d382c4e6ea42:激光笔
|
||||
0f75076039b85f74acde29f76907b07f:白板
|
||||
0f75076039b85f74e10cfa39bf43dc0f:白板笔
|
||||
d665d5e1347fa19228d88a08a19453dc:粉笔
|
||||
d665d5e1347fa192acde29f76907b07f:绿板
|
||||
d665d5e1347fa1926f38848189966e7d:荧光板
|
||||
d665d5e1347fa192ac899d2620c5df2b:计划表
|
||||
d665d5e1347fa192e10cfa39bf43dc0f:软木板
|
||||
a457d6fc43c609bda7f7e02f36b0b49a:中性笔
|
||||
c230ba4ca293f3b5e10cfa39bf43dc0f:圆珠笔
|
||||
c230ba4ca293f3b51921b9e373cead75:铅芯
|
||||
f9910185f1984f2937b3d33c282f2081:正姿笔
|
||||
c230ba4ca293f3b5acde29f76907b07f:油漆笔
|
||||
c230ba4ca293f3b5b108d382c4e6ea42:泡泡笔
|
||||
c230ba4ca293f3b537b3d33c282f2081:墨水墨囊
|
||||
c230ba4ca293f3b5267e0a01017d9f44:荧光笔
|
||||
f4a071d4dba28eccac899d2620c5df2b:记号笔
|
||||
c230ba4ca293f3b56f38848189966e7d:针管笔
|
||||
dfdbd3409fadcd3f6f38848189966e7d:其他笔
|
||||
58e84885c426409e267e0a01017d9f44:书签
|
||||
b7fd03d456abe30128d88a08a19453dc:便签
|
||||
e9fa1ad466b79d97b108d382c4e6ea42:信封
|
||||
af2cf5b1faa3537a1921b9e373cead75:信纸
|
||||
b7fd03d456abe30137b3d33c282f2081:包装纸
|
||||
e9fa1ad466b79d97a7f7e02f36b0b49a:纪念册
|
||||
b7fd03d456abe301ac899d2620c5df2b:复写纸
|
||||
b7fd03d456abe301267e0a01017d9f44:奖状证书
|
||||
e9fa1ad466b79d971921b9e373cead75:手工纸
|
||||
e9fa1ad466b79d9728d88a08a19453dc:草稿纸
|
||||
b7fd03d456abe3016f38848189966e7d:日记本
|
||||
e9fa1ad466b79d97ac899d2620c5df2b:硬面抄
|
||||
b7fd03d456abe301a7f7e02f36b0b49a:记事本
|
||||
b7fd03d456abe301acde29f76907b07f:课业本
|
||||
e9fa1ad466b79d9737b3d33c282f2081:通讯录
|
||||
dbaba36adf47af966f38848189966e7d:磁性贴
|
||||
6c0543ec11db7e61267e0a01017d9f44:贴纸/标签
|
||||
0f75076039b85f741921b9e373cead75:圆规
|
||||
0f75076039b85f74ac899d2620c5df2b:显微镜
|
||||
1c75d8021bacf61e267e0a01017d9f44:放大镜
|
||||
a9ef3505c7fe4b66b108d382c4e6ea42:丙烯颜料
|
||||
823f8d7bd96780d0ac899d2620c5df2b:书法用纸
|
||||
a9ef3505c7fe4b66ac899d2620c5df2b:儿童填色本
|
||||
a9ef3505c7fe4b66267e0a01017d9f44:国画颜料
|
||||
823f8d7bd96780d037b3d33c282f2081:描图硫酸纸
|
||||
5fd3299edc3ff44a37b3d33c282f2081:毛边纸
|
||||
823f8d7bd96780d01921b9e373cead75:水彩笔
|
||||
823f8d7bd96780d0267e0a01017d9f44:水彩颜料
|
||||
823f8d7bd96780d0acde29f76907b07f:水粉水彩油画笔
|
||||
823f8d7bd96780d0e10cfa39bf43dc0f:水粉颜料
|
||||
0f75076039b85f7437b3d33c282f2081:油画棒
|
||||
0f75076039b85f74a7f7e02f36b0b49a:油画颜料
|
||||
a9ef3505c7fe4b66acde29f76907b07f:画板画架
|
||||
823f8d7bd96780d0b108d382c4e6ea42:石膏像
|
||||
823f8d7bd96780d06f38848189966e7d:素描本
|
||||
a9ef3505c7fe4b66e10cfa39bf43dc0f:绘图纸
|
||||
823f8d7bd96780d028d88a08a19453dc:色卡
|
||||
a9ef3505c7fe4b666f38848189966e7d:蜡笔
|
||||
823f8d7bd96780d0a7f7e02f36b0b49a:铅画纸
|
||||
7dba397e41d08d49a7f7e02f36b0b49a:裁剪刀片
|
||||
7dba397e41d08d49b108d382c4e6ea42:雕刻垫板
|
||||
7dba397e41d08d49ac899d2620c5df2b:切纸刀
|
||||
7dba397e41d08d4928d88a08a19453dc:美工刀
|
||||
356e5d8126d3aefaa7f7e02f36b0b49a:裁剪剪刀
|
||||
e9fa1ad466b79d97267e0a01017d9f44:回形针
|
||||
621bd460d751e0fca7f7e02f36b0b49a:回形针盒
|
||||
621bd460d751e0fcb108d382c4e6ea42:图钉工字钉
|
||||
e9fa1ad466b79d97e10cfa39bf43dc0f:大头针
|
||||
e9fa1ad466b79d97acde29f76907b07f:打孔机
|
||||
621bd460d751e0fc28d88a08a19453dc:票夹长尾夹
|
||||
e9fa1ad466b79d976f38848189966e7d:订书钉
|
||||
a457d6fc43c609bd1921b9e373cead75:凭证
|
||||
a457d6fc43c609bde10cfa39bf43dc0f:印油印泥
|
||||
a457d6fc43c609bd28d88a08a19453dc:报表
|
||||
a457d6fc43c609bd267e0a01017d9f44:湿手器
|
||||
a457d6fc43c609bdb108d382c4e6ea42:财务证明用品
|
||||
a457d6fc43c609bd6f38848189966e7d:账本账册
|
||||
740736cf215b7509a7f7e02f36b0b49a:电子壁纸
|
||||
94
modules/xianYu/xianYu.go
Normal file
94
modules/xianYu/xianYu.go
Normal file
@ -0,0 +1,94 @@
|
||||
package xianYu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"planA/initialization/golabl"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gXianYuDll *XianYuDLL
|
||||
)
|
||||
|
||||
// XianYuDLL 闲鱼工具DLL结构
|
||||
type XianYuDLL struct {
|
||||
Dll *syscall.DLL
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// InitXianYuDll 初始化 XianYuDLL
|
||||
func InitXianYuDll() (*XianYuDLL, error) {
|
||||
if gXianYuDll != nil {
|
||||
return gXianYuDll, nil
|
||||
}
|
||||
dllPath := filepath.Join(golabl.Config.FileUrl.XianYuDll, "xy.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("XianYu DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载XianYu DLL 失败: %s", err)
|
||||
}
|
||||
gXianYuDll = &XianYuDLL{
|
||||
Dll: dll,
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}
|
||||
return gXianYuDll, nil
|
||||
}
|
||||
|
||||
// XianYuGoodsAdd 商品新增
|
||||
func (m *XianYuDLL) XianYuGoodsAdd(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsCreat")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsCreat: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuLaunchGoods 商品上架
|
||||
func (m *XianYuDLL) XianYuLaunchGoods(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsPublish")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsPublish: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
BIN
modules/xianYu/xy.dll
Normal file
BIN
modules/xianYu/xy.dll
Normal file
Binary file not shown.
239
modules/xianYu/咸鱼发布dll.md
Normal file
239
modules/xianYu/咸鱼发布dll.md
Normal file
@ -0,0 +1,239 @@
|
||||
##### FreeCString(str *C.char)
|
||||
|
||||
接收其他函数返回值之后,释放内存,参考示例
|
||||
|
||||
##### 内存释放示例
|
||||
|
||||
```go
|
||||
func example () {
|
||||
// ...其他逻辑
|
||||
var res = StartServer (configFile *C.char)
|
||||
FreeCString(res) //释放内存
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### StartServer (configFile *C.char)
|
||||
|
||||
启动http服务器,参数配置文件路径,不提供默认使用工程根目录config.ini
|
||||
|
||||
返回C字符串启动消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### StopServer
|
||||
|
||||
停止HTTP服务器
|
||||
|
||||
返回C字符串停止消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### GetServerStatus
|
||||
|
||||
获取服务器当前状态
|
||||
|
||||
返回C字符串指针消息,running/stopped,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### GetServerAddress
|
||||
|
||||
获取服务器监听地址
|
||||
|
||||
返回C字符串指针服务器地址消息,未运行返回空串,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### ReloadConfig(configFile *C.char)
|
||||
|
||||
重新加载配置文件,参数配置文件路径,不提供默认使用根目录config.ini
|
||||
|
||||
返回C字符串加载结果消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 以下都需要传递appid和appSecret ###
|
||||
|
||||
##### ExecuteGoodsCreat(bodyJson *C.char, configFile *C.char)
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品创建操作,参数商品信息,参考示例
|
||||
|
||||
返回C字符串指针创建商品结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### 商品信息参考示例
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": 1228288260261189,
|
||||
"appSecret": "aq9gAwrwp6WGZkMRqKIXmnu2c2uCm82k",
|
||||
"token": "",
|
||||
"apiShopId": 0,
|
||||
"typePlatform": 4,
|
||||
"shopId": 0,
|
||||
"shopToken": "",
|
||||
"shopName": "",
|
||||
"province": 210000,
|
||||
"city": 210100,
|
||||
"district": 210101,
|
||||
"typeClass": "",
|
||||
"typeGoods": "",
|
||||
"catIds": "d14d229692616168b108d382c4e6ea42",
|
||||
"shop": [
|
||||
{
|
||||
"userName": "xy938400231518",
|
||||
"province": 210000,
|
||||
"city": 210100,
|
||||
"district": 210101,
|
||||
"title": "牧羊少年奇幻之旅",
|
||||
"content": "牧羊少年奇幻之旅",
|
||||
"mainImgs": ["https://img.cdn1.vip/i/68cf5cb4e5840_1758420148.webp"],
|
||||
"contentImgs": []
|
||||
}
|
||||
],
|
||||
"stuffStatus": 90,
|
||||
"bookData": [
|
||||
{
|
||||
"ISBN": "9787530217054",
|
||||
"Title": "牧羊少年奇幻之旅",
|
||||
"Author": "保罗·柯艾略",
|
||||
"Publisher": "北京十月文艺出版",
|
||||
"itemBizType": 2,
|
||||
"spBizType": 24,
|
||||
"prices": [199999, 299999],
|
||||
"stock": 100,
|
||||
"catIds": "22e1d81dc4cf3a25a7f7e02f36b0b49a"
|
||||
}
|
||||
],
|
||||
"itemKey": "itemAAAAA1111"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsPublish(bodyJson *C.char, configFile *C.char)
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品上架操作,参数上架信息,参考示例
|
||||
|
||||
返回C字符串指针行商品上架结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 上架信息参考示例
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"user_name": ["xy938400231518"],
|
||||
"specify_publish_time": "",
|
||||
"notify_url": ""
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 追加下架,改价,擦亮 ####
|
||||
|
||||
##### ExecuteGoodsDownShelf(bodyJson *C.char, configFile *C.char) ######
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品下架操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品下架结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 下架信息参考示例 #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsFlash(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品擦亮操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品擦亮结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 擦亮信息参考示例 #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsEditPrice(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品改价操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 改价信息参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"price": 550000,
|
||||
"originalPrice": 770000
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsEditStock(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品改库存操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 改库存信息参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"stock": 10
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteSelectGoodsListPrice(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
查询店铺列表操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 查询参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
//online_time 字段可传空
|
||||
"online_time": [
|
||||
1690300800,
|
||||
1690366883
|
||||
],
|
||||
"product_status": 22
|
||||
}
|
||||
```
|
||||
|
||||
46
planA.md
Normal file
46
planA.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Plan A
|
||||
## 目录结构
|
||||
```gotemplate
|
||||
controller 逻辑控制
|
||||
controlState 全局状态控制
|
||||
|-lock 状态锁
|
||||
|-serviceAlive 服务存活状态
|
||||
export 导出的csv文件
|
||||
initialization 初始化
|
||||
|-config 初始化配置文件
|
||||
|-cron 初始化定时任务
|
||||
|-golabl 初始化全局变量
|
||||
|-middle 初始化中间件
|
||||
|-mysql 初始化mysql数据库
|
||||
|-redis 初始化redis数据库
|
||||
|-router 初始化路由
|
||||
|-sqlite 初始化sqlite数据库
|
||||
|-validator 初始化验证器
|
||||
|-init.go 初始化文件
|
||||
logs 日志
|
||||
modules DLL模块
|
||||
planB 模块B
|
||||
rep 工厂模式接口
|
||||
router 路由
|
||||
service 服务(针对数据库相关操作)
|
||||
tool 工具
|
||||
type 结构体
|
||||
|-mysql mysql结构体
|
||||
|-redis redis结构体
|
||||
|-sqlite sqlite结构体
|
||||
|-validator 验证器结构体
|
||||
validator 验证器
|
||||
config.yaml 配置文件
|
||||
taskDb.db sqlite数据库(自动创建)
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
planA web服务器
|
||||
planB 任务执行器
|
||||
planC 同步redis数据到硬盘
|
||||
planD 删除任务
|
||||
planE 图片上传到拼多多图片空间(未使用)
|
||||
planF 获取12个商品信息,主要转发小军的接口信息
|
||||
```
|
||||
128
planB/config.yaml
Normal file
128
planB/config.yaml
Normal file
@ -0,0 +1,128 @@
|
||||
server:
|
||||
port: "8080" #服务器端口
|
||||
f_port : "8284" #F程序端口
|
||||
filter: 1 #是否开启违禁词过滤器 0=关闭 1=开启
|
||||
replace_mark: "0" #标题违规词是否替换* 0 不替换 1 替换(替换会继续发布,不替换则不发布)
|
||||
redis_exp: 192 #redis过期时间 192小时(8天)
|
||||
read_db: "sqlite" #读数据库 mysql sqlite
|
||||
err_pause_time: 3000 #错误暂停时间(毫秒)
|
||||
sign_key: "jRQdCh52Z55Kzh1hADaA2ZtdTKetj2PXk60Tz5Yc0iz9aD8Wafbk7CwAZ8cz69A9zb9caZ3k9dnR3Ys06J5nYFPrZ0xE9p6TY8DCD538ryiRjW81YTPmk41tCEnXizPh" #签名密钥
|
||||
data_day: 2 #数据保存时间(天)
|
||||
is_c: true #是否启动 C程序
|
||||
speed: #限速器
|
||||
pdd_speed: 18 #拼多多 每秒多少个任务
|
||||
xianyu_speed: 5 #闲鱼 每秒多少个任务
|
||||
watermark: 15 #打水印速率的个数
|
||||
minio: #minio 图片空间
|
||||
url: "shxy.image.yushutx.com" #minio地址
|
||||
access_key_id: "minioadmin" #minio keyId
|
||||
secret_access_key: "minioadmin" #minio key
|
||||
bucket_name: "task-xianyu" #存储桶
|
||||
target_dir: "test/2025" #目标目录
|
||||
use_ssl: true #是否使用 SSL
|
||||
alive:
|
||||
fluent: 50 #存活状态-流畅时间(毫秒)
|
||||
slow: 200 #存活状态-缓慢时间(毫秒)
|
||||
pool_config:
|
||||
size: 6 #协程数量
|
||||
with_expiry_duration: 10 #过期时间
|
||||
with_pre_alloc: true #预分配
|
||||
with_max_blocking_tasks: 600 #阻塞任务数
|
||||
with_nonblocking : true #非阻塞
|
||||
mysql_config:
|
||||
db_name: "task_user" #数据库名称
|
||||
user: "root" #数据库用户名
|
||||
password: "root" #数据库密码
|
||||
host: "127.0.0.1" #数据库地址
|
||||
port: 3306 #数据库端口
|
||||
loglevel: "info" #数据库日志级别 info=开发环境:输出所有日志(SQL、执行时间等) warn=预发布:输出警告+错误 error=生产环境:只输出错误日志 silent=生产环境:关闭所有日志
|
||||
max_retry_times: 3 #数据库重试次数
|
||||
base_retry_interval: 100 #数据库重试间隔(毫秒)
|
||||
max_retry_interval: 3 #数据库最大重试间隔(秒)
|
||||
max_open_conns: 100 #数据库最大打开连接数
|
||||
max_idle_conns: 20 #数据库最大空闲连接数
|
||||
conn_max_idle_time: 30 #数据库连接最大空闲时间(分钟)
|
||||
conn_max_lifetime: 1 #数据库连接最大生命周期(小数)
|
||||
redis_config:
|
||||
- db_name: "任务池"
|
||||
db: 0
|
||||
addr: "127.0.0.1:6379"
|
||||
password: "123456"
|
||||
- db_name: "书品库"
|
||||
db: 7
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "店铺信息"
|
||||
db: 8
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "出版社信息列表"
|
||||
db: 3
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "省市区列表"
|
||||
db: 4
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "没有图片的 isbn"
|
||||
db: 5
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "没有书籍的 isbn"
|
||||
db: 6
|
||||
addr: "36.212.12.247:6379"
|
||||
password: "long6166@@"
|
||||
- db_name: "拼多多操作回调数据库"
|
||||
db: 14
|
||||
addr: "36.212.20.113:7963"
|
||||
password: "j8nZ4jra2E"
|
||||
pdd_config:
|
||||
client_id: "203c5a7ba8bd4b8488d5e26f93052642" #拼多多clientId
|
||||
client_secret: "892ffaa86e12b7a3d8d2942b669d9aa520ad8179" #拼多多clientSecret
|
||||
kfz_config:
|
||||
app_id: 576 #孔夫子appid
|
||||
app_secret: "256e10220c5b307f5172b1a49c11467a6cfa8038bbe2a7feccc42231852324f8" #孔夫子appsecret
|
||||
taobao_config:
|
||||
app_key: 852496 #平台分配的 App Key
|
||||
app_secret: "6876bd91e93840wet8d264255a24d892" #App 密钥,用于 HMAC-MD5 签名计算
|
||||
token: "f614764edd33345a41acd10ed3edaa9d" #App 密钥,用于 HMAC-MD5 签名计算
|
||||
ati: "455224047430" #设备/用户标识
|
||||
user_id: "1779953588275" #当前操作用户 ID
|
||||
company_id: "1779953588275" #公司/店铺所属 ID
|
||||
base_url: "https://fxzs.yulinkai.com" #API 基础域名
|
||||
local_img_dir: "D:\\file\\taobao" #图片本地缓存目录
|
||||
request_timeout: 20 #请求超时时间(秒)
|
||||
http_url:
|
||||
task_url: "http://127.0.0.1:8080" #A 程序接口地址
|
||||
file_url:
|
||||
xian_yu_dll: "D:\\source\\planA\\planB\\modules\\xianYu" #闲鱼 DLL库路径
|
||||
pdd_dll: "D:\\source\\planA\\planB\\modules\\pdd" #拼多多 DLL库路径
|
||||
kfz_dll: "D:\\source\\planA\\planB\\modules\\kfz" #孔夫子 DLL库路径
|
||||
log_dll: "D:\\source\\planA\\planB\\modules\\logs" #日志 DLL库路径
|
||||
image_dll: "D:\\source\\planA\\planB\\modules\\image" #水印 DLL库路径
|
||||
b_file_name: "D:\\source\\planA\\planB\\planB.exe" #B 程序文件路径
|
||||
c_file_name: "D:\\source\\planA\\planC\\planC.exe" #C 程序文件路径
|
||||
d_file_name: "D:\\source\\planA\\planD\\planD.exe" #D 程序文件路径
|
||||
e_file_name: "D:\\source\\planA\\planE\\planE.exe" #E 程序文件路径
|
||||
f_file_name: "D:\\source\\planA\\planE\\planF.exe" #F 程序文件路径
|
||||
create_task_url: "https://api.buzhiyushu.cn/zhishu/baseInfo/addNewTask" #新增任务接口
|
||||
create_task_notice_url: "http://36.212.12.92:8055/task" #核价软件提交数据通知接口
|
||||
create_operation_task_notice_url: "http://36.212.12.92:8055/taskV2" #操作商品任务核价软件提交数据通知接口
|
||||
banned_word_substitution_url : "http://36.212.12.247:13001/task/getFilterSetNew" #违禁词替换接口
|
||||
pdd_token_url: "https://api.buzhiyushu.cn/huidiao/pdd/getToken" #获取系统规定拼多多 token
|
||||
deduction_url: "https://api.buzhiyushu.cn/zhishu/userRecharge/apiBalancePayment" #扣费接口
|
||||
pdd_get_goods_url: "http://pdd.buzhiyushu.cn/api/pdd/auth/getShopGoodsList" #查询拼多多商品接口
|
||||
pdd_get_goods_detail_url: "http://pdd.buzhiyushu.cn/api/pdd/auth/newGetShopGoodsDetailList" #查询拼多多商品详情列表接口
|
||||
pdd_add_goods_url: "http://119.45.237.193:14003/task/putShopGoods" #添加拼多多商品接口
|
||||
pdd_get_sku_id: "http://192.168.101.127:18099/shopGoods/getShopGoods" #批量获取 skuId接口
|
||||
xianyu_add_goods_url: "http://119.45.237.193:14008/task/putShopGoods" #添加闲鱼商品接口
|
||||
kfz_add_goods_url: "http://119.45.237.193:14009/task/kfzverifyPricePublishGoods" #添加孔夫子商品接口
|
||||
del_task_url: "http://119.45.237.193:14008/shopGoods/delShopGoodsk" #删除任务通知接口
|
||||
backup_url: "D:\\file\\backup" #备份文件路径
|
||||
pdd_goods_details_url: "D:\\file\\pdd_goods_details" #保存拼多多详情路径
|
||||
update_token_url: "http://146.56.227.42:9099/api/updateToken" #更新拼多多 token 到redis
|
||||
kfz_img_temp_url: "D:\\file\\kfzImg" #孔夫子图片临时路径
|
||||
kfz_img_http_url: "https://www0.kfzimg.com/" #孔夫子图片 http 路径
|
||||
get_pdd_goods_shopid_isbn_url : "http://119.45.237.193:14008/shopGoods/selectTrilateralIds" #获取拼多多商品 shopId 和 isbn
|
||||
get_subscription_expiration_date_url : "http://119.45.237.193:9096/api/user/getKfzUserRecbusiness" #获取订阅到期时间
|
||||
pdd_img_temp_url: "D:\\file\\pddImg" #拼多多图片临时路径
|
||||
36
planB/dispatcher/dispatcher.go
Normal file
36
planB/dispatcher/dispatcher.go
Normal file
@ -0,0 +1,36 @@
|
||||
package dispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
planAType "planA/type"
|
||||
)
|
||||
|
||||
// Go 调度任务
|
||||
// @param bodyWait 任务体
|
||||
// @return string 任务ID
|
||||
// @return error 错误信息
|
||||
func Go(bodyWait planAType.TaskBody) (string, error) {
|
||||
switch golabl.TaskType {
|
||||
case golabl.TaskTypeAddGoodsTask:
|
||||
return golabl.Platform.AddGoodsTask(bodyWait) // 添加商品
|
||||
|
||||
//挪到了main方法中执行
|
||||
//case "GetGoodsTask":
|
||||
// return golabl.Platform.GetGoodsTask() // 获取商品
|
||||
|
||||
case golabl.TaskTypeSetGoodsTask:
|
||||
|
||||
return golabl.Platform.SetGoodsTask(), nil // 修改商品
|
||||
|
||||
case golabl.TaskTypeOperationGoodsTask:
|
||||
return golabl.Platform.OperationGoodsTask(bodyWait) // 操作商品
|
||||
|
||||
case golabl.TaskTypeIncStock:
|
||||
return golabl.Platform.IncStockTask(bodyWait) // 增量库存
|
||||
|
||||
default:
|
||||
|
||||
return "", fmt.Errorf("没有此任务类型")
|
||||
}
|
||||
}
|
||||
1633
planB/dispatcher/kongfuzi/kongfizi.go
Normal file
1633
planB/dispatcher/kongfuzi/kongfizi.go
Normal file
File diff suppressed because it is too large
Load Diff
1715
planB/dispatcher/pinduoduo/pinduoduo.go
Normal file
1715
planB/dispatcher/pinduoduo/pinduoduo.go
Normal file
File diff suppressed because it is too large
Load Diff
1042
planB/dispatcher/taobao/taobao.go
Normal file
1042
planB/dispatcher/taobao/taobao.go
Normal file
File diff suppressed because it is too large
Load Diff
1495
planB/dispatcher/xianyu/xianyu.go
Normal file
1495
planB/dispatcher/xianyu/xianyu.go
Normal file
File diff suppressed because it is too large
Load Diff
40
planB/go.sum
Normal file
40
planB/go.sum
Normal file
@ -0,0 +1,40 @@
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/panjf2000/ants/v2 v2.12.0 h1:u9JhESo83i/GkZnhfTNuFMMWcNt7mnV1bGJ6FT4wXH8=
|
||||
github.com/panjf2000/ants/v2 v2.12.0/go.mod h1:tSQuaNQ6r6NRhPt+IZVUevvDyFMTs+eS4ztZc52uJTY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
46
planB/initialization/config/config.go
Normal file
46
planB/initialization/config/config.go
Normal file
@ -0,0 +1,46 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
planBConfig "planA/planB/modules/config"
|
||||
"planA/tool"
|
||||
planAtype "planA/type"
|
||||
)
|
||||
|
||||
// GetConfigSetToG 获取配置文件并保存到全局变量中
|
||||
// @return error 错误信息
|
||||
func GetConfigSetToG() error {
|
||||
// 检查全局 CTX 是否失效 以防止重复初始化和 ctx 失效 导致程序崩溃
|
||||
checkContextErr := tool.CheckContext(golabl.Ctx)
|
||||
if checkContextErr != nil {
|
||||
// 返回 且 返回错误
|
||||
return checkContextErr
|
||||
}
|
||||
|
||||
//读取配置文件
|
||||
var config planAtype.Config
|
||||
|
||||
// 加载 config.dll
|
||||
dll, initConfigDLLErr := planBConfig.InitConfigDLL()
|
||||
if initConfigDLLErr != nil {
|
||||
return initConfigDLLErr
|
||||
}
|
||||
|
||||
// 读取配置文件
|
||||
configJson, ReadConfigFileErr := dll.ReadConfigFile("", "config.yaml")
|
||||
if ReadConfigFileErr != nil {
|
||||
return fmt.Errorf("读取配置文件失败:%v", ReadConfigFileErr)
|
||||
}
|
||||
// 转换配置文件到 JSON
|
||||
jsonUnmarshalErr := json.Unmarshal([]byte(configJson), &config)
|
||||
if jsonUnmarshalErr != nil {
|
||||
return fmt.Errorf("解析配置文件失败:%v", jsonUnmarshalErr)
|
||||
}
|
||||
|
||||
// 保存到全局变量
|
||||
golabl.Config = config
|
||||
// 返回
|
||||
return nil
|
||||
}
|
||||
39
planB/initialization/dll/dll.go
Normal file
39
planB/initialization/dll/dll.go
Normal file
@ -0,0 +1,39 @@
|
||||
package dll
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/dll/image"
|
||||
"planA/planB/initialization/dll/kfz"
|
||||
"planA/planB/initialization/dll/logs"
|
||||
"planA/planB/initialization/dll/pdd"
|
||||
"planA/planB/initialization/dll/xianYu"
|
||||
)
|
||||
|
||||
// GetDllSetToG 获取DLL
|
||||
func GetDllSetToG() error {
|
||||
// 初始化 PddDll
|
||||
getPddDllSetToGErr := pdd.GetPddDllSetToG()
|
||||
if getPddDllSetToGErr != nil {
|
||||
return getPddDllSetToGErr
|
||||
}
|
||||
// 初始化 ImageDll
|
||||
getImageDllSetToGErr := image.GetImageDllSetToG()
|
||||
if getImageDllSetToGErr != nil {
|
||||
return getImageDllSetToGErr
|
||||
}
|
||||
// 初始化 XianYuDll
|
||||
getXianYuDllSetToGErr := xianYu.GetXianYuDllSetToG()
|
||||
if getXianYuDllSetToGErr != nil {
|
||||
return getXianYuDllSetToGErr
|
||||
}
|
||||
// 初始化 LogrDll
|
||||
getLogrDllSetToGErr := logs.GetLogrDllSetToG()
|
||||
if getLogrDllSetToGErr != nil {
|
||||
return getLogrDllSetToGErr
|
||||
}
|
||||
// 获取KfzDll
|
||||
getKfzDllSetToGErr := kfz.GetKfzDllSetToG()
|
||||
if getKfzDllSetToGErr != nil {
|
||||
return getKfzDllSetToGErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
16
planB/initialization/dll/image/image.go
Normal file
16
planB/initialization/dll/image/image.go
Normal file
@ -0,0 +1,16 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/modules/image"
|
||||
)
|
||||
|
||||
// GetImageDllSetToG 获取图片DLL
|
||||
func GetImageDllSetToG() error {
|
||||
imageDll, imageDllErr := image.InitImageDll(golabl.Config.FileUrl.ImageDll)
|
||||
if imageDllErr != nil {
|
||||
return imageDllErr
|
||||
}
|
||||
golabl.ImageDll = imageDll
|
||||
return nil
|
||||
}
|
||||
17
planB/initialization/dll/kfz/kfz.go
Normal file
17
planB/initialization/dll/kfz/kfz.go
Normal file
@ -0,0 +1,17 @@
|
||||
package kfz
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/modules/kfz"
|
||||
)
|
||||
|
||||
// GetKfzDllSetToG 获取孔夫子DLL
|
||||
func GetKfzDllSetToG() error {
|
||||
// 初始化 KfzDll
|
||||
kfzDll, err := kfz.InitKfzDll(golabl.Config.FileUrl.KfzDll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
golabl.KfzDll = kfzDll
|
||||
return nil
|
||||
}
|
||||
16
planB/initialization/dll/logs/logs.go
Normal file
16
planB/initialization/dll/logs/logs.go
Normal file
@ -0,0 +1,16 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/modules/logs"
|
||||
)
|
||||
|
||||
// GetLogrDllSetToG 获取日志DLL
|
||||
func GetLogrDllSetToG() error {
|
||||
dll, ensureLoggerDLLErr := logs.EnsureLoggerDLL(golabl.Config.FileUrl.LogDll)
|
||||
if ensureLoggerDLLErr != nil {
|
||||
return ensureLoggerDLLErr
|
||||
}
|
||||
golabl.LogDll = dll
|
||||
return nil
|
||||
}
|
||||
17
planB/initialization/dll/pdd/pdd.go
Normal file
17
planB/initialization/dll/pdd/pdd.go
Normal file
@ -0,0 +1,17 @@
|
||||
package pdd
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/modules/pdd"
|
||||
)
|
||||
|
||||
// GetPddDllSetToG 获取拼多多DLL
|
||||
func GetPddDllSetToG() error {
|
||||
// 初始化 PddDll
|
||||
pddDll, err := pdd.InitPddDll(golabl.Config.FileUrl.PddDll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
golabl.PddDll = pddDll
|
||||
return nil
|
||||
}
|
||||
16
planB/initialization/dll/xianYu/xianYu.go
Normal file
16
planB/initialization/dll/xianYu/xianYu.go
Normal file
@ -0,0 +1,16 @@
|
||||
package xianYu
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/modules/xianYu"
|
||||
)
|
||||
|
||||
// GetXianYuDllSetToG 获取闲鱼DLL
|
||||
func GetXianYuDllSetToG() error {
|
||||
xianYuDll, xianYuDllErr := xianYu.InitXianYuDll(golabl.Config.FileUrl.XianYuDll)
|
||||
if xianYuDllErr != nil {
|
||||
return xianYuDllErr
|
||||
}
|
||||
golabl.XianYuDll = xianYuDll
|
||||
return nil
|
||||
}
|
||||
59
planB/initialization/golabl/golabl.go
Normal file
59
planB/initialization/golabl/golabl.go
Normal file
@ -0,0 +1,59 @@
|
||||
package golabl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"planA/planB/interfaces"
|
||||
"planA/planB/modules/image"
|
||||
"planA/planB/modules/kfz"
|
||||
"planA/planB/modules/logs"
|
||||
"planA/planB/modules/pdd"
|
||||
xianYuDll "planA/planB/modules/xianYu"
|
||||
|
||||
planBType "planA/planB/type"
|
||||
planAType "planA/type"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
Ctx context.Context // 全局上下文
|
||||
Speed *rate.Limiter // 全局令牌桶限速器
|
||||
Config planAType.Config // 全局配置
|
||||
Redis planBType.Redis // 全局 Redis
|
||||
Task *planBType.Task // 全局任务
|
||||
Pool planBType.Pool // 全局线程池
|
||||
Logic planBType.Logic // 全局逻辑控制
|
||||
Platform interfaces.GoodsTask // 全局平台对象
|
||||
TaskType string // 全局任务类型
|
||||
MinIo *planBType.MinIOClient // 全局 MinIO
|
||||
PddDll *pdd.PddDLL // 全局拼多多 DLL
|
||||
ImageDll *image.ImageDLL // 全局 ImageDll
|
||||
XianYuDll *xianYuDll.XianYuDLL // 全局 闲鱼 DLL
|
||||
LogDll *logs.LoggerDLL // 全局日志 DLL
|
||||
KfzDll *kfz.KfzDLL // 全局孔夫子 DLL
|
||||
MysqlDb *gorm.DB // 全局 mysql
|
||||
KfzGetCommonCategory map[string]string // 孔夫子商品分类列表
|
||||
)
|
||||
|
||||
// 任务 body 状态
|
||||
const (
|
||||
BodyStatusSuccess int64 = 1 // 正常
|
||||
BodyStatusError int64 = 2 // 错误
|
||||
)
|
||||
|
||||
// 任务类型
|
||||
const (
|
||||
TaskTypeAddGoodsTask string = "AddGoodsTask" // 添加商品
|
||||
TaskTypeGetGoodsTask string = "GetGoodsTask" // 获取商品
|
||||
TaskTypeSetGoodsTask string = "SetGoodsTask" // 修改商品
|
||||
TaskTypeOperationGoodsTask string = "OperationGoodsTask" // 操作商品
|
||||
TaskTypeIncStock string = "IncStock" // 增量库存
|
||||
)
|
||||
|
||||
// 错误集
|
||||
const (
|
||||
LastIndexRedisNil int64 = 10001 // redis 多次读Nil
|
||||
LastIndexGoodsMaxRestriction int64 = 11002 // 店铺已达到最大商品限制
|
||||
LastIndexFilteWordErr int64 = 10003 // 过滤关键词异常
|
||||
)
|
||||
95
planB/initialization/init.go
Normal file
95
planB/initialization/init.go
Normal file
@ -0,0 +1,95 @@
|
||||
package initialization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"planA/planB/initialization/config"
|
||||
"planA/planB/initialization/dll"
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/initialization/kfz"
|
||||
"planA/planB/initialization/minIo"
|
||||
"planA/planB/initialization/mysql"
|
||||
"planA/planB/initialization/platform"
|
||||
"planA/planB/initialization/pool"
|
||||
"planA/planB/initialization/redis"
|
||||
"planA/planB/initialization/speed"
|
||||
"planA/planB/initialization/task"
|
||||
"planA/planB/initialization/taskType"
|
||||
"planA/planB/initialization/title"
|
||||
planBType "planA/planB/type"
|
||||
planAType "planA/type"
|
||||
)
|
||||
|
||||
// Init 初始化
|
||||
func Init(taskId string) error {
|
||||
//初始化上下文
|
||||
if golabl.Ctx == nil {
|
||||
golabl.Ctx = context.Background()
|
||||
}
|
||||
|
||||
// 初始化配置文件
|
||||
if configErr := config.GetConfigSetToG(); configErr != nil {
|
||||
return fmt.Errorf("初始化配置文件失败:%v", configErr)
|
||||
}
|
||||
|
||||
// 初始化 redis
|
||||
if redisErr := redis.LinkRedisSetToG(); redisErr != nil {
|
||||
return fmt.Errorf("初始化redis失败: %v", redisErr)
|
||||
}
|
||||
|
||||
// 初始化 mysql
|
||||
if mysqlErr := mysql.LikeMysqlSetToG(); mysqlErr != nil {
|
||||
return fmt.Errorf("初始化mysql失败: %v", mysqlErr)
|
||||
}
|
||||
|
||||
// 初始化 task
|
||||
golabl.Task = &planBType.Task{
|
||||
TaskId: taskId,
|
||||
Header: &planAType.TaskHeader{},
|
||||
Footer: &planAType.TaskFooter{},
|
||||
BodyWait: &planAType.TaskBody{},
|
||||
BodyOver: &planAType.TaskBody{},
|
||||
BodyBackup: &planAType.TaskBody{},
|
||||
}
|
||||
if taskErr := task.GetTaskHeaderAndFooterSetToG(); taskErr != nil {
|
||||
return fmt.Errorf("初始化任务失败: %v", taskErr)
|
||||
}
|
||||
|
||||
// 初始化限速器
|
||||
speed.Init()
|
||||
|
||||
// 初始化 协程池
|
||||
if poolErr := pool.CreatePoolToG(); poolErr != nil {
|
||||
return fmt.Errorf("初始化协程池失败: %v", poolErr)
|
||||
}
|
||||
|
||||
// 初始化平台
|
||||
if platformErr := platform.GetPlatformSetToG(); platformErr != nil {
|
||||
return fmt.Errorf("初始化平台失败: %v", platformErr)
|
||||
}
|
||||
|
||||
// 初始化任务类型
|
||||
if taskTypeErr := taskType.GetTaskTypeSetToG(); taskTypeErr != nil {
|
||||
return fmt.Errorf("初始化任务类型失败: %v", taskTypeErr)
|
||||
}
|
||||
|
||||
// 初始化图片空间
|
||||
if newMinIOClientErr := minIo.NewMinIOClient(); newMinIOClientErr != nil {
|
||||
return fmt.Errorf("初始化图片空间失败: %v", newMinIOClientErr)
|
||||
}
|
||||
|
||||
// 初始化 DLL
|
||||
if dllErr := dll.GetDllSetToG(); dllErr != nil {
|
||||
return fmt.Errorf("初始化DLL失败: %v", dllErr)
|
||||
}
|
||||
|
||||
// 初始化 孔夫子公共分类
|
||||
if getKfzGoodsCategorySetToGErr := kfz.GetKfzGetCommonCategorySetToG(); getKfzGoodsCategorySetToGErr != nil {
|
||||
return fmt.Errorf("初始化孔夫子公共分类失败: %v", getKfzGoodsCategorySetToGErr)
|
||||
}
|
||||
|
||||
//设置窗口标题
|
||||
title.SetWinTitle()
|
||||
|
||||
return nil
|
||||
}
|
||||
95
planB/initialization/kfz/kfz.go
Normal file
95
planB/initialization/kfz/kfz.go
Normal file
@ -0,0 +1,95 @@
|
||||
package kfz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
planBTypeKfz "planA/planB/type/kfz"
|
||||
)
|
||||
|
||||
func GetKfzGetCommonCategorySetToG() error {
|
||||
if golabl.Task.Header.ShopType == "2" {
|
||||
//获取孔夫子商品分类
|
||||
goodsCategoryList, getGoodsCategoryListErr := golabl.KfzDll.GetCommonCategory(golabl.Config.KfzConfig.AppId, golabl.Config.KfzConfig.AppSecret, golabl.Task.Header.ShopMsg.Token)
|
||||
if getGoodsCategoryListErr != nil {
|
||||
return getGoodsCategoryListErr
|
||||
}
|
||||
//转为结构体
|
||||
var kfzGoodsCategoryList planBTypeKfz.KfzCategoryRet
|
||||
unmarshalErr := json.Unmarshal([]byte(goodsCategoryList), &kfzGoodsCategoryList)
|
||||
if unmarshalErr != nil {
|
||||
return unmarshalErr
|
||||
}
|
||||
//判断是否错误
|
||||
if kfzGoodsCategoryList.ErrorResponse != nil {
|
||||
return fmt.Errorf("获取商品公共分类失败 %v", kfzGoodsCategoryList.ErrorResponse)
|
||||
}
|
||||
//设置为全局
|
||||
golabl.KfzGetCommonCategory = make(map[string]string)
|
||||
|
||||
// 使用递归函数遍历所有分类,传入路径前缀
|
||||
for _, level1 := range kfzGoodsCategoryList.SuccessResponse {
|
||||
collectCategoriesWithPath(level1, "")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 递归收集分类,带路径
|
||||
func collectCategoriesWithPath(category interface{}, parentPath string) {
|
||||
switch v := category.(type) {
|
||||
case planBTypeKfz.CategoryLevel1:
|
||||
currentPath := v.Name
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
for _, child := range v.Children {
|
||||
collectCategoriesWithPath(child, currentPath)
|
||||
}
|
||||
case planBTypeKfz.CategoryLevel2:
|
||||
currentPath := buildPath(parentPath, v.Name)
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
for _, child := range v.Children {
|
||||
collectCategoriesWithPath(child, currentPath)
|
||||
}
|
||||
case planBTypeKfz.CategoryLevel3:
|
||||
currentPath := buildPath(parentPath, v.Name)
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
for _, child := range v.Children {
|
||||
collectCategoriesWithPath(child, currentPath)
|
||||
}
|
||||
case planBTypeKfz.CategoryLevel4:
|
||||
currentPath := buildPath(parentPath, v.Name)
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
for _, child := range v.Children {
|
||||
collectCategoriesWithPath(child, currentPath)
|
||||
}
|
||||
case planBTypeKfz.CategoryLevel5:
|
||||
currentPath := buildPath(parentPath, v.Name)
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
for _, child := range v.Children {
|
||||
collectCategoriesWithPath(child, currentPath)
|
||||
}
|
||||
case planBTypeKfz.CategoryLevel6:
|
||||
currentPath := buildPath(parentPath, v.Name)
|
||||
if v.Name != "" && v.Id != "" {
|
||||
golabl.KfzGetCommonCategory[currentPath] = v.Id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建路径,用 / 连接
|
||||
func buildPath(parentPath, currentName string) string {
|
||||
if parentPath == "" {
|
||||
return currentName
|
||||
}
|
||||
return parentPath + "/" + currentName
|
||||
}
|
||||
30
planB/initialization/minIo/minIo.go
Normal file
30
planB/initialization/minIo/minIo.go
Normal file
@ -0,0 +1,30 @@
|
||||
package minIo
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
PlanBType "planA/planB/type"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
// NewMinIOClient 创建 MinIO 客户端实例
|
||||
func NewMinIOClient() error {
|
||||
client, newMinIoErr := minio.New(golabl.Config.Minio.Url, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(golabl.Config.Minio.AccessKeyID, golabl.Config.Minio.SecretAccessKey, ""),
|
||||
Secure: false,
|
||||
})
|
||||
if newMinIoErr != nil {
|
||||
return newMinIoErr
|
||||
}
|
||||
|
||||
golabl.MinIo = &PlanBType.MinIOClient{
|
||||
Client: client,
|
||||
Endpoint: golabl.Config.Minio.Url,
|
||||
AccessKey: golabl.Config.Minio.AccessKeyID,
|
||||
SecretKey: golabl.Config.Minio.SecretAccessKey,
|
||||
UseSSL: golabl.Config.Minio.UseSSL,
|
||||
BucketName: golabl.Config.Minio.BucketName,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
73
planB/initialization/mysql/mysql.go
Normal file
73
planB/initialization/mysql/mysql.go
Normal file
@ -0,0 +1,73 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// LikeMysqlSetToG 链接mysql并保留到全局变量中
|
||||
func LikeMysqlSetToG() error {
|
||||
|
||||
// 1. 获取mysql配置
|
||||
mysqlConfig := golabl.Config.MysqlConfig
|
||||
|
||||
// 2. 配置 DSN
|
||||
dsn := fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
mysqlConfig.User,
|
||||
mysqlConfig.Password,
|
||||
mysqlConfig.Host,
|
||||
mysqlConfig.Port,
|
||||
mysqlConfig.DBName,
|
||||
)
|
||||
|
||||
// 3. 配置 GORM 连接选项
|
||||
|
||||
logLevel := logger.Silent
|
||||
switch mysqlConfig.Loglevel {
|
||||
case "info":
|
||||
logLevel = logger.Info
|
||||
case "warn":
|
||||
logLevel = logger.Warn
|
||||
case "error":
|
||||
logLevel = logger.Error
|
||||
case "silent":
|
||||
logLevel = logger.Silent
|
||||
}
|
||||
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logLevel), //日志级别
|
||||
DisableForeignKeyConstraintWhenMigrating: true, //不创建外键约束
|
||||
}
|
||||
|
||||
// 4. 连接数据库
|
||||
db, openErr := gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
|
||||
// 5. 获取底层 sql.DB,配置连接池
|
||||
sqlDB, dbErr := db.DB()
|
||||
if dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
// 连接池优化 + 保活配置
|
||||
sqlDB.SetMaxOpenConns(mysqlConfig.MaxOpenConns)
|
||||
sqlDB.SetMaxIdleConns(mysqlConfig.MaxIdleConns)
|
||||
sqlDB.SetConnMaxIdleTime(mysqlConfig.ConnMaxIdleTime * time.Minute)
|
||||
sqlDB.SetConnMaxLifetime(mysqlConfig.ConnMaxLifetime * time.Hour)
|
||||
|
||||
// 5. 验证连接
|
||||
if dbPingErr := sqlDB.Ping(); dbPingErr != nil {
|
||||
return dbPingErr
|
||||
}
|
||||
|
||||
// 7. 保存db实例
|
||||
golabl.MysqlDb = db
|
||||
return nil
|
||||
}
|
||||
30
planB/initialization/platform/platform.go
Normal file
30
planB/initialization/platform/platform.go
Normal file
@ -0,0 +1,30 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"planA/planB/dispatcher/kongfuzi"
|
||||
pinDuoDuo "planA/planB/dispatcher/pinduoduo"
|
||||
"planA/planB/dispatcher/taobao"
|
||||
"planA/planB/dispatcher/xianyu"
|
||||
"planA/planB/initialization/golabl"
|
||||
)
|
||||
|
||||
// GetPlatformSetToG 获取平台并保存到全局变量中
|
||||
func GetPlatformSetToG() error {
|
||||
switch golabl.Task.Header.ShopType {
|
||||
case "1":
|
||||
golabl.Platform = pinDuoDuo.NewPinDuoDuo()
|
||||
return nil
|
||||
case "2":
|
||||
golabl.Platform = kongfuzi.NewKongFuZi()
|
||||
return nil
|
||||
case "5":
|
||||
golabl.Platform = xianyu.NewXianYu()
|
||||
return nil
|
||||
case "6":
|
||||
golabl.Platform = taobao.NewTaobao()
|
||||
return nil
|
||||
default:
|
||||
return errors.New("错误!")
|
||||
}
|
||||
}
|
||||
30
planB/initialization/pool/pool.go
Normal file
30
planB/initialization/pool/pool.go
Normal file
@ -0,0 +1,30 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/panjf2000/ants/v2"
|
||||
)
|
||||
|
||||
// CreatePoolToG 创建协程池到全局变量中
|
||||
// @return error 错误信息
|
||||
func CreatePoolToG() error {
|
||||
// 创建协程池
|
||||
pool, err := ants.NewPool(
|
||||
golabl.Config.PoolConfig.Size,
|
||||
ants.WithExpiryDuration(time.Duration(golabl.Config.PoolConfig.WithExpiryDuration)*time.Second), // 过期时间
|
||||
ants.WithPreAlloc(golabl.Config.PoolConfig.WithPreAlloc), // 预分配
|
||||
ants.WithMaxBlockingTasks(golabl.Config.PoolConfig.WithMaxBlockingTasks), // 最大阻塞任务数
|
||||
ants.WithNonblocking(golabl.Config.PoolConfig.WithNonblocking), // 非阻塞
|
||||
ants.WithPanicHandler(func(p interface{}) { fmt.Printf("panic: %v", p) }), // panic 处理
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
golabl.Pool.Pool = pool
|
||||
golabl.Pool.Wg = &sync.WaitGroup{}
|
||||
return nil
|
||||
}
|
||||
82
planB/initialization/redis/redis.go
Normal file
82
planB/initialization/redis/redis.go
Normal file
@ -0,0 +1,82 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
planAType "planA/type"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// LinkRedisSetToG 链接redis并保留到全局变量中
|
||||
// @return error 错误信息
|
||||
func LinkRedisSetToG() error {
|
||||
|
||||
// 1. 获取redis配置
|
||||
redisConfig := golabl.Config.RedisConfig
|
||||
redisClientA, redisErr := NewRedisClient(redisConfig[0])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[0].Addr, redisConfig[0].DB, redisErr)
|
||||
}
|
||||
golabl.Redis.RedisDbA = redisClientA
|
||||
// Redis B - Redis实例
|
||||
redisClientB, redisErr := NewRedisClient(redisConfig[3])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[3].Addr, redisConfig[3].DB, redisErr)
|
||||
}
|
||||
golabl.Redis.RedisDbB = redisClientB
|
||||
|
||||
// Redis C - Redis实例
|
||||
redisClientC, redisErr := NewRedisClient(redisConfig[4])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[4].Addr, redisConfig[4].DB, redisErr)
|
||||
}
|
||||
golabl.Redis.RedisDbC = redisClientC
|
||||
|
||||
// Redis D - Redis实例
|
||||
redisClientD, redisErr := NewRedisClient(redisConfig[5])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[5].Addr, redisConfig[5].DB, redisErr)
|
||||
}
|
||||
golabl.Redis.RedisDbD = redisClientD
|
||||
|
||||
// Redis E - Redis实例
|
||||
redisClientE, redisErr := NewRedisClient(redisConfig[2])
|
||||
if redisErr != nil {
|
||||
return fmt.Errorf("初始化 redis %v db%v 失败: %v\n", redisConfig[2].Addr, redisConfig[2].DB, redisErr)
|
||||
}
|
||||
golabl.Redis.RedisDbE = redisClientE
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRedisClient 创建redis 客户端
|
||||
// @param config redis配置
|
||||
// @return *redis.Client redis客户端
|
||||
// @return error 错误信息
|
||||
func NewRedisClient(config planAType.RedisConfig) (*redis.Client, error) {
|
||||
ctx := golabl.Ctx
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: config.Addr, // 连接地址
|
||||
Password: config.Password, // 密码
|
||||
DB: config.DB, // 数据库
|
||||
PoolSize: config.PoolSize, // 连接池大小
|
||||
PoolTimeout: time.Duration(config.PoolTimeout), // 连接池超时时间
|
||||
ReadTimeout: time.Duration(config.ReadTimeout), // 读取超时
|
||||
WriteTimeout: time.Duration(config.WriteTimeout), // 写入超时
|
||||
DialTimeout: time.Duration(config.DialTimeout), // 连接超时
|
||||
IdleTimeout: time.Duration(config.IdleTimeout), // 空闲超时
|
||||
MinIdleConns: config.MinIdleConns, // 最小空闲连接数
|
||||
IdleCheckFrequency: time.Duration(config.IdleCheckFrequency), // 空闲检查频率
|
||||
MaxRetries: config.MaxRetries, // 最大重试次数
|
||||
MaxRetryBackoff: time.Duration(config.MaxRetryBackoff), // 最大重试间隔
|
||||
MinRetryBackoff: time.Duration(config.MinRetryBackoff), // 最小重试间隔
|
||||
})
|
||||
// 测试连接
|
||||
_, err := rdb.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
return rdb, err
|
||||
}
|
||||
return rdb, nil
|
||||
}
|
||||
32
planB/initialization/speed/speed.go
Normal file
32
planB/initialization/speed/speed.go
Normal file
@ -0,0 +1,32 @@
|
||||
package speed
|
||||
|
||||
import (
|
||||
"planA/planB/initialization/golabl"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// Init 初始化 限速器
|
||||
func Init() {
|
||||
//默认为18
|
||||
speed := 18
|
||||
//根据平台设置速率
|
||||
switch golabl.Task.Header.ShopType {
|
||||
case "1":
|
||||
speed = golabl.Config.Speed.PddSpeed
|
||||
//case 2:
|
||||
case "5":
|
||||
speed = golabl.Config.Speed.XianyuSpeed
|
||||
default:
|
||||
speed = 18
|
||||
}
|
||||
//如果需要打水印,则速率下降为10
|
||||
if golabl.Task.Header.ShopMsg.WatermarkImgUrl != "" && golabl.Task.Header.ShopType == "1" {
|
||||
speed = golabl.Config.Speed.Watermark
|
||||
if speed == 0 {
|
||||
speed = 10
|
||||
}
|
||||
}
|
||||
//初始化限速器
|
||||
golabl.Speed = rate.NewLimiter(rate.Limit(speed), 1)
|
||||
}
|
||||
20
planB/initialization/task/task.go
Normal file
20
planB/initialization/task/task.go
Normal file
@ -0,0 +1,20 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/service"
|
||||
)
|
||||
|
||||
// GetTaskHeaderAndFooterSetToG 获取任务头和尾并保存到全局变量中
|
||||
// @return error 错误信息
|
||||
func GetTaskHeaderAndFooterSetToG() error {
|
||||
// 获取任务头
|
||||
if err := service.GetTaskHeader(); err != nil {
|
||||
return fmt.Errorf("获取任务头失败 %v", err)
|
||||
}
|
||||
// 获取任务尾
|
||||
if err := service.GetTaskFooter(); err != nil {
|
||||
return fmt.Errorf("获取任务尾失败 %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
44
planB/initialization/taskType/taskType.go
Normal file
44
planB/initialization/taskType/taskType.go
Normal file
@ -0,0 +1,44 @@
|
||||
package taskType
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
)
|
||||
|
||||
// GetTaskTypeSetToG 获取任务类型并保存到全局变量中
|
||||
// @return error 错误信息
|
||||
func GetTaskTypeSetToG() error {
|
||||
switch golabl.Task.Header.TaskType {
|
||||
case 1: //核价发布
|
||||
golabl.TaskType = golabl.TaskTypeAddGoodsTask
|
||||
return nil
|
||||
case 2: //表格发布
|
||||
golabl.TaskType = golabl.TaskTypeAddGoodsTask
|
||||
return nil
|
||||
case 3: //获取商品
|
||||
golabl.TaskType = golabl.TaskTypeGetGoodsTask
|
||||
return nil
|
||||
case 4: //获取拼多多详情商品
|
||||
golabl.TaskType = golabl.TaskTypeGetGoodsTask
|
||||
return nil
|
||||
case 5: //操作商品
|
||||
golabl.TaskType = golabl.TaskTypeOperationGoodsTask
|
||||
return nil
|
||||
case 6: //核价表格发布
|
||||
golabl.TaskType = golabl.TaskTypeAddGoodsTask
|
||||
return nil
|
||||
case 7: //增量库存
|
||||
golabl.TaskType = golabl.TaskTypeIncStock
|
||||
return nil
|
||||
case 8: //自营书品发布
|
||||
golabl.TaskType = golabl.TaskTypeAddGoodsTask
|
||||
return nil
|
||||
case 9: //核价改价
|
||||
golabl.TaskType = golabl.TaskTypeOperationGoodsTask
|
||||
return nil
|
||||
default:
|
||||
fmt.Println(golabl.Task.Header.TaskType)
|
||||
return errors.New("错误!")
|
||||
}
|
||||
}
|
||||
82
planB/initialization/title/title.go
Normal file
82
planB/initialization/title/title.go
Normal file
@ -0,0 +1,82 @@
|
||||
package title
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization/golabl"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetWinTitle 设置窗口标题
|
||||
func SetWinTitle() {
|
||||
title := ""
|
||||
|
||||
//平台
|
||||
switch golabl.Task.Header.ShopType {
|
||||
case "1":
|
||||
title = title + "【拼多多】"
|
||||
case "2":
|
||||
title = title + "【孔夫子】"
|
||||
case "5":
|
||||
title = title + "【闲鱼】"
|
||||
default:
|
||||
title = title + "【其他平台 " + golabl.Task.Header.ShopType + "】"
|
||||
}
|
||||
|
||||
//店铺名称
|
||||
title = title + "【" + golabl.Task.Header.ShopName + "】"
|
||||
|
||||
//任务类型
|
||||
switch golabl.Task.Header.TaskType {
|
||||
case 1:
|
||||
title = title + "【核价发布】"
|
||||
case 2:
|
||||
title = title + "【表格发布】"
|
||||
case 3:
|
||||
title = title + "【拉取商品】"
|
||||
case 4:
|
||||
title = title + "【拉取商品详情】"
|
||||
case 5:
|
||||
title = title + "【操作商品】"
|
||||
case 6:
|
||||
title = title + "【核价表格发布】"
|
||||
case 7:
|
||||
title = title + "【增量库存】"
|
||||
default:
|
||||
title = title + "【其他任务类型 " + fmt.Sprint(golabl.Task.Header.TaskType) + "】"
|
||||
}
|
||||
|
||||
//图片类型
|
||||
switch golabl.Task.Header.ImgType {
|
||||
case 1:
|
||||
title = title + "【仅官图】"
|
||||
case 2:
|
||||
title = title + "【实拍图】"
|
||||
case 3:
|
||||
title = title + "【优先官图】"
|
||||
case 4:
|
||||
title = title + "【优先实拍图】"
|
||||
default:
|
||||
title = title + "【其他图片类型 " + fmt.Sprint(golabl.Task.Header.ImgType) + "】"
|
||||
}
|
||||
|
||||
//创建时间
|
||||
createTime := time.Unix(golabl.Task.Header.TaskCreateAt, 0)
|
||||
timeStr := createTime.Format("2006-01-02 15:04:05")
|
||||
title = title + "【创建时间 " + timeStr + "】"
|
||||
|
||||
//任务 id
|
||||
title = title + golabl.Task.Header.TaskId
|
||||
setConsoleTitle(title)
|
||||
}
|
||||
|
||||
// SetConsoleTitle 设置窗口标题
|
||||
// @param title 标题
|
||||
func setConsoleTitle(title string) {
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
procSetConsoleTitle := kernel32.NewProc("SetConsoleTitleW")
|
||||
// 将字符串转换为UTF-16指针
|
||||
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
||||
procSetConsoleTitle.Call(uintptr(unsafe.Pointer(titlePtr)))
|
||||
}
|
||||
23
planB/interfaces/interfaces.go
Normal file
23
planB/interfaces/interfaces.go
Normal file
@ -0,0 +1,23 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
planAType "planA/type"
|
||||
)
|
||||
|
||||
// GoodsTask 商品任务接口
|
||||
type GoodsTask interface {
|
||||
// AddGoodsTask 添加商品任务
|
||||
AddGoodsTask(bodyWait planAType.TaskBody) (string, error)
|
||||
|
||||
// SetGoodsTask 设置商品任务
|
||||
SetGoodsTask() string
|
||||
|
||||
// GetGoodsTask 获取商品任务
|
||||
GetGoodsTask() (string, error)
|
||||
|
||||
// OperationGoodsTask 操作商品任务
|
||||
OperationGoodsTask(bodyWait planAType.TaskBody) (string, error)
|
||||
|
||||
// IncStockTask 增量库存
|
||||
IncStockTask(bodyWait planAType.TaskBody) (string, error)
|
||||
}
|
||||
410
planB/logic/logic.go
Normal file
410
planB/logic/logic.go
Normal file
@ -0,0 +1,410 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"planA/planB/dispatcher"
|
||||
"planA/planB/initialization/config"
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/initialization/task"
|
||||
"planA/planB/modules/logs"
|
||||
"planA/planB/service"
|
||||
"planA/planB/tool"
|
||||
planAType "planA/type"
|
||||
planATypeMysql "planA/type/mysql"
|
||||
redisType "planA/type/redis"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var Goto bool = false
|
||||
|
||||
// Logic 执行任务
|
||||
func Logic() {
|
||||
//loop:
|
||||
// 开始读取待处理任务 等待任务数必须大于0
|
||||
for golabl.Task.Footer.TaskCountWait.Load() > 0 {
|
||||
// 任务索引
|
||||
atomic.AddInt64(&golabl.Logic.TaskIndex, 1)
|
||||
|
||||
//TODO 在更新config方法出去后应该去除该代码 每次重新获取配置文件
|
||||
if configErr := config.GetConfigSetToG(); configErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, configErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 使用令牌桶进行速率控制(每秒20个)
|
||||
if err := golabl.Speed.Wait(golabl.Ctx); err != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("令牌桶等待失败-原因来自于:%v", err))
|
||||
continue
|
||||
}
|
||||
|
||||
//TODO 重新获取任务头尾
|
||||
if taskErr := task.GetTaskHeaderAndFooterSetToG(); taskErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, taskErr.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果连续读出 redisNil 的次数大于100
|
||||
if atomic.LoadInt64(&golabl.Logic.RedisNilCon) > 100 {
|
||||
//Goto = true
|
||||
|
||||
// 等待所有任务完成 暂停 5 秒
|
||||
golabl.Pool.Wg.Wait()
|
||||
fmt.Println("等待当前所有协程完成后 暂停5秒,如果等待的任务真的是0的话,则通知A完成任务!")
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
//获取任务真实的 wait数量
|
||||
count, getTaskBodyWaitCountErr := service.GetTaskBodyWaitCount()
|
||||
if getTaskBodyWaitCountErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取任务任务真实的 wait数量失败-原因来自:%v", getTaskBodyWaitCountErr))
|
||||
return
|
||||
}
|
||||
// 如果数量真的是0,则完成任务
|
||||
if count == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
atomic.StoreInt64(&golabl.Logic.RedisNilCon, 0)
|
||||
}
|
||||
|
||||
// 创建等待
|
||||
golabl.Pool.Wg.Add(1)
|
||||
|
||||
//协程池 提交
|
||||
if golabl.Task.Header.TaskType == 7 {
|
||||
// 单线程执行
|
||||
taskExecute()
|
||||
if taskPoolErr := golabl.Pool.Pool.Submit(taskExecute); taskPoolErr != nil {
|
||||
golabl.Pool.Wg.Done()
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("协程池意外-原因来自:%d", taskPoolErr))
|
||||
} else {
|
||||
golabl.Pool.Wg.Done()
|
||||
}
|
||||
} else {
|
||||
// 多线程执行
|
||||
if taskPoolErr := golabl.Pool.Pool.Submit(func() {
|
||||
defer golabl.Pool.Wg.Done()
|
||||
taskExecute()
|
||||
}); taskPoolErr != nil {
|
||||
golabl.Pool.Wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// 判断 任务数是否超过1000 并且 判断是否执行到了1000的倍数
|
||||
if golabl.Task.Header.TaskCountTrue > 1000 && golabl.Logic.TaskIndex%1000 == 0 {
|
||||
// 更新任务头部信息
|
||||
updateTaskHeaderErr := tool.UpdateTaskHeader()
|
||||
if updateTaskHeaderErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("更新任务头信息失败-原因来自:%v", updateTaskHeaderErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有任务完成
|
||||
golabl.Pool.Wg.Wait()
|
||||
|
||||
//等待指定时间后重新执行循环
|
||||
//if Goto == true {
|
||||
// golabl.Logic.RedisNilCon = 0
|
||||
// golabl.Logic.LastIndex = golabl.LastIndexRedisNil
|
||||
// fmt.Printf("连续读出 redisNil 的次数 %v 暂停%v毫秒", golabl.Logic.RedisNilCon, golabl.Config.Server.ErrPauseTime)
|
||||
// time.Sleep(time.Duration(golabl.Config.Server.ErrPauseTime) * time.Millisecond)
|
||||
// goto loop
|
||||
//}
|
||||
|
||||
// 更新任务头部信息
|
||||
if updateTaskHeaderErr := tool.UpdateTaskHeader(); updateTaskHeaderErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("更新任务头信息失败-原因来自:%v", updateTaskHeaderErr))
|
||||
}
|
||||
|
||||
// 通知 A程序任务完成
|
||||
httpTaskStatusOverErr := tool.NotifyA()
|
||||
if httpTaskStatusOverErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, httpTaskStatusOverErr.Error())
|
||||
}
|
||||
|
||||
// 延迟2分钟
|
||||
time.Sleep(2 * time.Minute)
|
||||
}
|
||||
|
||||
// 任务执行
|
||||
func taskExecute() {
|
||||
//初始化 变量
|
||||
status := golabl.BodyStatusSuccess //默认的书籍执行状态·
|
||||
errorStr := "执行成功" //默认的书籍执行描述
|
||||
|
||||
// 获取任务信息
|
||||
taskMsg, taskMsgErr := service.GetTaskToPopFromBodyWait()
|
||||
|
||||
if errors.Is(taskMsgErr, redis.Nil) {
|
||||
//redis 读nil空+1
|
||||
fmt.Printf("第 %v 次读出 Redis Nil \n", atomic.LoadInt64(&golabl.Logic.RedisNilCon))
|
||||
atomic.AddInt64(&golabl.Logic.RedisNilCon, 1)
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取任务信息失败-原因来自:%v", taskMsgErr))
|
||||
return
|
||||
} else if taskMsgErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取任务信息失败-原因来自:%v", taskMsgErr))
|
||||
return
|
||||
}
|
||||
|
||||
//设置混合任务成功状态
|
||||
if golabl.Task.Header.TaskType == 5 || golabl.Task.Header.TaskType == 9 {
|
||||
switch taskMsg.Detail.Status {
|
||||
case 1:
|
||||
errorStr = "设置商品上架 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
case 2:
|
||||
errorStr = "设置商品下架 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
case 3:
|
||||
//删除商品的任务存储到 mysql中
|
||||
//删除商品 {"book_info":{"isbn":"9787543982888"},"detail":{"goods_id":935670364385,"status":3}}
|
||||
DelTask(taskMsg)
|
||||
errorStr = "删除商品 已转转移至删除中心"
|
||||
case 4:
|
||||
errorStr = "修改商品库存 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
case 5:
|
||||
errorStr = "修改商品价格 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
case 6:
|
||||
errorStr = "发布商品 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
case 7:
|
||||
errorStr = "删除并重新发布 " + errorStr
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
|
||||
default:
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
}
|
||||
// 更新任务信息
|
||||
taskMsg.Detail.Status = status
|
||||
taskMsg.Detail.Error = errorStr
|
||||
} else if golabl.Task.Header.TaskType == 7 {
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
taskMsg.Detail.Status = status
|
||||
if status != 1 {
|
||||
taskMsg.Detail.Error = errorStr
|
||||
}
|
||||
} else {
|
||||
//执行任务
|
||||
status, errorStr, taskMsg = exeTask(taskMsg, status, errorStr)
|
||||
// 更新任务信息
|
||||
taskMsg.Detail.Status = status
|
||||
taskMsg.Detail.Error = errorStr
|
||||
}
|
||||
|
||||
//isbn 不为空的添加到body中,比如拉取店铺商品信息isbn可以返回空的
|
||||
if taskMsg.BookInfo.Isbn != "" && (golabl.TaskType == "3" || golabl.TaskType == "4") {
|
||||
// 添加任务到bodyOver、bodyData、bodyBackup
|
||||
if addTaskToBodyOverErr := service.AddTaskToBodyOver(taskMsg, []string{}); addTaskToBodyOverErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("任务失败 添加到BodyOver失败-原因:%v", addTaskToBodyOverErr))
|
||||
}
|
||||
} else {
|
||||
if taskMsg.BookInfo.Isbn == "" && taskMsg.BookInfo.BookName == "" {
|
||||
taskMsg.BookInfo.BookName = "暂无书品信息"
|
||||
}
|
||||
// 添加任务到bodyOver、bodyData、bodyBackup
|
||||
if addTaskToBodyOverErr := service.AddTaskToBodyOver(taskMsg, []string{}); addTaskToBodyOverErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("任务失败 添加到BodyOver失败-原因:%v", addTaskToBodyOverErr))
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 footer信息
|
||||
if updateTaskFooterErr := service.UpdateTaskFooter(status, 1); updateTaskFooterErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("任务失败 添加到BodyOver失败-原因:%v", updateTaskFooterErr))
|
||||
}
|
||||
|
||||
// 如果错误是 店铺商品发布达到上限则暂停程序
|
||||
if strings.Contains(errorStr, "店铺内发布商品总数已达到上限") {
|
||||
golabl.Task.Header.LastIndex = golabl.LastIndexGoodsMaxRestriction
|
||||
//暂停 B程序运行
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "任务失败 添加到BodyOver失败-原因:店铺内发布商品总数已达到上限")
|
||||
pauseTaskErr := tool.PauseTask()
|
||||
if pauseTaskErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, "任务失败 添加到BodyOver失败-原因:店铺内发布商品总数已达到上限")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(errorStr)
|
||||
}
|
||||
|
||||
//****************************工具**************************************//
|
||||
|
||||
// parseShopData 解析店铺数据
|
||||
// @param shopData 店铺数据
|
||||
// @return *_type.ShopInfo 店铺信息
|
||||
func parseShopData(shopData string) (*planAType.ShopInfo, error) {
|
||||
shopData = strings.TrimSpace(shopData)
|
||||
|
||||
// 直接解析为 RedisData数组
|
||||
var redisData []redisType.RedisData
|
||||
err := json.Unmarshal([]byte(shopData), &redisData)
|
||||
if err != nil {
|
||||
// 尝试另一种格式:可能是单对象而不是数组
|
||||
var singleData redisType.RedisData
|
||||
if singleErr := json.Unmarshal([]byte(shopData), &singleData); singleErr == nil {
|
||||
redisData = []redisType.RedisData{singleData}
|
||||
} else {
|
||||
return nil, fmt.Errorf("JSON解析失败: %v, 原始数据: %s", err, shopData[:min(100, len(shopData))])
|
||||
}
|
||||
}
|
||||
|
||||
shopInfo := &planAType.ShopInfo{}
|
||||
|
||||
// 遍历所有数据,根据source_table分类
|
||||
for _, item := range redisData {
|
||||
switch item.SourceTable {
|
||||
case "t_shop":
|
||||
var shop planAType.Shop
|
||||
if err := json.Unmarshal(item.Data, &shop); err == nil {
|
||||
shopInfo.Shop = &shop
|
||||
} else {
|
||||
fmt.Printf("解析t_shop失败: %v\n", err)
|
||||
}
|
||||
case "t_shop_detail":
|
||||
var detail planAType.ShopDetail
|
||||
if err := json.Unmarshal(item.Data, &detail); err == nil {
|
||||
shopInfo.ShopDetail = &detail
|
||||
} else {
|
||||
fmt.Printf("解析t_shop_detail失败: %v\n", err)
|
||||
}
|
||||
case "t_shop_context":
|
||||
var context planAType.ShopContext
|
||||
if err := json.Unmarshal(item.Data, &context); err == nil {
|
||||
shopInfo.ShopContext = &context
|
||||
} else {
|
||||
fmt.Printf("解析t_shop_context失败: %v\n", err)
|
||||
}
|
||||
case "t_spec":
|
||||
var spec planAType.Spec
|
||||
if err := json.Unmarshal(item.Data, &spec); err == nil {
|
||||
shopInfo.Spec = &spec
|
||||
} else {
|
||||
fmt.Printf("解析t_spec失败: %v\n", err)
|
||||
}
|
||||
case "t_price_template":
|
||||
var template planAType.PriceTemplate
|
||||
if err := json.Unmarshal(item.Data, &template); err == nil {
|
||||
shopInfo.PriceTemplate = &template
|
||||
} else {
|
||||
fmt.Printf("解析t_price_template失败: %v\n", err)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("未知的source_table: %s\n", item.SourceTable)
|
||||
}
|
||||
}
|
||||
|
||||
return shopInfo, nil
|
||||
}
|
||||
|
||||
// 调度任务
|
||||
func exeTask(taskMsg planAType.TaskBody, status int64, errorStr string) (int64, string, planAType.TaskBody) {
|
||||
// 任务调度
|
||||
bodyOverJson, err := dispatcher.Go(taskMsg)
|
||||
if err != nil {
|
||||
//任务调度失败
|
||||
status = golabl.BodyStatusError
|
||||
errorStr = fmt.Sprintf("任务调度失败-原因来自:%v", err)
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("任务调度失败-原因来自:%v", err))
|
||||
} else {
|
||||
//任务调度成功
|
||||
var bodyOver planAType.TaskBody
|
||||
unmarshalErr := json.Unmarshal([]byte(bodyOverJson), &bodyOver)
|
||||
if unmarshalErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("bodyOver json.Unmarshal错误-原因:%v", unmarshalErr))
|
||||
}
|
||||
//更新 taskMsg
|
||||
taskMsg = bodyOver
|
||||
}
|
||||
return status, errorStr, taskMsg
|
||||
}
|
||||
|
||||
// DelTask 删除任务
|
||||
func DelTask(taskMsg planAType.TaskBody) {
|
||||
//删除商品的任务存储到 mysql中
|
||||
//删除商品 {"book_info":{"isbn":"9787543982888"},"detail":{"goods_id":935670364385,"status":3}}
|
||||
delTask, isExistDelTask, delTaskErr := service.GetDelTaskByTaskId()
|
||||
if !isExistDelTask && delTaskErr == nil {
|
||||
taskCount := 0
|
||||
taskCountOver := 0
|
||||
sta := 0
|
||||
//将header 转为json
|
||||
headerByte, headerJsonErr := json.Marshal(golabl.Task.Header)
|
||||
if headerJsonErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("将header 转为json失败-原因来自:%v", headerJsonErr))
|
||||
return
|
||||
}
|
||||
headerJson := string(headerByte)
|
||||
currentTime := time.Now()
|
||||
// 查询店铺数据
|
||||
shopDataStr, getTaskShopErr := service.GetTaskShop(golabl.Task.Header.ShopId)
|
||||
if getTaskShopErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("查询店铺数据失败:%v", headerJsonErr))
|
||||
return
|
||||
}
|
||||
// 解析 json数据
|
||||
shopData, parseShopDataErr := parseShopData(shopDataStr)
|
||||
if parseShopDataErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("解析店铺数据失败:%v", parseShopDataErr))
|
||||
return
|
||||
}
|
||||
userId := shopData.Shop.CreateBy
|
||||
taskType := 1
|
||||
//不存在 mysql任务则创建
|
||||
createDelTask := planATypeMysql.DelTask{
|
||||
UserID: &userId,
|
||||
ShopID: &golabl.Task.Header.ShopId,
|
||||
TaskID: &golabl.Task.Header.TaskId,
|
||||
ShopName: &golabl.Task.Header.ShopName,
|
||||
ShopType: &shopData.Shop.ShopType,
|
||||
TaskCount: &taskCount,
|
||||
TaskCountOver: &taskCountOver,
|
||||
Status: &sta,
|
||||
TaskType: &taskType,
|
||||
Header: &headerJson,
|
||||
CreateAt: ¤tTime,
|
||||
}
|
||||
var err error
|
||||
delTask, err = service.CreateDelTask(createDelTask)
|
||||
if err != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("创建删除任务失败-原因来自:%v", err))
|
||||
return
|
||||
}
|
||||
} else if delTaskErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("获取删除任务失败-原因来自:%v", delTaskErr))
|
||||
return
|
||||
}
|
||||
|
||||
//将任务状态修改为执行中
|
||||
updateDelTaskStatusToDoingErr := service.UpdateDelTaskStatusToDoing()
|
||||
if updateDelTaskStatusToDoingErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("将删除任务状态修改为执行中失败-原因来自:%v", updateDelTaskStatusToDoingErr))
|
||||
return
|
||||
}
|
||||
// 将明细的删除任务转移到 mysql中
|
||||
insertDelTaskDetailErr := service.InsertDelTaskDetail(delTask.ID, taskMsg)
|
||||
if insertDelTaskDetailErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("将明细的删除任务转移到 mysql中失败-原因来自:%v", insertDelTaskDetailErr))
|
||||
return
|
||||
}
|
||||
// 添加删除任务数量
|
||||
addDelTaskDetailCountErr := service.AddDelTaskDetailCount()
|
||||
if addDelTaskDetailCountErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, fmt.Sprintf("添加删除任务数量失败-原因来自:%v", addDelTaskDetailCountErr))
|
||||
return
|
||||
}
|
||||
}
|
||||
76
planB/main.go
Normal file
76
planB/main.go
Normal file
@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"planA/planB/initialization"
|
||||
"planA/planB/initialization/golabl"
|
||||
"planA/planB/logic"
|
||||
"planA/planB/modules/logs"
|
||||
"planA/planB/tool"
|
||||
"planA/planB/validation"
|
||||
//"planA/planB/initialization"
|
||||
//"planA/planB/initialization/golabl"
|
||||
//"planA/planB/logic"
|
||||
//"planA/planB/modules/logs"
|
||||
//"planA/planB/tool"
|
||||
//"planA/planB/validation"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//校验参数
|
||||
taskId, validationErr := validation.Validation()
|
||||
if validationErr != nil {
|
||||
fmt.Println(validationErr)
|
||||
return
|
||||
}
|
||||
|
||||
// 是否测试模式
|
||||
if taskId == "111" {
|
||||
//test()
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化配置
|
||||
err := initialization.Init(taskId)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 拉取商品列表与拼多多商品详情列表
|
||||
if golabl.Task.Header.TaskType == 3 || (golabl.Task.Header.TaskType == 4 && golabl.Task.Header.ShopType == "1") {
|
||||
_, getGoodsTask := golabl.Platform.GetGoodsTask()
|
||||
if getGoodsTask != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, getGoodsTask.Error())
|
||||
}
|
||||
// 通知 A程序任务完成
|
||||
httpTaskStatusOverErr := tool.NotifyA()
|
||||
if httpTaskStatusOverErr != nil {
|
||||
tool.LoggingMiddleware(logs.LOG_LEVEL_ERROR, httpTaskStatusOverErr.Error())
|
||||
}
|
||||
//延迟3分钟,并且循环打印每秒倒计时
|
||||
totalSeconds := 180 // 3分钟 = 180秒
|
||||
for i := totalSeconds; i >= 0; i-- {
|
||||
minutes := i / 60
|
||||
seconds := i % 60
|
||||
fmt.Printf("\r剩余时间: %02d:%02d", minutes, seconds)
|
||||
if i > 0 {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 执行任务
|
||||
logic.Logic()
|
||||
}
|
||||
}
|
||||
|
||||
// 测试模式
|
||||
func test() {
|
||||
//循环1000次
|
||||
for i := 0; i < 1000; i++ {
|
||||
//每秒打印 i
|
||||
fmt.Printf("i:%v\n", i)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
BIN
planB/modules/config/config.dll
Normal file
BIN
planB/modules/config/config.dll
Normal file
Binary file not shown.
74
planB/modules/config/conifg.go
Normal file
74
planB/modules/config/conifg.go
Normal file
@ -0,0 +1,74 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ConfigDLL 配置文件读取DLL结构
|
||||
type ConfigDLL struct {
|
||||
dll *syscall.DLL
|
||||
readConfigFile *syscall.Proc // 读取配置文件
|
||||
getVersion *syscall.Proc // 获取版本信息
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// InitConfigDLL 初始化ConfigDLL
|
||||
func InitConfigDLL() (*ConfigDLL, error) {
|
||||
dllPath := filepath.Join("modules/config/", "config.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("config DLL 不存在: %s", dllPath)
|
||||
}
|
||||
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||
return nil, fmt.Errorf("加载config DLL 失败: %s", err)
|
||||
} else {
|
||||
return &ConfigDLL{
|
||||
dll: dll,
|
||||
readConfigFile: dll.MustFindProc("ReadConfigFile"),
|
||||
getVersion: dll.MustFindProc("GetVersion"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// cStr 获取C字符串
|
||||
func (m *ConfigDLL) cStr(p uintptr) string {
|
||||
if p == 0 {
|
||||
return ""
|
||||
}
|
||||
b := []byte{}
|
||||
for i := uintptr(0); ; i++ {
|
||||
c := *(*byte)(unsafe.Pointer(p + i))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
}
|
||||
s := string(b)
|
||||
if m.freeCString != nil {
|
||||
m.freeCString.Call(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ReadConfigFile 读取配置文件
|
||||
func (m *ConfigDLL) ReadConfigFile(filePath, fileName string) (string, error) {
|
||||
proc, err := m.dll.FindProc("ReadConfigFile")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ReadConfigFile: %v", err)
|
||||
}
|
||||
|
||||
filePathPtr, _ := syscall.BytePtrFromString(filePath)
|
||||
fileNamePtr, _ := syscall.BytePtrFromString(fileName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(filePathPtr)),
|
||||
uintptr(unsafe.Pointer(fileNamePtr)),
|
||||
)
|
||||
|
||||
result := m.cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
BIN
planB/modules/image/image.dll
Normal file
BIN
planB/modules/image/image.dll
Normal file
Binary file not shown.
141
planB/modules/image/image.go
Normal file
141
planB/modules/image/image.go
Normal file
@ -0,0 +1,141 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gImageDll *ImageDLL
|
||||
|
||||
// Windows API - 使用 C 运行时库
|
||||
libc = syscall.NewLazyDLL("msvcrt.dll")
|
||||
procFree = libc.NewProc("free")
|
||||
procMalloc = libc.NewProc("malloc")
|
||||
)
|
||||
|
||||
// ImageDLL 图片工具DLL结构
|
||||
type ImageDLL struct {
|
||||
Dll *syscall.DLL
|
||||
AddWatermarkFromURLEx *syscall.Proc // 打水印
|
||||
}
|
||||
|
||||
// InitImageDll 初始化 imageDLL
|
||||
func InitImageDll(url string) (*ImageDLL, error) {
|
||||
dllPath := filepath.Join(url, "image.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("Image DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载Image DLL 失败: %s", err)
|
||||
}
|
||||
gImageDll = &ImageDLL{
|
||||
Dll: dll,
|
||||
AddWatermarkFromURLEx: dll.MustFindProc("AddWatermarkFromURLEx"),
|
||||
}
|
||||
return gImageDll, nil
|
||||
}
|
||||
|
||||
// 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轴偏移量
|
||||
YOffset int // Y轴偏移量
|
||||
Timeout int // 下载超时时间(秒),默认30秒
|
||||
OutputFormat string // 输出格式: "jpeg", "png", "auto"(默认auto,根据源图片格式)auto
|
||||
JPEGQuality int // JPEG质量 (1-100),默认95
|
||||
TargetWidth int // 目标宽度(0表示不缩放)
|
||||
TargetHeight int // 目标高度(0表示不缩放)
|
||||
ResizeMode string // 缩放模式: "fit"(适应,保持比例,可能有黑边), "fill"(填充,裁剪), "stretch"(拉伸)
|
||||
}
|
||||
|
||||
// AddWatermarkFromURLExs 添加水印
|
||||
func (m *ImageDLL) AddWatermarkFromURLExs(sourceImageUrl, watermarkUrl string) (string, error) {
|
||||
watermarkConfig := WatermarkConfig{
|
||||
SourceImageURL: sourceImageUrl,
|
||||
WatermarkBase64: watermarkUrl,
|
||||
Position: "center",
|
||||
Opacity: 1.0,
|
||||
Scale: 1.0,
|
||||
TileSpacing: 50,
|
||||
Timeout: 30,
|
||||
OutputFormat: "jpeg",
|
||||
JPEGQuality: 95,
|
||||
TargetWidth: 800,
|
||||
TargetHeight: 800,
|
||||
ResizeMode: "fit",
|
||||
}
|
||||
watermarkConfigJson, err := json.Marshal(watermarkConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||||
}
|
||||
|
||||
proc, err := m.Dll.FindProc("AddWatermarkFromURLEx")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 AddWatermarkFromURLEx: %v", err)
|
||||
}
|
||||
|
||||
// 分配内存并确保释放
|
||||
jsonStr := string(watermarkConfigJson)
|
||||
jsonPtr := cString(jsonStr)
|
||||
defer freeCString(jsonPtr)
|
||||
|
||||
// 调用 DLL 函数
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(jsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cString 分配 C 字符串(使用 malloc)
|
||||
func cString(str string) unsafe.Pointer {
|
||||
// 计算需要的内存大小
|
||||
size := len(str) + 1
|
||||
ptr, _, _ := procMalloc.Call(uintptr(size))
|
||||
if ptr == 0 {
|
||||
return nil
|
||||
}
|
||||
// 复制字符串内容
|
||||
for i := 0; i < len(str); i++ {
|
||||
*(*byte)(unsafe.Pointer(ptr + uintptr(i))) = str[i]
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(ptr + uintptr(len(str)))) = 0 // 结尾加 \0
|
||||
return unsafe.Pointer(ptr)
|
||||
}
|
||||
|
||||
// freeCString 释放 C 字符串
|
||||
func freeCString(ptr unsafe.Pointer) {
|
||||
if ptr != nil {
|
||||
procFree.Call(uintptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
BIN
planB/modules/kfz/kfz.dll
Normal file
BIN
planB/modules/kfz/kfz.dll
Normal file
Binary file not shown.
248
planB/modules/kfz/kfz.go
Normal file
248
planB/modules/kfz/kfz.go
Normal file
@ -0,0 +1,248 @@
|
||||
package kfz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// KfzDLL 孔夫子工具DLL结构
|
||||
type KfzDLL struct {
|
||||
Dll *syscall.DLL
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// InitKfzDll 初始化 kfzDLL
|
||||
func InitKfzDll(url string) (*KfzDLL, error) {
|
||||
dllPath := filepath.Join(url, "kfz.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("kfz DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
|
||||
}
|
||||
gKfzDll := &KfzDLL{
|
||||
Dll: dll,
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}
|
||||
return gKfzDll, nil
|
||||
}
|
||||
|
||||
// PublishGoods 发布商品
|
||||
func (m *KfzDLL) PublishGoods(appId int, clientSecret, accessToken, goodsAddJson string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemAdd")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemAdd: %v", err)
|
||||
}
|
||||
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsAddJsonPtr, _ := syscall.BytePtrFromString(goodsAddJson)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsAddJsonPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// KfzGoodsImageUpload 将图片上传到孔夫子图片空间
|
||||
func (m *KfzDLL) KfzGoodsImageUpload(appId int, clientSecret, accessToken, filePath string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzImageUpload")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzImageUpload: %v", err)
|
||||
}
|
||||
//appIdPtr, _ := syscall.BytePtrFromString(appId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsAddJsonPtr, _ := syscall.BytePtrFromString(filePath)
|
||||
savePathPtr, _ := syscall.BytePtrFromString("")
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsAddJsonPtr)),
|
||||
uintptr(unsafe.Pointer(savePathPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetGoodsCategoryList 获取本店商品分类列表
|
||||
func (m *KfzDLL) GetGoodsCategoryList(appId int, clientSecret, accessToken string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopCategoryNameList")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopCategoryNameList: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetCommonCategory 获取公用分类数据
|
||||
func (m *KfzDLL) GetCommonCategory(appId int, clientSecret, accessToken string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzCommonCategory")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzCommonCategory: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetGoodsList 获取商品列表
|
||||
func (m *KfzDLL) GetGoodsList(appId int, clientSecret, accessToken, getGoodsListReqJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemList")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemList: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
getGoodsListReqJsonPtr, _ := syscall.BytePtrFromString(getGoodsListReqJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(getGoodsListReqJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PutOnSale 上架
|
||||
func (m *KfzDLL) PutOnSale(appId int, clientSecret, accessToken, putOnSaleJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemListing")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemListing: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
putOnSaleJsonPtr, _ := syscall.BytePtrFromString(putOnSaleJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(putOnSaleJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PutOffSale 下架
|
||||
func (m *KfzDLL) PutOffSale(appId int, clientSecret, accessToken, putOffSaleJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemDelisting")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemDelisting: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
putOffSaleJsonPtr, _ := syscall.BytePtrFromString(putOffSaleJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(putOffSaleJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateGoodsStock 修改商品库存
|
||||
func (m *KfzDLL) UpdateGoodsStock(appId int, clientSecret, accessToken, updateGoodsStockJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemNumberUpdate")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemNumberUpdate: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
updateGoodsStockJsonPtr, _ := syscall.BytePtrFromString(updateGoodsStockJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(updateGoodsStockJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateGoodsPrice 修改商品价格
|
||||
func (m *KfzDLL) UpdateGoodsPrice(appId int, clientSecret, accessToken, updateGoodsPriceJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemPriceUpdate")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemPriceUpdate: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
updateGoodsPriceJsonPtr, _ := syscall.BytePtrFromString(updateGoodsPriceJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(updateGoodsPriceJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DeleteGoods 删除商品
|
||||
func (m *KfzDLL) DeleteGoods(appId int, clientSecret, accessToken, deleteGoodsJson string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("KongfzShopItemDelete")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 KongfzShopItemDelete: %v", err)
|
||||
}
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
deleteGoodsJsonPtr, _ := syscall.BytePtrFromString(deleteGoodsJson)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(appId),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(deleteGoodsJsonPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
322
planB/modules/logs/dll.go
Normal file
322
planB/modules/logs/dll.go
Normal file
@ -0,0 +1,322 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
LOG_LEVEL_DEBUG = "DEBUG"
|
||||
LOG_LEVEL_INFO = "INFO"
|
||||
LOG_LEVEL_WARNING = "WARNING"
|
||||
LOG_LEVEL_ERROR = "ERROR"
|
||||
LOG_LEVEL_SUCCESS = "SUCCESS"
|
||||
)
|
||||
|
||||
// LoggerDLL 封装 logger.dll 操作
|
||||
type LoggerDLL struct {
|
||||
dll *syscall.LazyDLL
|
||||
createLogger *syscall.LazyProc
|
||||
createContext *syscall.LazyProc
|
||||
logInfo *syscall.LazyProc
|
||||
logError *syscall.LazyProc
|
||||
logWarning *syscall.LazyProc
|
||||
logSuccess *syscall.LazyProc
|
||||
freeString *syscall.LazyProc
|
||||
closeAllLoggers *syscall.LazyProc
|
||||
}
|
||||
|
||||
// LoggerConfig logger配置结构
|
||||
type LoggerConfig struct {
|
||||
LogDir string `json:"log_dir"`
|
||||
SplitType int `json:"split_type"`
|
||||
RotateType int `json:"rotate_type"`
|
||||
MaxSize int64 `json:"max_size"`
|
||||
MaxCount int `json:"max_count"`
|
||||
Level int `json:"level"`
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
DefaultTaskType string `json:"default_task_type"`
|
||||
}
|
||||
|
||||
var loggerDLLInstance *LoggerDLL
|
||||
var loggerHandle string
|
||||
var loggerContextHandle string
|
||||
|
||||
// EnsureLoggerDLL 确保logger DLL已加载
|
||||
func EnsureLoggerDLL(url string) (*LoggerDLL, error) {
|
||||
if loggerDLLInstance != nil {
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// 检查是否在Windows平台
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil, fmt.Errorf("logger DLL only supported on Windows platform")
|
||||
}
|
||||
|
||||
dllPath := filepath.Join(url, "logger.dll")
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
// 尝试从当前目录查找
|
||||
if _, err := os.Stat("logger.dll"); err == nil {
|
||||
dllPath = "logger.dll"
|
||||
} else {
|
||||
return nil, fmt.Errorf("logger DLL not found at %s", dllPath)
|
||||
}
|
||||
}
|
||||
|
||||
dll := syscall.NewLazyDLL(dllPath)
|
||||
|
||||
loggerDLLInstance = &LoggerDLL{
|
||||
dll: dll,
|
||||
createLogger: dll.NewProc("CreateLogger"),
|
||||
createContext: dll.NewProc("CreateContextWithTaskType"),
|
||||
logInfo: dll.NewProc("LogInfo"),
|
||||
logError: dll.NewProc("LogError"),
|
||||
logWarning: dll.NewProc("LogWarning"),
|
||||
logSuccess: dll.NewProc("LogSuccess"),
|
||||
freeString: dll.NewProc("FreeString"),
|
||||
closeAllLoggers: dll.NewProc("CloseAllLoggers"),
|
||||
}
|
||||
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// InitializeLogger 初始化logger
|
||||
func InitializeLogger(m *LoggerDLL, logDir string) error {
|
||||
|
||||
// 确保日志目录存在
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("创建日志目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建 logger配置
|
||||
config := LoggerConfig{
|
||||
LogDir: logDir,
|
||||
SplitType: 2, // SplitByDay
|
||||
RotateType: 0, // RotateBySize
|
||||
MaxSize: 100 * 1024 * 1024, // 100MB
|
||||
MaxCount: 10,
|
||||
Level: 1, // LevelInfo - 只显示INFO及以上级别的日志
|
||||
EnableCaller: true,
|
||||
DefaultTaskType: "main",
|
||||
}
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 调用CreateLogger
|
||||
configPtr, _ := syscall.BytePtrFromString(string(configJSON))
|
||||
ret, _, _ := m.createLogger.Call(uintptr(unsafe.Pointer(configPtr)))
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger失败")
|
||||
}
|
||||
|
||||
// 获取logger句柄
|
||||
handle := cStr(ret)
|
||||
loggerHandle = handle
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
// 创建默认上下文
|
||||
return createLoggerContext(m, "main")
|
||||
}
|
||||
|
||||
// createLoggerContext 创建带任务类型的logger上下文
|
||||
func createLoggerContext(m *LoggerDLL, taskType string) error {
|
||||
|
||||
if loggerHandle == "" {
|
||||
return fmt.Errorf("logger未初始化")
|
||||
}
|
||||
|
||||
handlePtr, _ := syscall.BytePtrFromString(loggerHandle)
|
||||
taskTypePtr, _ := syscall.BytePtrFromString(taskType)
|
||||
|
||||
ret, _, _ := m.createContext.Call(
|
||||
uintptr(unsafe.Pointer(handlePtr)),
|
||||
uintptr(unsafe.Pointer(taskTypePtr)),
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger上下文失败")
|
||||
}
|
||||
|
||||
// 获取上下文句柄
|
||||
loggerContextHandle = cStr(ret)
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogTaskType 设置当前日志任务类型
|
||||
func SetLogTaskType(m *LoggerDLL, taskType string) error {
|
||||
return createLoggerContext(m, taskType)
|
||||
}
|
||||
|
||||
// LogInfo 记录信息日志
|
||||
func LogInfo(m *LoggerDLL, message string) error {
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logInfo.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogError 记录错误日志
|
||||
func LogError(m *LoggerDLL, message string) error {
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logError.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogWarning 记录警告日志
|
||||
func LogWarning(m *LoggerDLL, message string) error {
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logWarning.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogSuccess 记录成功日志
|
||||
func LogSuccess(m *LoggerDLL, message string) error {
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logSuccess.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseLogger 关闭logger
|
||||
func CloseLogger(m *LoggerDLL) error {
|
||||
ret, _, _ := m.closeAllLoggers.Call()
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("关闭logger失败")
|
||||
}
|
||||
|
||||
m.freeString.Call(ret)
|
||||
|
||||
loggerHandle = ""
|
||||
loggerContextHandle = ""
|
||||
loggerDLLInstance = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLoggerHandle 获取当前logger句柄(用于外部调用)
|
||||
func GetLoggerHandle() string {
|
||||
return loggerContextHandle
|
||||
}
|
||||
|
||||
// IsLoggerInitialized 检查logger是否已初始化
|
||||
func IsLoggerInitialized() bool {
|
||||
return loggerHandle != "" && loggerContextHandle != ""
|
||||
}
|
||||
|
||||
// SetConsoleOutput 设置控制台输出开关
|
||||
func SetConsoleOutput(enabled bool) {
|
||||
if enabled {
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
} else {
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithLevel 带级别的日志记录,可以精确控制显示
|
||||
func LogWithLevel(m *LoggerDLL, level, message string, showConsole bool) {
|
||||
if !IsLoggerInitialized() {
|
||||
return
|
||||
}
|
||||
|
||||
switch level {
|
||||
case "ERROR":
|
||||
LogError(m, message)
|
||||
case "WARNING":
|
||||
LogWarning(m, message)
|
||||
case "SUCCESS":
|
||||
LogSuccess(m, message)
|
||||
case "INFO":
|
||||
LogInfo(m, message)
|
||||
default:
|
||||
LogInfo(m, message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogOnlyFile 仅写入文件,不输出到控制台
|
||||
func LogOnlyFile(m *LoggerDLL, level, message string) {
|
||||
// 临时禁用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
LogWithLevel(m, level, message, false)
|
||||
}
|
||||
|
||||
// LogConsoleAndFile 同时输出到控制台和文件
|
||||
func LogConsoleAndFile(m *LoggerDLL, level, message string) {
|
||||
// 临时启用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
LogWithLevel(m, level, message, true)
|
||||
}
|
||||
BIN
planB/modules/logs/logger.dll
Normal file
BIN
planB/modules/logs/logger.dll
Normal file
Binary file not shown.
602
planB/modules/logs/logger.md
Normal file
602
planB/modules/logs/logger.md
Normal file
@ -0,0 +1,602 @@
|
||||
# logger.dll 使用教程
|
||||
## 1. 创建DLL工具实例
|
||||
### 加载DLL文件
|
||||
```gotemplate
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// LoggerDLL 封装 logger.dll 操作
|
||||
type LoggerDLL struct {
|
||||
dll *syscall.LazyDLL
|
||||
createLogger *syscall.LazyProc
|
||||
createContext *syscall.LazyProc
|
||||
logInfo *syscall.LazyProc
|
||||
logError *syscall.LazyProc
|
||||
logWarning *syscall.LazyProc
|
||||
logSuccess *syscall.LazyProc
|
||||
freeString *syscall.LazyProc
|
||||
closeAllLoggers *syscall.LazyProc
|
||||
}
|
||||
|
||||
// LoggerConfig logger配置结构
|
||||
type LoggerConfig struct {
|
||||
LogDir string `json:"log_dir"`
|
||||
SplitType int `json:"split_type"`
|
||||
RotateType int `json:"rotate_type"`
|
||||
MaxSize int64 `json:"max_size"`
|
||||
MaxCount int `json:"max_count"`
|
||||
Level int `json:"level"`
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
DefaultTaskType string `json:"default_task_type"`
|
||||
}
|
||||
|
||||
var loggerDLLInstance *LoggerDLL
|
||||
var loggerHandle string
|
||||
var loggerContextHandle string
|
||||
|
||||
// ensureLoggerDLL 确保logger DLL已加载
|
||||
func ensureLoggerDLL() (*LoggerDLL, error) {
|
||||
if loggerDLLInstance != nil {
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// 检查是否在Windows平台
|
||||
if runtime.GOOS != "windows" {
|
||||
return nil, fmt.Errorf("logger DLL only supported on Windows platform")
|
||||
}
|
||||
|
||||
// logger.dll 位于 dll/logger.dll
|
||||
//dllPath := filepath.Join("modules", "logs", "logger.dll")
|
||||
dllPath := "D:\\www\\wwwroot\\planA\\modules\\logs\\logger.dll"
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
// 尝试从当前目录查找
|
||||
if _, err := os.Stat("logger.dll"); err == nil {
|
||||
dllPath = "logger.dll"
|
||||
} else {
|
||||
return nil, fmt.Errorf("logger DLL not found at %s", dllPath)
|
||||
}
|
||||
}
|
||||
|
||||
dll := syscall.NewLazyDLL(dllPath)
|
||||
|
||||
loggerDLLInstance = &LoggerDLL{
|
||||
dll: dll,
|
||||
createLogger: dll.NewProc("CreateLogger"),
|
||||
createContext: dll.NewProc("CreateContextWithTaskType"),
|
||||
logInfo: dll.NewProc("LogInfo"),
|
||||
logError: dll.NewProc("LogError"),
|
||||
logWarning: dll.NewProc("LogWarning"),
|
||||
logSuccess: dll.NewProc("LogSuccess"),
|
||||
freeString: dll.NewProc("FreeString"),
|
||||
closeAllLoggers: dll.NewProc("CloseAllLoggers"),
|
||||
}
|
||||
|
||||
return loggerDLLInstance, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// InitializeLogger 初始化logger
|
||||
func InitializeLogger(logDir string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 确保日志目录存在
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("创建日志目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建logger配置
|
||||
config := LoggerConfig{
|
||||
LogDir: logDir,
|
||||
SplitType: 1, // SplitByDay
|
||||
RotateType: 0, // RotateBySize
|
||||
MaxSize: 100 * 1024 * 1024, // 100MB
|
||||
MaxCount: 10,
|
||||
Level: 1, // LevelInfo - 只显示INFO及以上级别的日志
|
||||
EnableCaller: true,
|
||||
DefaultTaskType: "main",
|
||||
}
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 调用CreateLogger
|
||||
configPtr, _ := syscall.BytePtrFromString(string(configJSON))
|
||||
ret, _, _ := m.createLogger.Call(uintptr(unsafe.Pointer(configPtr)))
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger失败")
|
||||
}
|
||||
|
||||
// 获取logger句柄
|
||||
handle := cStr(ret)
|
||||
loggerHandle = handle
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
// 创建默认上下文
|
||||
return createLoggerContext("main")
|
||||
}
|
||||
|
||||
// createLoggerContext 创建带任务类型的logger上下文
|
||||
func createLoggerContext(taskType string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerHandle == "" {
|
||||
return fmt.Errorf("logger未初始化")
|
||||
}
|
||||
|
||||
handlePtr, _ := syscall.BytePtrFromString(loggerHandle)
|
||||
taskTypePtr, _ := syscall.BytePtrFromString(taskType)
|
||||
|
||||
ret, _, _ := m.createContext.Call(
|
||||
uintptr(unsafe.Pointer(handlePtr)),
|
||||
uintptr(unsafe.Pointer(taskTypePtr)),
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("创建logger上下文失败")
|
||||
}
|
||||
|
||||
// 获取上下文句柄
|
||||
loggerContextHandle = cStr(ret)
|
||||
|
||||
// 释放返回的字符串
|
||||
m.freeString.Call(ret)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogTaskType 设置当前日志任务类型
|
||||
func SetLogTaskType(taskType string) error {
|
||||
return createLoggerContext(taskType)
|
||||
}
|
||||
|
||||
// LogInfo 记录信息日志
|
||||
func LogInfo(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logInfo.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogError 记录错误日志
|
||||
func LogError(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logError.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogWarning 记录警告日志
|
||||
func LogWarning(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logWarning.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogSuccess 记录成功日志
|
||||
func LogSuccess(message string) error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loggerContextHandle == "" {
|
||||
return fmt.Errorf("logger上下文未初始化")
|
||||
}
|
||||
|
||||
ctxPtr, _ := syscall.BytePtrFromString(loggerContextHandle)
|
||||
msgPtr, _ := syscall.BytePtrFromString(message)
|
||||
|
||||
m.logSuccess.Call(
|
||||
uintptr(unsafe.Pointer(ctxPtr)),
|
||||
uintptr(unsafe.Pointer(msgPtr)),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseLogger 关闭logger
|
||||
func CloseLogger() error {
|
||||
m, err := ensureLoggerDLL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, _, _ := m.closeAllLoggers.Call()
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("关闭logger失败")
|
||||
}
|
||||
|
||||
m.freeString.Call(ret)
|
||||
|
||||
loggerHandle = ""
|
||||
loggerContextHandle = ""
|
||||
loggerDLLInstance = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLoggerHandle 获取当前logger句柄(用于外部调用)
|
||||
func GetLoggerHandle() string {
|
||||
return loggerContextHandle
|
||||
}
|
||||
|
||||
// IsLoggerInitialized 检查logger是否已初始化
|
||||
func IsLoggerInitialized() bool {
|
||||
return loggerHandle != "" && loggerContextHandle != ""
|
||||
}
|
||||
|
||||
// SetConsoleOutput 设置控制台输出开关
|
||||
func SetConsoleOutput(enabled bool) {
|
||||
if enabled {
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
} else {
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
}
|
||||
}
|
||||
|
||||
// LogWithLevel 带级别的日志记录,可以精确控制显示
|
||||
func LogWithLevel(level, message string, showConsole bool) {
|
||||
if !IsLoggerInitialized() {
|
||||
return
|
||||
}
|
||||
|
||||
switch level {
|
||||
case "ERROR":
|
||||
LogError(message)
|
||||
case "WARNING":
|
||||
LogWarning(message)
|
||||
case "SUCCESS":
|
||||
LogSuccess(message)
|
||||
case "INFO":
|
||||
LogInfo(message)
|
||||
default:
|
||||
LogInfo(message)
|
||||
}
|
||||
}
|
||||
|
||||
// LogOnlyFile 仅写入文件,不输出到控制台
|
||||
func LogOnlyFile(level, message string) {
|
||||
// 临时禁用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "false")
|
||||
LogWithLevel(level, message, false)
|
||||
}
|
||||
|
||||
// LogConsoleAndFile 同时输出到控制台和文件
|
||||
func LogConsoleAndFile(level, message string) {
|
||||
// 临时启用控制台输出
|
||||
os.Setenv("LOG_CONSOLE", "true")
|
||||
LogWithLevel(level, message, true)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# 接口详情
|
||||
## 创建日志器--CreateLogger
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CreateLogger(configJSON)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| configJSON | string | 是 | 配置信息JSON字符串 |
|
||||
#### 配置JSON结构
|
||||
```json
|
||||
{
|
||||
"log_dir": "/path/to/logs",
|
||||
"split_type": 0,
|
||||
"rotate_type": 0,
|
||||
"max_size": 104857600,
|
||||
"max_count": 30,
|
||||
"level": 1,
|
||||
"enable_caller": true,
|
||||
"default_task_type": "main"
|
||||
}
|
||||
```
|
||||
#### 参数说明:
|
||||
```text
|
||||
log_dir: 日志目录路径
|
||||
split_type: 分片方式(0=按月,1=按天,2=按小时,3=按分钟,4=按秒)
|
||||
rotate_type: 轮转方式(0=按大小,1=按数量)
|
||||
max_size: 最大文件大小(字节),仅在rotate_type=0时有效
|
||||
max_count: 最大文件数量,仅在rotate_type=1时有效
|
||||
level: 日志级别(0=SUCCESS,1=INFO,2=WARNING,3=ERROR)
|
||||
enable_caller: 是否启用调用者信息
|
||||
default_task_type: 默认任务类型
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"错误: 创建日志目录失败: permission denied"
|
||||
```
|
||||
|
||||
## 创建带任务类型的上下文--CreateContextWithTaskType
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CreateContextWithTaskType(loggerHandle, taskType)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
| taskType | string | 是 | 任务类型 |
|
||||
### 响应示例
|
||||
```json
|
||||
"ctx_1645497600000000000"
|
||||
```
|
||||
#### 错误响应示例
|
||||
```json
|
||||
"错误: 无效的logger句柄"
|
||||
```
|
||||
|
||||
## 记录信息日志--LogInfo
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogInfo(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录错误日志--LogError
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogError(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录警告日志--LogWarning
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogWarning(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 记录成功日志--LogSuccess
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.LogSuccess(ctxHandle, message)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|--------------|
|
||||
| ctxHandle | string | 是 | 上下文句柄 |
|
||||
| message | string | 是 | 日志消息 |
|
||||
### 响应示例
|
||||
```text
|
||||
无返回值,日志将写入到对应的日志文件中。
|
||||
```
|
||||
|
||||
## 获取日志条目--GetLogs
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetLogs(loggerHandle, configJSON)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
| configJSON | string | 是 | 查询配置JSON |
|
||||
#### 查询配置JSON结构
|
||||
```json
|
||||
{
|
||||
"level": 1,
|
||||
"task_type": "main",
|
||||
"start_time": "2024-01-01 00:00:00",
|
||||
"end_time": "2024-01-31 23:59:59",
|
||||
"max_entries": 1000
|
||||
}
|
||||
```
|
||||
#### 参数说明:
|
||||
```text
|
||||
level: 日志级别(-1表示所有级别)
|
||||
task_type: 任务类型(空字符串表示所有任务类型)
|
||||
start_time: 开始时间(格式: 2006-01-02 15:04:05)
|
||||
end_time: 结束时间(格式: 2006-01-02 15:04:05)
|
||||
max_entries: 最大返回条目数(0表示使用默认值1000)
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"count": 125,
|
||||
"entries": [
|
||||
{
|
||||
"timestamp": "2024-01-15 10:30:45.123",
|
||||
"level": "INFO",
|
||||
"task_type": "main",
|
||||
"caller": "logger.go:256",
|
||||
"message": "系统启动完成"
|
||||
},
|
||||
{
|
||||
"timestamp": "2024-01-15 10:31:15.456",
|
||||
"level": "ERROR",
|
||||
"task_type": "backup",
|
||||
"caller": "backup.go:89",
|
||||
"message": "备份文件失败: 磁盘空间不足"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{"error": "无效的logger句柄"}
|
||||
```
|
||||
|
||||
## 获取日志文件列表--GetLogFiles
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetLogFiles(loggerHandle)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| loggerHandle | string | 是 | 日志器句柄 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"count": 8,
|
||||
"files": [
|
||||
{
|
||||
"level": "INFO",
|
||||
"task_type": "main",
|
||||
"file_name": "INFO-main-2024-01.logs",
|
||||
"file_size": 1048576,
|
||||
"mod_time": "2024-01-15 10:30:45"
|
||||
},
|
||||
{
|
||||
"level": "ERROR",
|
||||
"task_type": "backup",
|
||||
"file_name": "ERROR-backup-2024-01.logs",
|
||||
"file_size": 51200,
|
||||
"mod_time": "2024-01-15 10:31:15"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{"error": "无效的logger句柄"}
|
||||
```
|
||||
|
||||
## 获取版本信息--GetVersion
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.GetVersion()
|
||||
```
|
||||
### 请求参数
|
||||
```text
|
||||
无参数
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"v1"
|
||||
```
|
||||
|
||||
## 关闭所有日志器--CloseAllLoggers
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.CloseAllLoggers()
|
||||
```
|
||||
### 请求参数
|
||||
```text
|
||||
无参数
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
"成功关闭所有logger"
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
"关闭了5个logger,其中1个出错,最后错误: close file error"
|
||||
```
|
||||
|
||||
## 释放C字符串内存--FreeString
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.FreeString(str)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------|
|
||||
| str | string | 是 | 需要释放的字符串 |
|
||||
BIN
planB/modules/pdd/pdd.dll
Normal file
BIN
planB/modules/pdd/pdd.dll
Normal file
Binary file not shown.
337
planB/modules/pdd/pdd.go
Normal file
337
planB/modules/pdd/pdd.go
Normal file
@ -0,0 +1,337 @@
|
||||
package pdd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gPddDll *PddDLL
|
||||
)
|
||||
|
||||
// PddResponse 定义完整的响应结构(包含成功和失败两种情况)
|
||||
type PddResponse struct {
|
||||
SuccessResponse *PddSuccessResponse `json:"outer_cat_mapping_get_response,omitempty"`
|
||||
ErrorResponse *PddErrorResponse `json:"error_response,omitempty"`
|
||||
}
|
||||
type PddSuccessResponse struct {
|
||||
OuterCatMappingGetResponse PddCategoryMappingResponse `json:"outer_cat_mapping_get_response"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// PddDLL 拼多多工具DLL结构
|
||||
type PddDLL struct {
|
||||
Dll *syscall.DLL
|
||||
pddGoodsOuterCatMappingGet *syscall.Proc // 类目预测
|
||||
freeCString *syscall.Proc // 释放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
|
||||
}
|
||||
|
||||
// InitPddDll 初始化 pddDLL
|
||||
func InitPddDll(url string) (*PddDLL, error) {
|
||||
dllPath := filepath.Join(url, "pdd.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("pdd DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
|
||||
}
|
||||
gPddDll = &PddDLL{
|
||||
Dll: dll,
|
||||
pddGoodsOuterCatMappingGet: dll.MustFindProc("PddGoodsOuterCatMappingGet"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}
|
||||
return gPddDll, nil
|
||||
}
|
||||
|
||||
// PddGoodsOuterCatMappingGet 类目预测
|
||||
func (m *PddDLL) PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsOuterCatMappingGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsOuterCatMappingGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
outerCatIdPtr, _ := syscall.BytePtrFromString(outerCatId)
|
||||
outerCatNamePtr, _ := syscall.BytePtrFromString(outerCatName)
|
||||
outerGoodsNamePtr, _ := syscall.BytePtrFromString(outerGoodsName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatIdPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatNamePtr)),
|
||||
uintptr(unsafe.Pointer(outerGoodsNamePtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsAdd 商品新增
|
||||
func (m *PddDLL) PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddGoodsAdd")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsAdd: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsAddJsonPtr, _ := syscall.BytePtrFromString(goodsAddJson)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsAddJsonPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// PddGoodsSpecIdGet 生成商家自定义的规格
|
||||
func (m *PddDLL) PddGoodsSpecIdGet(clientId, clientSecret, accessToken, parentSpecId, specName string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsSpecIdGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsSpecIdGet: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
parentSpecIdPtr, _ := syscall.BytePtrFromString(parentSpecId)
|
||||
specNamePtr, _ := syscall.BytePtrFromString(specName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(parentSpecIdPtr)),
|
||||
uintptr(unsafe.Pointer(specNamePtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsCommitDetailGet 获取商品提交的商品详情
|
||||
func (m *PddDLL) PddGoodsCommitDetailGet(clientId, clientSecret, accessToken, goodsCommitId, goodsId string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsCommitDetailGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsCommitDetailGet: %v", err)
|
||||
}
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
goodsCommitIdPtr, _ := syscall.BytePtrFromString(goodsCommitId)
|
||||
goodsIdPtr, _ := syscall.BytePtrFromString(goodsId)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(goodsCommitIdPtr)),
|
||||
uintptr(unsafe.Pointer(goodsIdPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddTimeGet 获取拼多多系统时间
|
||||
func (m *PddDLL) PddTimeGet(clientId, clientSecret, accessToken string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddTimeGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsCommitDetailGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsImageUpload 上传图片
|
||||
func (m *PddDLL) PddGoodsImageUpload(clientId, clientSecret, accessToken, imgBase64 string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsImageUpload")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsImageUpload: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
imgBase64Ptr, _ := syscall.BytePtrFromString(imgBase64)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(imgBase64Ptr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsListGet 获取店铺商品
|
||||
func (m *PddDLL) PddGoodsListGet(clientId, clientSecret, accessToken string, params string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddGoodsListGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsListGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
paramsPtr, _ := syscall.BytePtrFromString(params)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(paramsPtr)),
|
||||
)
|
||||
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsSaleStatusSet 设置上下架状态
|
||||
func (m *PddDLL) PddGoodsSaleStatusSet(clientId, clientSecret, accessToken, params string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddGoodsSaleStatusSet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsSaleStatusSet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
paramsPtr, _ := syscall.BytePtrFromString(params)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(paramsPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddDeleteGoodsCommit 删除商品
|
||||
func (m *PddDLL) PddDeleteGoodsCommit(clientId, clientSecret, accessToken, params string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddDeleteGoodsCommit")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddDeleteGoodsCommit: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
paramsPtr, _ := syscall.BytePtrFromString(params)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(paramsPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsQuantityUpdate 更新库存
|
||||
func (m *PddDLL) PddGoodsQuantityUpdate(clientId, clientSecret, accessToken, params string) (string, error) {
|
||||
|
||||
proc, err := m.Dll.FindProc("PddGoodsQuantityUpdate")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsQuantityUpdate: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
paramsPtr, _ := syscall.BytePtrFromString(params)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(paramsPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PddGoodsSkuPriceUpdate 更新价格
|
||||
func (m *PddDLL) PddGoodsSkuPriceUpdate(clientId, clientSecret, accessToken, params string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("PddGoodsSkuPriceUpdate")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsSkuPriceUpdate: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
paramsPtr, _ := syscall.BytePtrFromString(params)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(paramsPtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
863
planB/modules/pdd/pdd.md
Normal file
863
planB/modules/pdd/pdd.md
Normal file
@ -0,0 +1,863 @@
|
||||
# pdd.dll 使用教程
|
||||
## 1.创建DLL工具实例
|
||||
### 加载DLL文件
|
||||
```gotemplate
|
||||
// PddDLL 拼多多工具DLL结构
|
||||
type pddDLL struct {
|
||||
dll *syscall.DLL
|
||||
pddGoodsOuterCatMappingGet *syscall.Proc // 类目预测
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// <初始化pddDLL></初始化pddDLL>
|
||||
func InitPddDLL() (*pddDLL, error) {
|
||||
dllPath := filepath.Join("dll", "pdd.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("pdd DLL 不存在: %s", dllPath)
|
||||
}
|
||||
if dll, err := syscall.LoadDLL(dllPath); err != nil {
|
||||
return nil, fmt.Errorf("加载pdd DLL 失败: %s", err)
|
||||
} else {
|
||||
return &pddDLL{
|
||||
dll: dll,
|
||||
pddGoodsOuterCatMappingGet: dll.MustFindProc("PddGoodsOuterCatMappingGet"),
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
dll, err := InitPddDLL()
|
||||
```
|
||||
|
||||
### 获取C字符串
|
||||
```gotemplate
|
||||
// cStr 获取C字符串
|
||||
func (m *pddDLL) cStr(p uintptr) string {
|
||||
if p == 0 {
|
||||
return ""
|
||||
}
|
||||
b := []byte{}
|
||||
for i := uintptr(0); ; i++ {
|
||||
c := *(*byte)(unsafe.Pointer(p + i))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
}
|
||||
s := string(b)
|
||||
if m.freeCString != nil {
|
||||
m.freeCString.Call(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 使用dll函数示例
|
||||
```gotemplate
|
||||
// 类目预测
|
||||
func (m *pddDLL) PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName string) (string, error) {
|
||||
proc, err := m.dll.FindProc("PddGoodsOuterCatMappingGet")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 PddGoodsOuterCatMappingGet: %v", err)
|
||||
}
|
||||
|
||||
clientIdPtr, _ := syscall.BytePtrFromString(clientId)
|
||||
clientSecretPtr, _ := syscall.BytePtrFromString(clientSecret)
|
||||
accessTokenPtr, _ := syscall.BytePtrFromString(accessToken)
|
||||
outerCatIdPtr, _ := syscall.BytePtrFromString(outerCatId)
|
||||
outerCatNamePtr, _ := syscall.BytePtrFromString(outerCatName)
|
||||
outerGoodsNamePtr, _ := syscall.BytePtrFromString(outerGoodsName)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(clientIdPtr)),
|
||||
uintptr(unsafe.Pointer(clientSecretPtr)),
|
||||
uintptr(unsafe.Pointer(accessTokenPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatIdPtr)),
|
||||
uintptr(unsafe.Pointer(outerCatNamePtr)),
|
||||
uintptr(unsafe.Pointer(outerGoodsNamePtr)),
|
||||
)
|
||||
|
||||
result := m.cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
```
|
||||
|
||||
# 接口详情
|
||||
## 1. 类目预测--PddGoodsOuterCatMappingGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsOuterCatMappingGet(clientId, clientSecret, accessToken,
|
||||
outerCatId, outerCatName, outerGoodsName)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| outerCatId | string | 是 | 外部平台类目ID |
|
||||
| outerCatName | string | 是 | 外部平台类目名称 |
|
||||
| outerGoodsName | string | 是 | 外部商品名称 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"outer_cat_mapping_get_response": {
|
||||
"cat_id2": 16028,
|
||||
"cat_id3": 16031,
|
||||
"cat_id1": 15543,
|
||||
"request_id": "17666480184871649",
|
||||
"cat_id4": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 快递公司查看--PddLogisticsCompaniesGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddLogisticsCompaniesGet(clientId, clientSecret)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"logistics_companies_get_response": {
|
||||
"logistics_companies": [
|
||||
{
|
||||
"available": 1,
|
||||
"code": "SF",
|
||||
"id": 1,
|
||||
"logistics_company": "顺丰速运"
|
||||
},
|
||||
{
|
||||
"available": 1,
|
||||
"code": "STO",
|
||||
"id": 2,
|
||||
"logistics_company": "申通快递"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. erp打单信息同步--PddErpOrderSync
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddErpOrderSync(clientId, clientSecret, accessToken, logisticsId,
|
||||
orderSn, orderState, waybillNo)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| logisticsId | string | 是 | 物流公司ID |
|
||||
| orderSn | string | 是 | 拼多多订单号 |
|
||||
| orderState | string | 是 | 订单状态 |
|
||||
| waybillNo | string | 是 | 运单号 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"erp_order_sync_response": {
|
||||
"is_success": true,
|
||||
"request_id": "17666480184871650"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 拼多多订单同步--PddOrderSynchronization
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddOrderSynchronization(clientId, clientSecret, accessToken, logisticsCompany, logisticsOnlineSendJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| logisticsCompany | string | 是 | 物流公司名称 |
|
||||
| logisticsOnlineSendJson | string | 是 | 拼多多订单同步json字符串 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"erp_order_sync_response": {
|
||||
"is_success": true,
|
||||
"request_id": "17666480184871651"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 商品图片上传接口--PddGoodsImgUpload
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsImgUpload(clientId, clientSecret, accessToken, filePath)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| filePath | string | 是 | 图片文件路径 |
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_img_upload_response": {
|
||||
"image_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"request_id": "17666480184871652"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 商品新增接口--PddGoodsAdd
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsAdd(clientId, clientSecret, accessToken, goodsAddJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| goodsAddJson | string | 是 | 商品信息JSON字符串 |
|
||||
#### 商品信息JSON结构示例
|
||||
```json
|
||||
{
|
||||
"goods_name": "测试商品",
|
||||
"goods_desc": "商品描述",
|
||||
"cat_id": 20111,
|
||||
"goods_type": 1,
|
||||
"market_price": 9900,
|
||||
"is_folt": false,
|
||||
"is_pre_sale": false,
|
||||
"is_refundable": true,
|
||||
"shipment_limit_second": 86400,
|
||||
"cost_template_id": 10001,
|
||||
"image_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"carousel_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"detail_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"sku_list": [
|
||||
{
|
||||
"out_sku_sn": "SKU001",
|
||||
"price": 8900,
|
||||
"quantity": 100,
|
||||
"spec_id_list": "1001:10001",
|
||||
"sku_properties": [
|
||||
{
|
||||
"ref_pid": 1001,
|
||||
"value": "红色",
|
||||
"vid": 10001,
|
||||
"punit": "个"
|
||||
}
|
||||
],
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 10,
|
||||
"multi_price": 8500,
|
||||
"thumb_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"weight": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_add_response": {
|
||||
"goods_id": 123456789,
|
||||
"goods_name": "测试商品",
|
||||
"goods_sn": "G202501200001",
|
||||
"request_id": "17666480184871653"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 联合拼多多图片上传的商品新增--SelfPddGoodsAdd
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.SelfPddGoodsAdd(clientId, clientSecret, accessToken, filePath, goodsAddJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| filePath | string | 是 | 图片文件路径 |
|
||||
| goodsAddJson | string | 是 | 商品信息JSON字符串(不需包含image_url)|
|
||||
#### 接口说明
|
||||
此接口为组合接口,内部执行以下步骤:
|
||||
1.上传商品主图文件到拼多多服务器
|
||||
2.获取图片URL并自动填充到商品信息中
|
||||
3.调用商品新增接口创建商品
|
||||
#### 商品信息JSON结构示例
|
||||
```json
|
||||
{
|
||||
"goods_name": "测试商品",
|
||||
"goods_desc": "商品描述",
|
||||
"cat_id": 20111,
|
||||
"goods_type": 1,
|
||||
"market_price": 9900,
|
||||
"is_folt": false,
|
||||
"is_pre_sale": false,
|
||||
"is_refundable": true,
|
||||
"shipment_limit_second": 86400,
|
||||
"cost_template_id": 10001,
|
||||
"image_url": "",
|
||||
"carousel_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"detail_gallery": [
|
||||
"http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg"
|
||||
],
|
||||
"sku_list": [
|
||||
{
|
||||
"out_sku_sn": "SKU001",
|
||||
"price": 8900,
|
||||
"quantity": 100,
|
||||
"spec_id_list": "1001:10001",
|
||||
"sku_properties": [
|
||||
{
|
||||
"ref_pid": 1001,
|
||||
"value": "红色",
|
||||
"vid": 10001,
|
||||
"punit": "个"
|
||||
}
|
||||
],
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 10,
|
||||
"multi_price": 8500,
|
||||
"thumb_url": "http://oms-imageimg.pinduoduo.com/upload/2025/01/20/e9a8c1b6e1a84f1d8d7c3a8b9e2f5c7d.jpg",
|
||||
"weight": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"goods_add_response": {
|
||||
"goods_id": 123456790,
|
||||
"goods_name": "测试商品",
|
||||
"goods_sn": "G202501200002",
|
||||
"request_id": "17666480184871654"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 批量数据解密脱敏接口--PddOpenDecryptMaskBatch
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddOpenDecryptMaskBatch(clientId, clientSecret, accessToken, reqJson)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| reqJson | string | 是 | 信息JSON字符串 |
|
||||
#### 信息JSON结构示例
|
||||
```json
|
||||
[
|
||||
{
|
||||
"data_tag": "251229-272441044622514",
|
||||
"encrypted_data": "~AgAAAAPlscEH0psOJAEXpTdsLOWvDJ9bB7IEjIoqNfiDhhJR9NHOxsdZ+PEFluSSCngCikoDU+CP/sSXZJ92ic7+PdNlJNLA7g/6VUMDWF6RvjW9IeRN+lKNarsjWDQR~0~"
|
||||
}
|
||||
]
|
||||
```
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"open_decrypt_mask_batch_response": {
|
||||
"data_decrypt_list": [
|
||||
{
|
||||
"data_tag": "str",
|
||||
"data_type": 0,
|
||||
"decrypted_data": "str",
|
||||
"encrypted_data": "str",
|
||||
"error_code": 0,
|
||||
"error_msg": "str"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 生成商家自定义的规格--PddGoodsSpecIdGet
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsSpecIdGet(clientId, clientSecret, accessToken, parentSpecId, specName)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| parentSpecId | string | 是 | 拼多多标准规格ID |
|
||||
| specName | string | 是 | 商家编辑的规格值,如颜色规格下设置白色属性 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_spec_id_get_response": {
|
||||
"parent_spec_id": 0,
|
||||
"spec_id": 0,
|
||||
"spec_name": "str"
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 修改商品SKU价格--PddGoodsSkuPriceUpdate
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsSkuPriceUpdate(clientId, clientSecret, accessToken, request)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| request | string | 是 | 价格更新请求JSON字符串 |
|
||||
#### 请求JSON结构
|
||||
```json
|
||||
{
|
||||
"goods_id": "必填,商品id,类型为LONG",
|
||||
"ignore_edit_warn": "非必填,是否获取商品发布警告信息,默认为忽略,类型为BOOLEAN",
|
||||
"market_price": "非必填,参考价(单位分),类型为LONG",
|
||||
"market_price_in_yuan": "非必填,参考价(单位元),类型为STRING",
|
||||
"sku_price_list": [
|
||||
{
|
||||
"group_price": "非必填,拼团购买价格(单位分),类型为LONG",
|
||||
"is_onsale": "非必填,sku上架状态,0-已下架,1-上架中,类型为INTEGER",
|
||||
"single_price": "非必填,单独购买价格(单位分),类型为LONG",
|
||||
"sku_id": "必填,sku标识,类型为LONG"
|
||||
}
|
||||
],
|
||||
"sync_goods_operate": "非必填,提交后上架状态,0:上架,1:保持原样,类型为INTEGER",
|
||||
"two_pieces_discount": "非必填,满2件折扣,可选范围0-100,0表示取消,95表示95折,设置需先查询规则接口获取实际可填范围,类型为INTEGER"
|
||||
}
|
||||
```
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_update_sku_price_response": {
|
||||
"goods_commit_id": 0,
|
||||
"is_success": true
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 商品库存更新接口--PddGoodsQuantityUpdate
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.PddGoodsQuantityUpdate(clientId, clientSecret, accessToken, request)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|---------------------|
|
||||
| clientId | string | 是 | 拼多多开放平台ClientID |
|
||||
| clientSecret | string | 是 | 拼多多开放平台ClientSecret |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
| request | string | 是 | 库存更新请求JSON字符串 |
|
||||
#### 请求JSON结构 request 字符串
|
||||
```json
|
||||
{
|
||||
"force_update": "非必填,是否强制更新,仅update_type=1(全量更新)时有效,默认值false;force_update=false时,quantity不能小于预扣库存;force_update=true时,代表强制更新,当quantity<预扣库存时,不报错,直接将quantity清0,类型为BOOLEAN",
|
||||
"goods_id": "必填,商品id,类型为LONG",
|
||||
"outer_id": "非必填,sku商家编码,类型为STRING",
|
||||
"quantity": "必填,库存修改值。当全量更新库存时,quantity必须为大于等于0的正整数;当增量更新库存时,quantity为整数,可小于等于0。若增量更新时传入的库存为负数,则负数与实际库存之和不能小于0。比如当前实际库存为1,传入增量更新quantity=-1,库存改为0,类型为LONG",
|
||||
"sku_id": "非必填,sku_id和outer_id必填一个,类型为LONG",
|
||||
"update_type": "非必填,库存更新方式,可选。1为全量更新,2为增量更新。如果不填,默认为全量更新,类型为INTEGER"
|
||||
}
|
||||
```
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"goods_quantity_update_response": {
|
||||
"is_success": false
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error_response": {
|
||||
"error_msg": "公共参数错误:type",
|
||||
"sub_msg": "",
|
||||
"sub_code": null,
|
||||
"error_code": 10001,
|
||||
"request_id": "15440104776643887"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 获取商品信息接口 -- OutPddAuthGetCommitDetailt
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthGetCommitDetailt(goodsCommitId, goodsId, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| goodsCommitId | string | 是 | 商品提交ID |
|
||||
| goodsId | string | 是 | 商品ID |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 获取商品详情信息接口 -- OutPddAuthGetGoodsDetail
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthGetGoodsDetail(goodsId, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| goodsId | string | 是 | 商品ID |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"bad_fruit_claim": 0,
|
||||
"buy_limit": 999999,
|
||||
"carousel_gallery_list": [
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/02a5c39a-7a90-4530-a338-3e87095a21a9.png",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/4539f740-331b-4687-aa00-5c96855de6cd.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/b0e89e39-c97b-475d-9be2-f1909e30acb5.jpeg"
|
||||
],
|
||||
"cat_id": 15678,
|
||||
"cost_template_id": 655688447565777,
|
||||
"country_id": 0,
|
||||
"customer_num": 2,
|
||||
"customs": "",
|
||||
"detail_gallery_list": [
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/b691c104-baf8-42b2-97e2-b7258113114b.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/53e6f7ff-d15e-4e8f-8625-e293717ca1e4.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-09-07/ecff591d-32a6-42c9-ba5a-6a42829092a8.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/7034f8a0-5d88-49f8-a96f-608abb8cac80.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/e10c2b6c-d4de-4fdd-8d48-f0a334735e9a.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2023-10-16/c19358fb-0a4d-49ad-bcc8-b2980e938064.jpeg",
|
||||
"https://img.pddpic.com/open-gw/2025-06-30/1deeb9c0-7212-432b-a309-f774db6e1adb.jpeg"
|
||||
],
|
||||
"goods_desc": "书名:金属工艺学 下 第6版,作者:'邓文英,宋力宏主编',ISBN:9787040456295,出版社:高等教育出版社",
|
||||
"goods_id": 770621582375,
|
||||
"goods_name": "金属工艺学 下 第6版 邓文英,宋力宏主编 高等教育出版社 978",
|
||||
"goods_property_list": [
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 425,
|
||||
"template_pid": 401030,
|
||||
"vid": 0,
|
||||
"vvalue": "9787040456295"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 876,
|
||||
"template_pid": 401029,
|
||||
"vid": 0,
|
||||
"vvalue": "金属工艺学 下 第6版"
|
||||
},
|
||||
{
|
||||
"punit": "页",
|
||||
"ref_pid": 692,
|
||||
"template_pid": 401032,
|
||||
"vid": 0,
|
||||
"vvalue": "157"
|
||||
},
|
||||
{
|
||||
"punit": "元",
|
||||
"ref_pid": 879,
|
||||
"template_pid": 401034,
|
||||
"vid": 0,
|
||||
"vvalue": "24.70"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 882,
|
||||
"template_pid": 401037,
|
||||
"vid": 0,
|
||||
"vvalue": "邓文英,宋力宏主编"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 880,
|
||||
"template_pid": 401035,
|
||||
"vid": 483761,
|
||||
"vvalue": "高等教育出版社"
|
||||
},
|
||||
{
|
||||
"punit": "",
|
||||
"ref_pid": 888,
|
||||
"template_pid": 401043,
|
||||
"vid": 0,
|
||||
"vvalue": "平装"
|
||||
}
|
||||
],
|
||||
"goods_type": 1,
|
||||
"image_url": "",
|
||||
"invoice_status": 0,
|
||||
"is_customs": 0,
|
||||
"is_folt": 0,
|
||||
"is_group_pre_sale": 0,
|
||||
"is_pre_sale": 0,
|
||||
"is_refundable": 1,
|
||||
"is_sku_pre_sale": 0,
|
||||
"market_price": 5948,
|
||||
"order_limit": 999999,
|
||||
"outer_goods_id": "9787040456295",
|
||||
"oversea_type": 0,
|
||||
"pre_sale_time": 0,
|
||||
"privacy_delivery": 0,
|
||||
"quan_guo_lian_bao": 0,
|
||||
"second_hand": 1,
|
||||
"shipment_limit_second": 172800,
|
||||
"sku_list": [
|
||||
{
|
||||
"is_onsale": 1,
|
||||
"limit_quantity": 999999,
|
||||
"multi_price": 1487,
|
||||
"out_sku_sn": "9787040456295",
|
||||
"price": 1587,
|
||||
"quantity": 0,
|
||||
"reserve_quantity": 0,
|
||||
"sku_id": 1753931570290,
|
||||
"sku_pre_sale_time": 0,
|
||||
"spec": [
|
||||
{
|
||||
"parent_id": 1216,
|
||||
"parent_name": "尺寸",
|
||||
"spec_id": 27632894279,
|
||||
"spec_name": "单本 无附赠 超七天不退换"
|
||||
}
|
||||
],
|
||||
"thumb_url": "https://img.pddpic.com/open-gw/2025-06-30/59c30d4c-193f-40a3-a639-7af59a381ec5.jpeg",
|
||||
"weight": 500
|
||||
}
|
||||
],
|
||||
"status": 4,
|
||||
"tiny_name": "金属工艺学 下 第6",
|
||||
"two_pieces_discount": 96,
|
||||
"video_gallery": [],
|
||||
"warehouse": "",
|
||||
"warm_tips": "",
|
||||
"zhi_huan_bu_xiu": 0
|
||||
}
|
||||
```
|
||||
|
||||
## 生成自定义规格接口 -- OutPddAuthSetSpec
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthSetSpec(specTypeId, specName, accessToken)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| specTypeId | int | 是 | 规格类型ID |
|
||||
| specName | string | 是 | 规格名称 |
|
||||
| accessToken | string | 是 | 授权令牌 |
|
||||
### 响应参数
|
||||
```json
|
||||
{
|
||||
"parentSpecId": 3820,
|
||||
"specName": "全新",
|
||||
"specId": 1080396526
|
||||
}
|
||||
```
|
||||
|
||||
## 修改价格接口 -- OutPddAuthUpdatePrice
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthUpdatePrice(jsonData)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------|
|
||||
| jsonData | int | 是 | 价格修改信息JSON字符串 |
|
||||
### 响应参数
|
||||
```json
|
||||
[
|
||||
{
|
||||
"success": true,
|
||||
"msg": "操作成功"
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"msg": "操作失败"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 修改库存接口 -- OutPddAuthUpdateStock
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.OutPddAuthUpdateStock(jsonData)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|-----------------|
|
||||
| jsonData | int | 是 | 价格修改信息JSON字符串 |
|
||||
### 响应参数
|
||||
```json
|
||||
[
|
||||
{
|
||||
"success": true,
|
||||
"msg": "操作成功"
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"msg": "操作失败"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 12.释放C字符串内存--FreeCString
|
||||
### 请求信息
|
||||
```gotemplate
|
||||
dll.FreeCString(str)
|
||||
```
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--|--|--|----------|
|
||||
| str | string | 是 | 需要释放的字符串 |
|
||||
BIN
planB/modules/xianYu/address.xlsx
Normal file
BIN
planB/modules/xianYu/address.xlsx
Normal file
Binary file not shown.
BIN
planB/modules/xianYu/bak/xy.dll
Normal file
BIN
planB/modules/xianYu/bak/xy.dll
Normal file
Binary file not shown.
25
planB/modules/xianYu/config.ini
Normal file
25
planB/modules/xianYu/config.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[app]
|
||||
AppId = 1228288260261189
|
||||
AppSecret = aq9gAwrwp6WGZkMRqKIXmnu2c2uCm82k
|
||||
Domain = https://open.goofish.pro
|
||||
[http]
|
||||
Addr = 127.0.0.1:53368
|
||||
[categoryListRequest]
|
||||
Path = /api/open/product/category/list
|
||||
ItemBizType: 2
|
||||
SpBizType: 24
|
||||
[batchCreatRequest]
|
||||
Path = /api/open/product/batchCreate
|
||||
[file]
|
||||
TxtPath = modules/xianYu/productCategory.txt
|
||||
ExcelPath = modules/xianYu/address.xlsx
|
||||
SheetName = Result
|
||||
[redis]
|
||||
Password = Long6166@@
|
||||
Addr = 127.0.0.1:6379
|
||||
Db = 5
|
||||
[tokenBucket]
|
||||
BucketKeyPrefix = "token_bucket_"
|
||||
TokenPerSecond = 10
|
||||
BucketSize = 100
|
||||
Delay = 100
|
||||
284
planB/modules/xianYu/productCategory.txt
Normal file
284
planB/modules/xianYu/productCategory.txt
Normal file
@ -0,0 +1,284 @@
|
||||
cbf4e2ec8f2013d31921b9e373cead75:电视剧
|
||||
cbf4e2ec8f2013d3267e0a01017d9f44:电影
|
||||
cbf4e2ec8f2013d36f38848189966e7d:生活
|
||||
cbf4e2ec8f2013d3ac899d2620c5df2b:成人教育音像
|
||||
cbf4e2ec8f2013d3acde29f76907b07f:动画
|
||||
cbf4e2ec8f2013d3e10cfa39bf43dc0f:儿童教育音像
|
||||
d14d229692616168b108d382c4e6ea42:废品回收
|
||||
d816d18aa66dfb3d1921b9e373cead75:励志成长
|
||||
dbaba36adf47af96b108d382c4e6ea42:不干胶标签
|
||||
e59460ef9961e2bd28d88a08a19453dc:古典吉他
|
||||
e59460ef9961e2bda7f7e02f36b0b49a:电箱吉他
|
||||
86cddebb2de0815c1921b9e373cead75:桌面文件柜
|
||||
86cddebb2de0815c267e0a01017d9f44:资料册
|
||||
86cddebb2de0815c6f38848189966e7d:镇纸
|
||||
86cddebb2de0815ca7f7e02f36b0b49a:文件袋
|
||||
86cddebb2de0815cacde29f76907b07f:文房墨汁
|
||||
86cddebb2de0815ce10cfa39bf43dc0f:文房四宝套装
|
||||
879b743300e7a58137b3d33c282f2081:古筝
|
||||
8bd8d9724880b84d28d88a08a19453dc:学习笔记
|
||||
a457d6fc43c609bdac899d2620c5df2b:单据收据
|
||||
a457d6fc43c609bdacde29f76907b07f:印台
|
||||
a9ef3505c7fe4b661921b9e373cead75:勾线笔
|
||||
a9ef3505c7fe4b66a7f7e02f36b0b49a:电子阅览器/电纸书
|
||||
ab78823bfd3c7134b108d382c4e6ea42:经济管理
|
||||
ac69f9982deabde1acde29f76907b07f:民谣吉他
|
||||
ac69f9982deabde1e10cfa39bf43dc0f:架子鼓
|
||||
b12c1c13a8dc3b2b6f38848189966e7d:POP广告纸
|
||||
b12c1c13a8dc3b2ba7f7e02f36b0b49a:修正贴
|
||||
b12c1c13a8dc3b2bac899d2620c5df2b:学生用印
|
||||
b12c1c13a8dc3b2bacde29f76907b07f:名片
|
||||
b2b61c32fc4c904428d88a08a19453dc:背胶证件照
|
||||
b3b713b29220947237b3d33c282f2081:台历
|
||||
4c49139fe1b6ae4aac899d2620c5df2b:童书育儿
|
||||
4fecb084c468ed626f38848189966e7d:黑板
|
||||
5042edcbd2cc4b94ac899d2620c5df2b:生活百科
|
||||
621bd460d751e0fc37b3d33c282f2081:订书机
|
||||
701ed8603d74ee60b108d382c4e6ea42:报纸
|
||||
722d38201b9c8cba267e0a01017d9f44:社科心理
|
||||
7912befd7e1215d11921b9e373cead75:挂历
|
||||
7dba397e41d08d4937b3d33c282f2081:拆信刀
|
||||
7eb776b01814cc6e1921b9e373cead75:教材教辅
|
||||
22e1d81dc4cf3a25a7f7e02f36b0b49a:图书
|
||||
2dfa3034d88aedcc1921b9e373cead75:期刊/杂志
|
||||
31329c43789fae0437b3d33c282f2081:戏曲综艺
|
||||
31329c43789fae04a7f7e02f36b0b49a:音乐唱片/专辑
|
||||
322a73805c38995f6f38848189966e7d:宝珠笔
|
||||
3cdbae6d47df9251a7f7e02f36b0b49a:电子资料
|
||||
22d3cfff678abab1e10cfa39bf43dc0f:握笔器
|
||||
b7fd03d456abe3011921b9e373cead75:活页替芯
|
||||
b7fd03d456abe301b108d382c4e6ea42:索引纸
|
||||
b7fd03d456abe301e10cfa39bf43dc0f:拍纸本
|
||||
c230ba4ca293f3b528d88a08a19453dc:马克笔
|
||||
c230ba4ca293f3b5a7f7e02f36b0b49a:钢笔
|
||||
c230ba4ca293f3b5ac899d2620c5df2b:铅笔
|
||||
c3c6e8d1d63c0618b108d382c4e6ea42:文学/小说
|
||||
c58d3dbcff05e404acde29f76907b07f:笔筒
|
||||
eac1d67ece5fa9b16f38848189966e7d:钢琴
|
||||
ee8603696d446e931921b9e373cead75:电钢琴
|
||||
06d80b131d7b0b616f38848189966e7d:毛笔
|
||||
0e28c0f1f1e57eb1ac899d2620c5df2b:地图
|
||||
0f75076039b85f74267e0a01017d9f44:计算器
|
||||
0f75076039b85f7428d88a08a19453dc:尺
|
||||
0f75076039b85f746f38848189966e7d:板擦
|
||||
0f75076039b85f74b108d382c4e6ea42:算盘
|
||||
11c38799bd389b3828d88a08a19453dc:漫画书籍
|
||||
ac69f9982deabde1a7f7e02f36b0b49a:上弦器
|
||||
83f9286d1ea41056ac899d2620c5df2b:其他吉他配件
|
||||
e59460ef9961e2bd1921b9e373cead75:变调夹
|
||||
83f9286d1ea4105637b3d33c282f2081:古典吉他弦
|
||||
e59460ef9961e2bdacde29f76907b07f:吉他单块效果器
|
||||
83f9286d1ea41056267e0a01017d9f44:吉他效果器配件
|
||||
83f9286d1ea41056b108d382c4e6ea42:吉他电源
|
||||
ac69f9982deabde1267e0a01017d9f44:吉他综合效果器
|
||||
e59460ef9961e2bdb108d382c4e6ea42:吉他背包琴盒
|
||||
83f9286d1ea4105628d88a08a19453dc:吉他背带
|
||||
83f9286d1ea410561921b9e373cead75:吉他连接线
|
||||
e59460ef9961e2bd6f38848189966e7d:吊架
|
||||
ac69f9982deabde1ac899d2620c5df2b:弦枕
|
||||
ac69f9982deabde11921b9e373cead75:弦柱
|
||||
e59460ef9961e2bd267e0a01017d9f44:拨片
|
||||
ac69f9982deabde128d88a08a19453dc:拾音器
|
||||
83f9286d1ea41056e10cfa39bf43dc0f:曼陀铃弦
|
||||
83f9286d1ea410566f38848189966e7d:民谣吉他弦
|
||||
ac69f9982deabde137b3d33c282f2081:清洁保护品
|
||||
83f9286d1ea41056a7f7e02f36b0b49a:滑棒指套
|
||||
e59460ef9961e2bde10cfa39bf43dc0f:电吉他弦
|
||||
e59460ef9961e2bdac899d2620c5df2b:背带钮
|
||||
83f9286d1ea41056acde29f76907b07f:脚凳
|
||||
ac69f9982deabde1b108d382c4e6ea42:调音器
|
||||
e59460ef9961e2bd37b3d33c282f2081:电吉他
|
||||
c6d5c9e68467b108ac899d2620c5df2b:哑鼓垫
|
||||
c6d5c9e68467b10837b3d33c282f2081:镲片
|
||||
c6d5c9e68467b10828d88a08a19453dc:鼓凳
|
||||
ac69f9982deabde16f38848189966e7d:鼓刷
|
||||
c6d5c9e68467b108b108d382c4e6ea42:鼓架镲架
|
||||
c6d5c9e68467b108a7f7e02f36b0b49a:鼓棒鼓锤
|
||||
1cac27c660d7b098b108d382c4e6ea42:唢呐
|
||||
1cac27c660d7b098267e0a01017d9f44:埙
|
||||
f22578f0c6a8eaa5267e0a01017d9f44:尺八
|
||||
f22578f0c6a8eaa51921b9e373cead75:巴乌
|
||||
1cac27c660d7b09828d88a08a19453dc:笙
|
||||
f22578f0c6a8eaa5acde29f76907b07f:笛子
|
||||
f22578f0c6a8eaa5e10cfa39bf43dc0f:管子
|
||||
1cac27c660d7b0981921b9e373cead75:箫
|
||||
1cac27c660d7b098a7f7e02f36b0b49a:芦笙
|
||||
1cac27c660d7b09837b3d33c282f2081:葫芦丝
|
||||
f22578f0c6a8eaa56f38848189966e7d:葫芦笙
|
||||
1cac27c660d7b098ac899d2620c5df2b:陶笛
|
||||
879b743300e7a581acde29f76907b07f:三弦
|
||||
1cac27c660d7b098e10cfa39bf43dc0f:冬不拉
|
||||
1cac27c660d7b0986f38848189966e7d:古琴
|
||||
1cac27c660d7b098acde29f76907b07f:弹布尔
|
||||
879b743300e7a581e10cfa39bf43dc0f:扬琴
|
||||
879b743300e7a5816f38848189966e7d:月琴
|
||||
879b743300e7a58128d88a08a19453dc:柳琴
|
||||
879b743300e7a5811921b9e373cead75:热瓦普
|
||||
879b743300e7a581b108d382c4e6ea42:琵琶
|
||||
879b743300e7a581ac899d2620c5df2b:秦琴
|
||||
879b743300e7a581a7f7e02f36b0b49a:箜篌
|
||||
879b743300e7a581267e0a01017d9f44:阮
|
||||
a2eba09f5b889a7c28d88a08a19453dc:中胡
|
||||
7d61e938542f6790b108d382c4e6ea42:二胡
|
||||
7d61e938542f6790267e0a01017d9f44:京二胡
|
||||
7d61e938542f6790acde29f76907b07f:京胡
|
||||
7d61e938542f679028d88a08a19453dc:低音胡
|
||||
a2eba09f5b889a7c37b3d33c282f2081:四胡
|
||||
a2eba09f5b889a7cb108d382c4e6ea42:坠琴
|
||||
7d61e938542f6790a7f7e02f36b0b49a:板胡
|
||||
a2eba09f5b889a7ca7f7e02f36b0b49a:椰胡
|
||||
7d61e938542f679037b3d33c282f2081:艾捷克
|
||||
7d61e938542f67901921b9e373cead75:革胡
|
||||
7d61e938542f67906f38848189966e7d:马头琴
|
||||
7d61e938542f6790e10cfa39bf43dc0f:马骨胡
|
||||
7d61e938542f6790ac899d2620c5df2b:高胡
|
||||
882b39ff0db2dd0037b3d33c282f2081:军镲
|
||||
00a32e7ff35aaf9e267e0a01017d9f44:大钹
|
||||
00a32e7ff35aaf9ee10cfa39bf43dc0f:大铙
|
||||
00a32e7ff35aaf9eacde29f76907b07f:大顶钹
|
||||
00a32e7ff35aaf9e1921b9e373cead75:川钹
|
||||
00a32e7ff35aaf9e6f38848189966e7d:广钹
|
||||
882b39ff0db2dd00a7f7e02f36b0b49a:快板
|
||||
882b39ff0db2dd0028d88a08a19453dc:拍板
|
||||
00a32e7ff35aaf9eb108d382c4e6ea42:梆子
|
||||
882b39ff0db2dd001921b9e373cead75:水镲
|
||||
882b39ff0db2dd00b108d382c4e6ea42:碰钟
|
||||
882b39ff0db2dd00acde29f76907b07f:秧歌镲
|
||||
882b39ff0db2dd00e10cfa39bf43dc0f:腰鼓镲
|
||||
882b39ff0db2dd00ac899d2620c5df2b:萨巴依
|
||||
882b39ff0db2dd00267e0a01017d9f44:铜书板
|
||||
00a32e7ff35aaf9eac899d2620c5df2b:镲锅
|
||||
0ea61a801ba323c1267e0a01017d9f44:堂鼓
|
||||
00a32e7ff35aaf9e28d88a08a19453dc:战鼓
|
||||
0ea61a801ba323c11921b9e373cead75:排鼓
|
||||
0ea61a801ba323c1b108d382c4e6ea42:板鼓
|
||||
00a32e7ff35aaf9e37b3d33c282f2081:秧歌鼓
|
||||
0ea61a801ba323c1e10cfa39bf43dc0f:细腰鼓
|
||||
00a32e7ff35aaf9ea7f7e02f36b0b49a:腰鼓
|
||||
0ea61a801ba323c1ac899d2620c5df2b:花盆鼓
|
||||
0ea61a801ba323c16f38848189966e7d:象脚鼓
|
||||
0ea61a801ba323c1acde29f76907b07f:铜鼓
|
||||
a7133eb411b587cf1921b9e373cead75:空灵鼓/无忧鼓
|
||||
0ea61a801ba323c1a7f7e02f36b0b49a:云锣
|
||||
a2eba09f5b889a7c267e0a01017d9f44:京锣
|
||||
a2eba09f5b889a7cac899d2620c5df2b:低音锣
|
||||
a2eba09f5b889a7cacde29f76907b07f:开道锣
|
||||
a2eba09f5b889a7ce10cfa39bf43dc0f:手锣
|
||||
0ea61a801ba323c137b3d33c282f2081:武锣
|
||||
0ea61a801ba323c128d88a08a19453dc:舟山锣
|
||||
a2eba09f5b889a7c6f38848189966e7d:苏锣
|
||||
a2eba09f5b889a7c1921b9e373cead75:虎音锣
|
||||
33a0daa5d89d68fa1921b9e373cead75:宣纸
|
||||
b12c1c13a8dc3b2b1921b9e373cead75:吊牌
|
||||
b12c1c13a8dc3b2be10cfa39bf43dc0f:自封袋
|
||||
b12c1c13a8dc3b2b267e0a01017d9f44:贺卡明信片
|
||||
22d3cfff678abab11921b9e373cead75:书皮
|
||||
b12c1c13a8dc3b2b37b3d33c282f2081:修正带
|
||||
b12c1c13a8dc3b2b28d88a08a19453dc:修正液
|
||||
22d3cfff678abab1a7f7e02f36b0b49a:削笔器
|
||||
22d3cfff678abab128d88a08a19453dc:可爱印泥
|
||||
b12c1c13a8dc3b2bb108d382c4e6ea42:学生书包
|
||||
22d3cfff678abab1acde29f76907b07f:文具套装
|
||||
22d3cfff678abab1267e0a01017d9f44:文具盒
|
||||
22d3cfff678abab16f38848189966e7d:橡皮
|
||||
22d3cfff678abab1b108d382c4e6ea42:练字帖
|
||||
22d3cfff678abab1ac899d2620c5df2b:视力保护器
|
||||
dbaba36adf47af9637b3d33c282f2081:笔袋
|
||||
54e552aa1c9b2cbcacde29f76907b07f:彩泥橡皮泥
|
||||
bf164bd2e8dd8cebb108d382c4e6ea42:便条照片夹
|
||||
bf164bd2e8dd8ceb28d88a08a19453dc:便签盒座
|
||||
bf164bd2e8dd8cebe10cfa39bf43dc0f:卡套证件套
|
||||
bf164bd2e8dd8ceb6f38848189966e7d:名片册
|
||||
86cddebb2de0815c37b3d33c282f2081:名片盒
|
||||
bf164bd2e8dd8cebacde29f76907b07f:快劳夹
|
||||
86cddebb2de0815c28d88a08a19453dc:文件夹
|
||||
86cddebb2de0815cb108d382c4e6ea42:文件架
|
||||
bf164bd2e8dd8ceb1921b9e373cead75:档案盒
|
||||
bf164bd2e8dd8cebac899d2620c5df2b:档案袋
|
||||
86cddebb2de0815cac899d2620c5df2b:相册
|
||||
1ad9ac4511bbb8646f38848189966e7d:笔插
|
||||
bf164bd2e8dd8ceba7f7e02f36b0b49a:笔架
|
||||
bf164bd2e8dd8ceb267e0a01017d9f44:风琴包
|
||||
d665d5e1347fa192a7f7e02f36b0b49a:地球仪
|
||||
bf164bd2e8dd8ceb37b3d33c282f2081:展板
|
||||
d665d5e1347fa192267e0a01017d9f44:教学仪器器材
|
||||
d665d5e1347fa1921921b9e373cead75:教鞭
|
||||
bb9bba251ee78e59267e0a01017d9f44:旗帜
|
||||
d665d5e1347fa19237b3d33c282f2081:提示牌
|
||||
d665d5e1347fa192b108d382c4e6ea42:激光笔
|
||||
0f75076039b85f74acde29f76907b07f:白板
|
||||
0f75076039b85f74e10cfa39bf43dc0f:白板笔
|
||||
d665d5e1347fa19228d88a08a19453dc:粉笔
|
||||
d665d5e1347fa192acde29f76907b07f:绿板
|
||||
d665d5e1347fa1926f38848189966e7d:荧光板
|
||||
d665d5e1347fa192ac899d2620c5df2b:计划表
|
||||
d665d5e1347fa192e10cfa39bf43dc0f:软木板
|
||||
a457d6fc43c609bda7f7e02f36b0b49a:中性笔
|
||||
c230ba4ca293f3b5e10cfa39bf43dc0f:圆珠笔
|
||||
c230ba4ca293f3b51921b9e373cead75:铅芯
|
||||
f9910185f1984f2937b3d33c282f2081:正姿笔
|
||||
c230ba4ca293f3b5acde29f76907b07f:油漆笔
|
||||
c230ba4ca293f3b5b108d382c4e6ea42:泡泡笔
|
||||
c230ba4ca293f3b537b3d33c282f2081:墨水墨囊
|
||||
c230ba4ca293f3b5267e0a01017d9f44:荧光笔
|
||||
f4a071d4dba28eccac899d2620c5df2b:记号笔
|
||||
c230ba4ca293f3b56f38848189966e7d:针管笔
|
||||
dfdbd3409fadcd3f6f38848189966e7d:其他笔
|
||||
58e84885c426409e267e0a01017d9f44:书签
|
||||
b7fd03d456abe30128d88a08a19453dc:便签
|
||||
e9fa1ad466b79d97b108d382c4e6ea42:信封
|
||||
af2cf5b1faa3537a1921b9e373cead75:信纸
|
||||
b7fd03d456abe30137b3d33c282f2081:包装纸
|
||||
e9fa1ad466b79d97a7f7e02f36b0b49a:纪念册
|
||||
b7fd03d456abe301ac899d2620c5df2b:复写纸
|
||||
b7fd03d456abe301267e0a01017d9f44:奖状证书
|
||||
e9fa1ad466b79d971921b9e373cead75:手工纸
|
||||
e9fa1ad466b79d9728d88a08a19453dc:草稿纸
|
||||
b7fd03d456abe3016f38848189966e7d:日记本
|
||||
e9fa1ad466b79d97ac899d2620c5df2b:硬面抄
|
||||
b7fd03d456abe301a7f7e02f36b0b49a:记事本
|
||||
b7fd03d456abe301acde29f76907b07f:课业本
|
||||
e9fa1ad466b79d9737b3d33c282f2081:通讯录
|
||||
dbaba36adf47af966f38848189966e7d:磁性贴
|
||||
6c0543ec11db7e61267e0a01017d9f44:贴纸/标签
|
||||
0f75076039b85f741921b9e373cead75:圆规
|
||||
0f75076039b85f74ac899d2620c5df2b:显微镜
|
||||
1c75d8021bacf61e267e0a01017d9f44:放大镜
|
||||
a9ef3505c7fe4b66b108d382c4e6ea42:丙烯颜料
|
||||
823f8d7bd96780d0ac899d2620c5df2b:书法用纸
|
||||
a9ef3505c7fe4b66ac899d2620c5df2b:儿童填色本
|
||||
a9ef3505c7fe4b66267e0a01017d9f44:国画颜料
|
||||
823f8d7bd96780d037b3d33c282f2081:描图硫酸纸
|
||||
5fd3299edc3ff44a37b3d33c282f2081:毛边纸
|
||||
823f8d7bd96780d01921b9e373cead75:水彩笔
|
||||
823f8d7bd96780d0267e0a01017d9f44:水彩颜料
|
||||
823f8d7bd96780d0acde29f76907b07f:水粉水彩油画笔
|
||||
823f8d7bd96780d0e10cfa39bf43dc0f:水粉颜料
|
||||
0f75076039b85f7437b3d33c282f2081:油画棒
|
||||
0f75076039b85f74a7f7e02f36b0b49a:油画颜料
|
||||
a9ef3505c7fe4b66acde29f76907b07f:画板画架
|
||||
823f8d7bd96780d0b108d382c4e6ea42:石膏像
|
||||
823f8d7bd96780d06f38848189966e7d:素描本
|
||||
a9ef3505c7fe4b66e10cfa39bf43dc0f:绘图纸
|
||||
823f8d7bd96780d028d88a08a19453dc:色卡
|
||||
a9ef3505c7fe4b666f38848189966e7d:蜡笔
|
||||
823f8d7bd96780d0a7f7e02f36b0b49a:铅画纸
|
||||
7dba397e41d08d49a7f7e02f36b0b49a:裁剪刀片
|
||||
7dba397e41d08d49b108d382c4e6ea42:雕刻垫板
|
||||
7dba397e41d08d49ac899d2620c5df2b:切纸刀
|
||||
7dba397e41d08d4928d88a08a19453dc:美工刀
|
||||
356e5d8126d3aefaa7f7e02f36b0b49a:裁剪剪刀
|
||||
e9fa1ad466b79d97267e0a01017d9f44:回形针
|
||||
621bd460d751e0fca7f7e02f36b0b49a:回形针盒
|
||||
621bd460d751e0fcb108d382c4e6ea42:图钉工字钉
|
||||
e9fa1ad466b79d97e10cfa39bf43dc0f:大头针
|
||||
e9fa1ad466b79d97acde29f76907b07f:打孔机
|
||||
621bd460d751e0fc28d88a08a19453dc:票夹长尾夹
|
||||
e9fa1ad466b79d976f38848189966e7d:订书钉
|
||||
a457d6fc43c609bd1921b9e373cead75:凭证
|
||||
a457d6fc43c609bde10cfa39bf43dc0f:印油印泥
|
||||
a457d6fc43c609bd28d88a08a19453dc:报表
|
||||
a457d6fc43c609bd267e0a01017d9f44:湿手器
|
||||
a457d6fc43c609bdb108d382c4e6ea42:财务证明用品
|
||||
a457d6fc43c609bd6f38848189966e7d:账本账册
|
||||
740736cf215b7509a7f7e02f36b0b49a:电子壁纸
|
||||
195
planB/modules/xianYu/xianYu.go
Normal file
195
planB/modules/xianYu/xianYu.go
Normal file
@ -0,0 +1,195 @@
|
||||
package xianYu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
gXianYuDll *XianYuDLL
|
||||
)
|
||||
|
||||
// XianYuDLL 闲鱼工具DLL结构
|
||||
type XianYuDLL struct {
|
||||
Dll *syscall.DLL
|
||||
freeCString *syscall.Proc // 释放C字符串
|
||||
}
|
||||
|
||||
// InitXianYuDll 初始化 XianYuDLL
|
||||
func InitXianYuDll(url string) (*XianYuDLL, error) {
|
||||
if gXianYuDll != nil {
|
||||
return gXianYuDll, nil
|
||||
}
|
||||
dllPath := filepath.Join(url, "xy.dll")
|
||||
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("XianYu DLL 不存在: %s", dllPath)
|
||||
}
|
||||
dll, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载XianYu DLL 失败: %s", err)
|
||||
}
|
||||
gXianYuDll = &XianYuDLL{
|
||||
Dll: dll,
|
||||
freeCString: dll.MustFindProc("FreeCString"),
|
||||
}
|
||||
return gXianYuDll, nil
|
||||
}
|
||||
|
||||
// XianYuGoodsAdd 商品新增
|
||||
func (m *XianYuDLL) XianYuGoodsAdd(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsCreat")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsCreat: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuGoodsAddCheckIsbn 商品新增检查ISBN
|
||||
func (m *XianYuDLL) XianYuGoodsAddNew(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsCreatNew")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsCreatNew: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuLaunchGoods 商品上架
|
||||
func (m *XianYuDLL) XianYuLaunchGoods(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsPublish")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsPublish: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuGetGoodsList 拉取商品列表
|
||||
func (m *XianYuDLL) XianYuGetGoodsList(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteSelectGoodsListPrice")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteSelectGoodsListPrice: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuGetGoodsDetail 拉取商品详情
|
||||
func (m *XianYuDLL) XianYuGetGoodsDetail(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGetGoodsDetail")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGetGoodsDetail: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuExecuteGoodsDownShelf 下架商品
|
||||
func (m *XianYuDLL) XianYuExecuteGoodsDownShelf(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsDownShelf")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsDownShelf: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuExecuteGoodsUpdateStock 修改库存
|
||||
func (m *XianYuDLL) XianYuExecuteGoodsUpdateStock(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsEditStock")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsEditStock: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XianYuExecuteGoodsUpdatePrice 修改价格
|
||||
func (m *XianYuDLL) XianYuExecuteGoodsUpdatePrice(bodyJson string, configFile string) (string, error) {
|
||||
proc, err := m.Dll.FindProc("ExecuteGoodsEditPrice")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("找不到函数 ExecuteGoodsEditPrice: %v", err)
|
||||
}
|
||||
bodyJsonPtr, _ := syscall.BytePtrFromString(bodyJson)
|
||||
configFile = configFile + "\\config.ini"
|
||||
configFilePtr, _ := syscall.BytePtrFromString(configFile)
|
||||
resultPtr, _, _ := proc.Call(
|
||||
uintptr(unsafe.Pointer(bodyJsonPtr)),
|
||||
uintptr(unsafe.Pointer(configFilePtr)),
|
||||
)
|
||||
result := cStr(resultPtr)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// cStr 将 C 字符串指针转换为 Go 字符串
|
||||
func cStr(ptr uintptr) string {
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
var b []byte
|
||||
for {
|
||||
c := *(*byte)(unsafe.Pointer(ptr))
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
b = append(b, c)
|
||||
ptr++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
BIN
planB/modules/xianYu/xy.dll
Normal file
BIN
planB/modules/xianYu/xy.dll
Normal file
Binary file not shown.
239
planB/modules/xianYu/咸鱼发布dll.md
Normal file
239
planB/modules/xianYu/咸鱼发布dll.md
Normal file
@ -0,0 +1,239 @@
|
||||
##### FreeCString(str *C.char)
|
||||
|
||||
接收其他函数返回值之后,释放内存,参考示例
|
||||
|
||||
##### 内存释放示例
|
||||
|
||||
```go
|
||||
func example () {
|
||||
// ...其他逻辑
|
||||
var res = StartServer (configFile *C.char)
|
||||
FreeCString(res) //释放内存
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### StartServer (configFile *C.char)
|
||||
|
||||
启动http服务器,参数配置文件路径,不提供默认使用工程根目录config.ini
|
||||
|
||||
返回C字符串启动消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### StopServer
|
||||
|
||||
停止HTTP服务器
|
||||
|
||||
返回C字符串停止消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### GetServerStatus
|
||||
|
||||
获取服务器当前状态
|
||||
|
||||
返回C字符串指针消息,running/stopped,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### GetServerAddress
|
||||
|
||||
获取服务器监听地址
|
||||
|
||||
返回C字符串指针服务器地址消息,未运行返回空串,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### ReloadConfig(configFile *C.char)
|
||||
|
||||
重新加载配置文件,参数配置文件路径,不提供默认使用根目录config.ini
|
||||
|
||||
返回C字符串加载结果消息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 以下都需要传递appid和appSecret ###
|
||||
|
||||
##### ExecuteGoodsCreat(bodyJson *C.char, configFile *C.char)
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品创建操作,参数商品信息,参考示例
|
||||
|
||||
返回C字符串指针创建商品结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
|
||||
|
||||
##### 商品信息参考示例
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": 1228288260261189,
|
||||
"appSecret": "aq9gAwrwp6WGZkMRqKIXmnu2c2uCm82k",
|
||||
"token": "",
|
||||
"apiShopId": 0,
|
||||
"typePlatform": 4,
|
||||
"shopId": 0,
|
||||
"shopToken": "",
|
||||
"shopName": "",
|
||||
"province": 210000,
|
||||
"city": 210100,
|
||||
"district": 210101,
|
||||
"typeClass": "",
|
||||
"typeGoods": "",
|
||||
"catIds": "d14d229692616168b108d382c4e6ea42",
|
||||
"shop": [
|
||||
{
|
||||
"userName": "xy938400231518",
|
||||
"province": 210000,
|
||||
"city": 210100,
|
||||
"district": 210101,
|
||||
"title": "牧羊少年奇幻之旅",
|
||||
"content": "牧羊少年奇幻之旅",
|
||||
"mainImgs": ["https://img.cdn1.vip/i/68cf5cb4e5840_1758420148.webp"],
|
||||
"contentImgs": []
|
||||
}
|
||||
],
|
||||
"stuffStatus": 90,
|
||||
"bookData": [
|
||||
{
|
||||
"ISBN": "9787530217054",
|
||||
"Title": "牧羊少年奇幻之旅",
|
||||
"Author": "保罗·柯艾略",
|
||||
"Publisher": "北京十月文艺出版",
|
||||
"itemBizType": 2,
|
||||
"spBizType": 24,
|
||||
"prices": [199999, 299999],
|
||||
"stock": 100,
|
||||
"catIds": "22e1d81dc4cf3a25a7f7e02f36b0b49a"
|
||||
}
|
||||
],
|
||||
"itemKey": "itemAAAAA1111"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsPublish(bodyJson *C.char, configFile *C.char)
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品上架操作,参数上架信息,参考示例
|
||||
|
||||
返回C字符串指针行商品上架结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 上架信息参考示例
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"user_name": ["xy938400231518"],
|
||||
"specify_publish_time": "",
|
||||
"notify_url": ""
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 追加下架,改价,擦亮 ####
|
||||
|
||||
##### ExecuteGoodsDownShelf(bodyJson *C.char, configFile *C.char) ######
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品下架操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品下架结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 下架信息参考示例 #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsFlash(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品擦亮操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品擦亮结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 擦亮信息参考示例 #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsEditPrice(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品改价操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 改价信息参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"price": 550000,
|
||||
"originalPrice": 770000
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteGoodsEditStock(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
执行商品改库存操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 改库存信息参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
"product_id": 1250927879325125,
|
||||
"stock": 10
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### ExecuteSelectGoodsListPrice(bodyJson *C.char, configFile *C.char) #####
|
||||
|
||||
*管道通信直接调用此函数*
|
||||
|
||||
查询店铺列表操作,参数管家商品ID,参考示例
|
||||
|
||||
返回C字符串指针行商品改价结果信息,接收后使用FreeCString进行内存释放
|
||||
|
||||
##### 查询参考示例(单位:分) #####
|
||||
|
||||
```json
|
||||
{
|
||||
//online_time 字段可传空
|
||||
"online_time": [
|
||||
1690300800,
|
||||
1690366883
|
||||
],
|
||||
"product_status": 22
|
||||
}
|
||||
```
|
||||
|
||||
29
planB/planB.md
Normal file
29
planB/planB.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Plan B
|
||||
## 目录结构
|
||||
```gotemplate
|
||||
dispatcher 具体执行的平台操作(工厂模式,发布商品、上下架等)
|
||||
|-kongfuzi 孔夫子
|
||||
|-pinduoduo 拼多多
|
||||
|-xianyu 闲鱼
|
||||
initialization 初始化
|
||||
|-config 初始化配置文件
|
||||
|-golabl 初始化全局变量
|
||||
|-platform 初始化任务平台(拼多多、闲鱼等)
|
||||
|-pool 初始化协程池
|
||||
|-redis 初始化redis
|
||||
|-speed 初始化限速器
|
||||
|-task 初始化任务(获取header与footer)
|
||||
|-taskType 初始化任务类型(发布商品、上下架等)
|
||||
|-init.go 初始化文件
|
||||
interfaces 工厂模式接口
|
||||
logic 逻辑执行
|
||||
modules DLL模块
|
||||
service 服务(针对数据库相关操作)
|
||||
tool 工具
|
||||
type 结构体
|
||||
|-pinduoduo 拼多多结构体
|
||||
|-xianyu 闲鱼结构体
|
||||
validation 验证器
|
||||
|
||||
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user