feat: add toolbox query, upload module, update config and gitignore
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,6 +22,8 @@ data/*
|
||||
/app/api
|
||||
/app/main/api/main
|
||||
/app/main/api/_*
|
||||
__debug_bin.exe
|
||||
**/.../
|
||||
|
||||
|
||||
# 文档目录
|
||||
|
||||
29
app/main/api/desc/front/toolbox.api
Normal file
29
app/main/api/desc/front/toolbox.api
Normal file
@@ -0,0 +1,29 @@
|
||||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "工具箱服务"
|
||||
desc: "免费工具箱(天行聚合等)"
|
||||
version: "v1"
|
||||
)
|
||||
|
||||
//============================> toolbox v1 <============================
|
||||
@server (
|
||||
prefix: api/v1
|
||||
group: toolbox
|
||||
)
|
||||
service main {
|
||||
@doc "工具箱统一查询"
|
||||
@handler toolboxQuery
|
||||
post /toolbox/query (ToolboxQueryReq) returns (ToolboxQueryResp)
|
||||
}
|
||||
|
||||
type (
|
||||
ToolboxQueryReq {
|
||||
ToolKey string `json:"tool_key"`
|
||||
Params map[string]interface{} `json:"params,optional"`
|
||||
}
|
||||
ToolboxQueryResp {
|
||||
ToolKey string `json:"tool_key"`
|
||||
Result map[string]interface{} `json:"result"`
|
||||
}
|
||||
)
|
||||
44
app/main/api/desc/front/upload.api
Normal file
44
app/main/api/desc/front/upload.api
Normal file
@@ -0,0 +1,44 @@
|
||||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "上传服务"
|
||||
desc: "图片 Base64 上传与文件访问"
|
||||
version: "v1"
|
||||
)
|
||||
|
||||
//============================> upload v1 <============================
|
||||
// 访问已上传图片(直接输出二进制,无 JSON 包装)
|
||||
@server (
|
||||
prefix: api/v1
|
||||
group: upload
|
||||
)
|
||||
service main {
|
||||
@doc "访问已上传图片"
|
||||
@handler serveUpload
|
||||
get /upload/file/:name (ServeUploadFileReq)
|
||||
}
|
||||
|
||||
type ServeUploadFileReq {
|
||||
Name string `path:"name"`
|
||||
}
|
||||
|
||||
@server (
|
||||
prefix: api/v1
|
||||
group: upload
|
||||
jwt: JwtAuth
|
||||
middleware: AuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@doc "图片 Base64 上传"
|
||||
@handler uploadImage
|
||||
post /upload/image (UploadImageReq) returns (UploadImageResp)
|
||||
}
|
||||
|
||||
type (
|
||||
UploadImageReq {
|
||||
ImageBase64 string `json:"image_base64"`
|
||||
}
|
||||
UploadImageResp {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
)
|
||||
@@ -14,6 +14,8 @@ import "./front/product.api"
|
||||
import "./front/agent.api"
|
||||
import "./front/app.api"
|
||||
import "./front/authorization.api"
|
||||
import "./front/toolbox.api"
|
||||
import "./front/upload.api"
|
||||
// 后台
|
||||
import "./admin/auth.api"
|
||||
import "./admin/menu.api"
|
||||
@@ -29,3 +31,6 @@ import "./admin/admin_query.api"
|
||||
import "./admin/admin_agent.api"
|
||||
import "./admin/admin_api.api"
|
||||
import "./admin/admin_role_api.api"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -89,6 +89,10 @@ Tianyuanapi:
|
||||
Key: "04c6b4c559be6d5ba5351c04c8713a64"
|
||||
BaseURL: "https://api.tianyuanapi.com"
|
||||
Timeout: 60
|
||||
tianxingjuhe:
|
||||
url: "https://apis.tianapi.com"
|
||||
key: "4ceffb1ffb95b83230b9a9c9df2467e1"
|
||||
timeout: 30
|
||||
Authorization:
|
||||
FileBaseURL: "https://www.quannengcha.com/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||
Promotion:
|
||||
|
||||
@@ -78,6 +78,10 @@ Tianyuanapi:
|
||||
Key: "04c6b4c559be6d5ba5351c04c8713a64"
|
||||
BaseURL: "https://api.tianyuanapi.com"
|
||||
Timeout: 60
|
||||
tianxingjuhe:
|
||||
url: "https://apis.tianapi.com"
|
||||
key: "4ceffb1ffb95b83230b9a9c9df2467e1"
|
||||
timeout: 30
|
||||
Authorization:
|
||||
FileBaseURL: "https://www.quannengcha.com/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||
Promotion:
|
||||
|
||||
@@ -24,7 +24,8 @@ type Config struct {
|
||||
Query QueryConfig
|
||||
AdminConfig AdminConfig
|
||||
TaxConfig TaxConfig
|
||||
Promotion PromotionConfig // 推广链接配置
|
||||
Promotion PromotionConfig // 推广链接配置
|
||||
Tianxingjuhe TianxingjuheConfig // 天行聚合API配置
|
||||
}
|
||||
|
||||
// JwtAuth 用于 JWT 鉴权配置
|
||||
@@ -126,3 +127,10 @@ type PromotionConfig struct {
|
||||
PromotionDomain string // 推广域名(用于生成短链)
|
||||
OfficialDomain string // 正式站点域名(短链重定向的目标域名)
|
||||
}
|
||||
|
||||
// TianxingjuheConfig 天行聚合API配置
|
||||
type TianxingjuheConfig struct {
|
||||
URL string // API基础URL
|
||||
Key string // API密钥
|
||||
Timeout int // 超时时间(秒),默认30秒
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
pay "qnc-server/app/main/api/internal/handler/pay"
|
||||
product "qnc-server/app/main/api/internal/handler/product"
|
||||
query "qnc-server/app/main/api/internal/handler/query"
|
||||
toolbox "qnc-server/app/main/api/internal/handler/toolbox"
|
||||
upload "qnc-server/app/main/api/internal/handler/upload"
|
||||
user "qnc-server/app/main/api/internal/handler/user"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
|
||||
@@ -1091,6 +1093,46 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// 工具箱统一查询
|
||||
Method: http.MethodPost,
|
||||
Path: "/toolbox/query",
|
||||
Handler: toolbox.ToolboxQueryHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// 访问已上传图片
|
||||
Method: http.MethodGet,
|
||||
Path: "/upload/file/:name",
|
||||
Handler: upload.ServeUploadHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
// 图片 Base64 上传
|
||||
Method: http.MethodPost,
|
||||
Path: "/upload/image",
|
||||
Handler: upload.UploadImageHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
|
||||
25
app/main/api/internal/handler/toolbox/toolboxqueryhandler.go
Normal file
25
app/main/api/internal/handler/toolbox/toolboxqueryhandler.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/logic/toolbox"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func ToolboxQueryHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ToolboxQueryReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := toolbox.NewToolboxQueryLogic(r.Context(), svcCtx)
|
||||
resp, err := l.ToolboxQuery(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
26
app/main/api/internal/handler/upload/serveuploadhandler.go
Normal file
26
app/main/api/internal/handler/upload/serveuploadhandler.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
uploadlogic "qnc-server/app/main/api/internal/logic/upload"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func ServeUploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ServeUploadFileReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := uploadlogic.NewServeUploadLogic(r.Context(), svcCtx)
|
||||
if err := l.ServeUpload(req.Name, w); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
25
app/main/api/internal/handler/upload/uploadimagehandler.go
Normal file
25
app/main/api/internal/handler/upload/uploadimagehandler.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
uploadlogic "qnc-server/app/main/api/internal/logic/upload"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func UploadImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UploadImageReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := uploadlogic.NewUploadImageLogic(r.Context(), svcCtx)
|
||||
resp, err := l.UploadImage(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
47
app/main/api/internal/logic/toolbox/toolboxquerylogic.go
Normal file
47
app/main/api/internal/logic/toolbox/toolboxquerylogic.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ToolboxQueryLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewToolboxQueryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToolboxQueryLogic {
|
||||
return &ToolboxQueryLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ToolboxQueryLogic) ToolboxQuery(req *types.ToolboxQueryReq) (*types.ToolboxQueryResp, error) {
|
||||
if req.ToolKey == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("tool_key 不能为空"), "")
|
||||
}
|
||||
if l.svcCtx.ToolboxService == nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "工具箱服务未初始化")
|
||||
}
|
||||
params := req.Params
|
||||
if params == nil {
|
||||
params = map[string]interface{}{}
|
||||
}
|
||||
result, err := l.svcCtx.ToolboxService.Query(l.ctx, req.ToolKey, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ToolboxQueryResp{
|
||||
ToolKey: req.ToolKey,
|
||||
Result: result,
|
||||
}, nil
|
||||
}
|
||||
67
app/main/api/internal/logic/upload/serveuploadlogic.go
Normal file
67
app/main/api/internal/logic/upload/serveuploadlogic.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ServeUploadLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewServeUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ServeUploadLogic {
|
||||
return &ServeUploadLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ServeUploadLogic) ServeUpload(name string, w http.ResponseWriter) error {
|
||||
name = filepath.Base(strings.TrimSpace(name))
|
||||
if name == "" || name == "." || name == ".." || !isSafeUploadFileName(name) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("无效的文件名"), "")
|
||||
}
|
||||
fullPath := filepath.Join(uploadImageDir, name)
|
||||
data, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("文件不存在"), "")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取文件失败: %v", err)
|
||||
}
|
||||
w.Header().Set("Cache-Control", "public, max-age=86400")
|
||||
switch filepath.Ext(name) {
|
||||
case ".png":
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
case ".gif":
|
||||
w.Header().Set("Content-Type", "image/gif")
|
||||
case ".webp":
|
||||
w.Header().Set("Content-Type", "image/webp")
|
||||
default:
|
||||
w.Header().Set("Content-Type", "image/jpeg")
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func isSafeUploadFileName(name string) bool {
|
||||
for _, c := range name {
|
||||
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
93
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
93
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
const uploadImageDir = "uploads/images"
|
||||
|
||||
type UploadImageLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadImageLogic {
|
||||
return &UploadImageLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UploadImageLogic) UploadImage(req *types.UploadImageReq) (*types.UploadImageResp, error) {
|
||||
raw := strings.TrimSpace(req.ImageBase64)
|
||||
if raw == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("image_base64 不能为空"), "")
|
||||
}
|
||||
if idx := strings.Index(raw, ","); idx >= 0 {
|
||||
raw = raw[idx+1:]
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("图片 Base64 解析失败"), "")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("图片内容为空"), "")
|
||||
}
|
||||
if len(data) > 5*1024*1024 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("图片不能超过 5MB"), "")
|
||||
}
|
||||
|
||||
ext := detectImageExt(data)
|
||||
fileName := fmt.Sprintf("%s%s", uuid.NewString(), ext)
|
||||
dir := uploadImageDir
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建上传目录失败: %v", err)
|
||||
}
|
||||
fullPath := filepath.Join(dir, fileName)
|
||||
if err := os.WriteFile(fullPath, data, 0o644); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存图片失败: %v", err)
|
||||
}
|
||||
|
||||
base := publicAPIBase(l.svcCtx.Config.Promotion.OfficialDomain)
|
||||
url := fmt.Sprintf("%s/api/v1/upload/file/%s", base, fileName)
|
||||
return &types.UploadImageResp{Url: url}, nil
|
||||
}
|
||||
|
||||
func detectImageExt(data []byte) string {
|
||||
if len(data) >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF {
|
||||
return ".jpg"
|
||||
}
|
||||
if len(data) >= 8 && string(data[0:8]) == "\x89PNG\r\n\x1a\n" {
|
||||
return ".png"
|
||||
}
|
||||
if len(data) >= 6 && string(data[0:6]) == "GIF87a" || string(data[0:6]) == "GIF89a" {
|
||||
return ".gif"
|
||||
}
|
||||
if len(data) >= 12 && string(data[8:12]) == "WEBP" {
|
||||
return ".webp"
|
||||
}
|
||||
return ".jpg"
|
||||
}
|
||||
|
||||
func publicAPIBase(officialDomain string) string {
|
||||
base := strings.TrimRight(officialDomain, "/")
|
||||
if base != "" {
|
||||
return base
|
||||
}
|
||||
return "http://127.0.0.1:8888"
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
||||
encryptData = encryptedEmptyData
|
||||
} else {
|
||||
// 正常模式:调用API请求服务
|
||||
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
|
||||
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id, order.OrderNo)
|
||||
if err != nil {
|
||||
return l.handleError(ctx, err, order, query)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
tianyuanapi "qnc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -75,7 +77,8 @@ type APIResponseData struct {
|
||||
}
|
||||
|
||||
// ProcessRequests 处理请求
|
||||
func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]byte, error) {
|
||||
// orderNo: 当前查询订单号,供异步车辆类接口生成 return_url 回调地址
|
||||
func (a *ApiRequestService) ProcessRequests(params []byte, productID string, orderNo string) ([]byte, error) {
|
||||
var ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -112,6 +115,25 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
|
||||
if len(featureList) == 0 {
|
||||
return nil, errors.New("处理请求错误,产品无对应接口功能")
|
||||
}
|
||||
|
||||
// 在原始 params 上附加 order_no,供异步车辆类接口自动生成回调地址
|
||||
var baseParams map[string]interface{}
|
||||
if err := json.Unmarshal(params, &baseParams); err != nil {
|
||||
logx.Errorf("解析查询参数失败, Params: %s, Error: %v", string(params), err)
|
||||
return nil, fmt.Errorf("解析查询参数失败: %w", err)
|
||||
}
|
||||
if mobile, exists := baseParams["mobile"]; exists {
|
||||
baseParams["mobile_no"] = mobile
|
||||
}
|
||||
if orderNo != "" {
|
||||
baseParams["order_no"] = orderNo
|
||||
}
|
||||
paramsWithOrder, err := json.Marshal(baseParams)
|
||||
if err != nil {
|
||||
logx.Errorf("序列化查询参数失败, Params: %s, Error: %v", string(params), err)
|
||||
return nil, fmt.Errorf("序列化查询参数失败: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
resultsCh = make(chan APIResponseData, len(featureList))
|
||||
@@ -152,7 +174,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
|
||||
tryCount := 0
|
||||
for {
|
||||
tryCount++
|
||||
resp, preprocessErr = a.PreprocessRequestApi(params, feature.ApiId)
|
||||
resp, preprocessErr = a.PreprocessRequestApi(paramsWithOrder, feature.ApiId)
|
||||
if preprocessErr == nil {
|
||||
break
|
||||
}
|
||||
@@ -224,7 +246,21 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
|
||||
"QYGL6F2D": (*ApiRequestService).ProcessQYGL6F2DRequest,
|
||||
"JRZQ8203": (*ApiRequestService).ProcessJRZQ8203Request,
|
||||
"JRZQ4AA8": (*ApiRequestService).ProcessJRZQ4AA8Request,
|
||||
"QCXGGB2Q": (*ApiRequestService).ProcessQCXGGB2QRequest,
|
||||
"QCXGYTS2": (*ApiRequestService).ProcessQCXGYTS2Request,
|
||||
"QCXG5F3A": (*ApiRequestService).ProcessQCXG5F3ARequest,
|
||||
"QCXG7A2B": (*ApiRequestService).ProcessQCXG7A2BRequest,
|
||||
"QCXG9P1C": (*ApiRequestService).ProcessQCXG9P1CFRequest,
|
||||
"QCXG4D2E": (*ApiRequestService).ProcessQCXG4D2ERequest,
|
||||
"QCXG5U0Z": (*ApiRequestService).ProcessQCXG5U0ZRequest,
|
||||
"QCXG1U4U": (*ApiRequestService).ProcessQCXG1U4URequest,
|
||||
"QCXGY7F2": (*ApiRequestService).ProcessQCXGY7F2Request,
|
||||
"QCXG1H7Y": (*ApiRequestService).ProcessQCXG1H7YRequest,
|
||||
"QCXG4I1Z": (*ApiRequestService).ProcessQCXG4I1ZRequest,
|
||||
"QCXG3Y6B": (*ApiRequestService).ProcessQCXG3Y6BRequest,
|
||||
"QCXG3Z3L": (*ApiRequestService).ProcessQCXG3Z3LRequest,
|
||||
"QCXGP00W": (*ApiRequestService).ProcessQCXGP00WRequest,
|
||||
"QCXG6B4E": (*ApiRequestService).ProcessQCXG6B4ERequest,
|
||||
"DWBG8B4D": (*ApiRequestService).ProcessDWBG8B4DRequest,
|
||||
"DWBG6A2C": (*ApiRequestService).ProcessDWBG6A2CRequest,
|
||||
"JRZQ4B6C": (*ApiRequestService).ProcessJRZQ4B6CRequest,
|
||||
@@ -1126,24 +1162,290 @@ func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error
|
||||
return nil, fmt.Errorf("响应code错误%s", code.String())
|
||||
}
|
||||
|
||||
// ProcessQCXGGB2QRequest 人车核验简版
|
||||
func (a *ApiRequestService) ProcessQCXGGB2QRequest(params []byte) ([]byte, error) {
|
||||
plateNo := gjson.GetBytes(params, "plate_no")
|
||||
carplateType := gjson.GetBytes(params, "carplate_type")
|
||||
name := gjson.GetBytes(params, "name")
|
||||
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
|
||||
return nil, errors.New("api请求, QCXGGB2Q, 获取相关参数失败")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXGGB2Q", map[string]interface{}{
|
||||
"plate_no": plateNo.String(),
|
||||
"carplate_type": carplateType.String(),
|
||||
"name": name.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
// ProcessQCXGYTS2Request 人车核验详版
|
||||
func (a *ApiRequestService) ProcessQCXGYTS2Request(params []byte) ([]byte, error) {
|
||||
plateNo := gjson.GetBytes(params, "plate_no")
|
||||
carplateType := gjson.GetBytes(params, "carplate_type")
|
||||
name := gjson.GetBytes(params, "name")
|
||||
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
|
||||
return nil, errors.New("api请求, QCXGYTS2, 获取相关参数失败")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXGYTS2", map[string]interface{}{
|
||||
"plate_no": plateNo.String(),
|
||||
"carplate_type": carplateType.String(),
|
||||
"name": name.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
// ProcessQCXG5F3ARequest 名下车辆(车牌)
|
||||
func (a *ApiRequestService) ProcessQCXG5F3ARequest(params []byte) ([]byte, error) {
|
||||
idCard := gjson.GetBytes(params, "id_card")
|
||||
name := gjson.GetBytes(params, "name")
|
||||
if !idCard.Exists() || !name.Exists() {
|
||||
return nil, errors.New("api请求, QCXG5F3A, 获取相关参数失败")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG5F3A", map[string]interface{}{
|
||||
"id_card": idCard.String(),
|
||||
"name": name.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
// ProcessQCXG7A2BRequest 名下车辆
|
||||
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
|
||||
idCard := gjson.GetBytes(params, "id_card")
|
||||
if !idCard.Exists() {
|
||||
return nil, errors.New("api请求, QCXG7A2B, 获取相关参数失败")
|
||||
}
|
||||
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG7A2B", map[string]interface{}{
|
||||
"id_card": idCard.String(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
// ProcessQCXG9P1CFRequest 名下车辆车牌查询 A
|
||||
func (a *ApiRequestService) ProcessQCXG9P1CFRequest(params []byte) ([]byte, error) {
|
||||
idCard := gjson.GetBytes(params, "id_card")
|
||||
name := gjson.GetBytes(params, "name")
|
||||
mobile := gjson.GetBytes(params, "mobile")
|
||||
if !idCard.Exists() || !name.Exists() || !mobile.Exists() {
|
||||
return nil, errors.New("api请求, QCXG9P1C, 获取相关参数失败")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG9P1C", map[string]interface{}{
|
||||
"id_card": idCard.String(),
|
||||
"authorized": "1",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG4D2ERequest(params []byte) ([]byte, error) {
|
||||
m := map[string]interface{}{}
|
||||
if err := json.Unmarshal(params, &m); err != nil {
|
||||
return nil, fmt.Errorf("api请求, QCXG4D2E, 解析参数失败: %w", err)
|
||||
}
|
||||
body := map[string]interface{}{}
|
||||
if v, ok := m["user_type"].(string); ok && v != "" {
|
||||
body["user_type"] = v
|
||||
} else {
|
||||
body["user_type"] = "1"
|
||||
}
|
||||
if v, ok := m["id_card"].(string); ok {
|
||||
body["id_card"] = v
|
||||
} else {
|
||||
return nil, errors.New("api请求, QCXG4D2E, 缺少 id_card")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG4D2E", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG5U0ZRequest(params []byte) ([]byte, error) {
|
||||
vin := gjson.GetBytes(params, "vin_code")
|
||||
if !vin.Exists() || vin.String() == "" {
|
||||
return nil, errors.New("api请求, QCXG5U0Z, 缺少 vin_code")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG5U0Z", map[string]interface{}{"vin_code": vin.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG1U4URequest(params []byte) ([]byte, error) {
|
||||
body := buildVehicleBody(params, []string{"vin_code", "image_url"}, nil)
|
||||
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||
if body["vin_code"] == nil || body["image_url"] == nil || orderNo == "" {
|
||||
return nil, errors.New("api请求, QCXG1U4U, 缺少必填参数 vin_code/image_url/order_no")
|
||||
}
|
||||
logx.Infof("vehicle api request QCXG1U4U, order_no=%s, vin_code=%v, image_url=%v", orderNo, body["vin_code"], body["image_url"])
|
||||
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG1U4U")
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG1U4U", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXGY7F2Request(params []byte) ([]byte, error) {
|
||||
body := buildVehicleBody(params, []string{"vin_code", "vehicle_location", "first_registrationdate"}, nil)
|
||||
if body["vin_code"] == nil || body["vehicle_location"] == nil || body["first_registrationdate"] == nil {
|
||||
return nil, errors.New("api请求, QCXGY7F2, 缺少必填参数 vin_code/vehicle_location/first_registrationdate")
|
||||
}
|
||||
logx.Infof("vehicle api request QCXGY7F2, vin_code=%v, vehicle_location=%v, first_registrationdate=%v", body["vin_code"], body["vehicle_location"], body["first_registrationdate"])
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXGY7F2", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG1H7YRequest(params []byte) ([]byte, error) {
|
||||
vin := gjson.GetBytes(params, "vin_code")
|
||||
plate := gjson.GetBytes(params, "car_license")
|
||||
if !vin.Exists() || vin.String() == "" || !plate.Exists() || plate.String() == "" {
|
||||
return nil, errors.New("api请求, QCXG1H7Y, 缺少 vin_code 或 car_license")
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"vin_code": vin.String(),
|
||||
"plate_no": plate.String(),
|
||||
}
|
||||
logx.Infof("vehicle api request QCXG1H7Y, vin_code=%s, plate_no=%s", vin.String(), plate.String())
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG1H7Y", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG4I1ZRequest(params []byte) ([]byte, error) {
|
||||
vin := gjson.GetBytes(params, "vin_code")
|
||||
if !vin.Exists() || vin.String() == "" {
|
||||
return nil, errors.New("api请求, QCXG4I1Z, 缺少 vin_code")
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG4I1Z", map[string]interface{}{"vin_code": vin.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG3Y6BRequest(params []byte) ([]byte, error) {
|
||||
body := buildVehicleBody(params, []string{"vin_code"}, nil)
|
||||
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||
if body["vin_code"] == nil || orderNo == "" {
|
||||
return nil, errors.New("api请求, QCXG3Y6B, 缺少必填参数 vin_code/order_no")
|
||||
}
|
||||
logx.Infof("vehicle api request QCXG3Y6B, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
|
||||
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Y6B")
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG3Y6B", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG3Z3LRequest(params []byte) ([]byte, error) {
|
||||
body := buildVehicleBody(params, []string{"vin_code"}, nil)
|
||||
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||
if body["vin_code"] == nil || orderNo == "" {
|
||||
return nil, errors.New("api请求, QCXG3Z3L, 缺少必填参数 vin_code/order_no")
|
||||
}
|
||||
logx.Infof("vehicle api request QCXG3Z3L, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
|
||||
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Z3L")
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG3Z3L", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXGP00WRequest(params []byte) ([]byte, error) {
|
||||
vin := gjson.GetBytes(params, "vin_code")
|
||||
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||
vlphoto := gjson.GetBytes(params, "vlphoto_data")
|
||||
if !vin.Exists() || vin.String() == "" || orderNo == "" || !vlphoto.Exists() || vlphoto.String() == "" {
|
||||
return nil, errors.New("api请求, QCXGP00W, 缺少必填参数 vin_code/order_no/vlphoto_data")
|
||||
}
|
||||
logx.Infof("vehicle api request QCXGP00W, order_no=%s, vin_code=%s, vlphoto_data_len=%d", orderNo, vin.String(), len(vlphoto.String()))
|
||||
key, err := hex.DecodeString(a.config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("api请求, QCXGP00W, 密钥解析失败: %w", err)
|
||||
}
|
||||
encData, err := crypto.AesEncrypt([]byte(vlphoto.String()), key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("api请求, QCXGP00W, 加密行驶证数据失败: %w", err)
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXGP00W", map[string]interface{}{
|
||||
"vin_code": vin.String(),
|
||||
"return_url": a.buildVehicleCallbackURL(orderNo, "QCXGP00W"),
|
||||
"data": encData,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
func (a *ApiRequestService) ProcessQCXG6B4ERequest(params []byte) ([]byte, error) {
|
||||
vin := gjson.GetBytes(params, "vin_code")
|
||||
if !vin.Exists() || vin.String() == "" {
|
||||
return nil, errors.New("api请求, QCXG6B4E, 缺少 vin_code")
|
||||
}
|
||||
auth := gjson.GetBytes(params, "authorized").String()
|
||||
if auth == "" {
|
||||
auth = "1"
|
||||
}
|
||||
resp, err := a.tianyuanapi.CallInterface("QCXG6B4E", map[string]interface{}{
|
||||
"vin_code": vin.String(),
|
||||
"authorized": auth,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertTianyuanResponse(resp)
|
||||
}
|
||||
|
||||
// buildVehicleBody 从 params 中取 required 与 optional 键,仅非空才写入 body
|
||||
func buildVehicleBody(params []byte, required, optional []string) map[string]interface{} {
|
||||
body := make(map[string]interface{})
|
||||
for _, k := range required {
|
||||
v := gjson.GetBytes(params, k)
|
||||
if v.Exists() {
|
||||
body[k] = v.String()
|
||||
}
|
||||
}
|
||||
for _, k := range optional {
|
||||
v := gjson.GetBytes(params, k)
|
||||
if v.Exists() && v.String() != "" {
|
||||
body[k] = v.String()
|
||||
}
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
// buildVehicleCallbackURL 生成车辆类接口的异步回调地址
|
||||
func (a *ApiRequestService) buildVehicleCallbackURL(orderNo, apiID string) string {
|
||||
base := strings.TrimRight(a.config.Promotion.OfficialDomain, "/")
|
||||
if base == "" {
|
||||
return fmt.Sprintf("/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", orderNo, apiID)
|
||||
}
|
||||
return fmt.Sprintf("%s/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", base, orderNo, apiID)
|
||||
}
|
||||
|
||||
// ProcessYYSY09CDRequest 三要素
|
||||
func (a *ApiRequestService) ProcessYYSY09CDRequest(params []byte) ([]byte, error) {
|
||||
name := gjson.GetBytes(params, "name")
|
||||
|
||||
1984
app/main/api/internal/service/apirequestService2.md
Normal file
1984
app/main/api/internal/service/apirequestService2.md
Normal file
File diff suppressed because it is too large
Load Diff
150
app/main/api/internal/service/tianxingjuhe_sdk/client.go
Normal file
150
app/main/api/internal/service/tianxingjuhe_sdk/client.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package tianxingjuhe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client 天行聚合API客户端
|
||||
type Client struct {
|
||||
baseURL string
|
||||
key string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// Config 客户端配置
|
||||
type Config struct {
|
||||
BaseURL string // API基础URL
|
||||
Key string // API密钥
|
||||
Timeout int // 超时时间(秒)
|
||||
}
|
||||
|
||||
// Response 通用API响应结构
|
||||
type Response struct {
|
||||
Code int `json:"code"` // 状态码,200表示成功
|
||||
Msg string `json:"msg"` // 返回说明
|
||||
Result interface{} `json:"result"` // 返回结果集,具体内容根据接口而定
|
||||
}
|
||||
|
||||
// NewClient 创建新的客户端实例
|
||||
func NewClient(config Config) (*Client, error) {
|
||||
if config.BaseURL == "" {
|
||||
return nil, fmt.Errorf("baseURL不能为空")
|
||||
}
|
||||
if config.Key == "" {
|
||||
return nil, fmt.Errorf("key不能为空")
|
||||
}
|
||||
|
||||
return &Client{
|
||||
baseURL: config.BaseURL,
|
||||
key: config.Key,
|
||||
client: &http.Client{
|
||||
Timeout: time.Duration(config.Timeout) * time.Second,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get 发送GET请求
|
||||
func (c *Client) Get(endpoint string, params map[string]interface{}) (*Response, error) {
|
||||
// 构建完整URL
|
||||
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
|
||||
|
||||
// 添加请求参数
|
||||
queryParams := url.Values{}
|
||||
for key, value := range params {
|
||||
queryParams.Set(key, fmt.Sprintf("%v", value))
|
||||
}
|
||||
|
||||
// 添加key参数
|
||||
queryParams.Set("key", c.key)
|
||||
|
||||
// 拼接查询参数
|
||||
if len(queryParams) > 0 {
|
||||
fullURL = fmt.Sprintf("%s?%s", fullURL, queryParams.Encode())
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("GET", fullURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
|
||||
|
||||
// 发送请求
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var apiResp Response
|
||||
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
||||
}
|
||||
|
||||
return &apiResp, nil
|
||||
}
|
||||
|
||||
// Post 发送POST请求
|
||||
func (c *Client) Post(endpoint string, params map[string]interface{}) (*Response, error) {
|
||||
// 构建完整URL
|
||||
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
|
||||
|
||||
// 添加key参数
|
||||
if params == nil {
|
||||
params = make(map[string]interface{})
|
||||
}
|
||||
params["key"] = c.key
|
||||
|
||||
// 序列化请求体
|
||||
requestBody, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求体失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", fullURL, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
|
||||
|
||||
// 发送请求
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var apiResp Response
|
||||
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
||||
}
|
||||
|
||||
return &apiResp, nil
|
||||
}
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"qnc-server/app/main/model"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"qnc-server/app/main/model"
|
||||
)
|
||||
|
||||
// TianyuanapiCallLogService 天元API调用记录服务
|
||||
|
||||
8276
app/main/api/internal/service/toolboxService.go
Normal file
8276
app/main/api/internal/service/toolboxService.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import (
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/app/main/api/internal/middleware"
|
||||
"qnc-server/app/main/api/internal/service"
|
||||
tianxingjuhe "qnc-server/app/main/api/internal/service/tianxingjuhe_sdk"
|
||||
tianyuanapi "qnc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
||||
"qnc-server/app/main/model"
|
||||
"time"
|
||||
@@ -99,6 +100,7 @@ type ServiceContext struct {
|
||||
DictService *service.DictService
|
||||
ImageService *service.ImageService
|
||||
AuthorizationService *service.AuthorizationService
|
||||
ToolboxService *service.ToolboxService
|
||||
}
|
||||
|
||||
// NewServiceContext 创建服务上下文
|
||||
@@ -201,6 +203,23 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel)
|
||||
imageService := service.NewImageService()
|
||||
|
||||
tianxingjuheTimeout := c.Tianxingjuhe.Timeout
|
||||
if tianxingjuheTimeout <= 0 {
|
||||
tianxingjuheTimeout = 30
|
||||
}
|
||||
tianxingjuheClient, tjErr := tianxingjuhe.NewClient(tianxingjuhe.Config{
|
||||
BaseURL: c.Tianxingjuhe.URL,
|
||||
Key: c.Tianxingjuhe.Key,
|
||||
Timeout: tianxingjuheTimeout,
|
||||
})
|
||||
if tjErr != nil {
|
||||
logx.Errorf("初始化天行聚合API失败: %+v", tjErr)
|
||||
}
|
||||
var toolboxService *service.ToolboxService
|
||||
if tianxingjuheClient != nil {
|
||||
toolboxService = service.NewToolboxService(tianxingjuheClient)
|
||||
}
|
||||
|
||||
// ============================== 异步任务服务 ==============================
|
||||
asynqServer := asynq.NewServer(
|
||||
asynq.RedisClientOpt{Addr: c.CacheRedis[0].Host, Password: c.CacheRedis[0].Pass},
|
||||
@@ -296,6 +315,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
DictService: dictService,
|
||||
ImageService: imageService,
|
||||
AuthorizationService: authorizationService,
|
||||
ToolboxService: toolboxService,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2199,6 +2199,10 @@ type RoleListItem struct {
|
||||
MenuIds []string `json:"menu_ids"` // 关联的菜单ID列表
|
||||
}
|
||||
|
||||
type ServeUploadFileReq struct {
|
||||
Name string `path:"name"`
|
||||
}
|
||||
|
||||
type ShortLinkRedirectResp struct {
|
||||
}
|
||||
|
||||
@@ -2251,6 +2255,16 @@ type TeamStatisticsResp struct {
|
||||
MonthNewMembers int64 `json:"month_new_members"` // 本月新增成员
|
||||
}
|
||||
|
||||
type ToolboxQueryReq struct {
|
||||
ToolKey string `json:"tool_key"`
|
||||
Params map[string]interface{} `json:"params,optional"`
|
||||
}
|
||||
|
||||
type ToolboxQueryResp struct {
|
||||
ToolKey string `json:"tool_key"`
|
||||
Result map[string]interface{} `json:"result"`
|
||||
}
|
||||
|
||||
type UpdateMenuReq struct {
|
||||
Id string `path:"id"` // 菜单ID
|
||||
Pid *string `json:"pid,optional"` // 父菜单ID
|
||||
@@ -2334,6 +2348,14 @@ type UpgradeSubordinateResp struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type UploadImageReq struct {
|
||||
ImageBase64 string `json:"image_base64"`
|
||||
}
|
||||
|
||||
type UploadImageResp struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id string `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
|
||||
Reference in New Issue
Block a user