From d31b52d38a1f60a639a7fe7b1b57aba7257891ee Mon Sep 17 00:00:00 2001 From: wukesheng Date: Tue, 30 Apr 2024 17:57:27 +0800 Subject: [PATCH] =?UTF-8?q?meituan-union=E5=B9=B3=E5=8F=B0api=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + api/api.go | 20 +++ api/entity.go | 51 ++++++++ client/client.go | 106 ++++++++++++++++ go.mod | 7 ++ platform/eleme-union/api.go | 41 +++++++ platform/eleme-union/client.go | 54 +++++++++ platform/eleme-union/consts.go | 1 + platform/eleme-union/index.go | 1 + platform/eleme-union/types.go | 3 + platform/meituan-csr/api.go | 28 +++++ platform/meituan-csr/client.go | 1 + platform/meituan-csr/consts.go | 1 + platform/meituan-csr/types.go | 1 + platform/meituan-union/api.go | 107 ++++++++++++++++ platform/meituan-union/client.go | 35 ++++++ platform/meituan-union/consts.go | 9 ++ platform/meituan-union/index.go | 1 + platform/meituan-union/types.go | 87 +++++++++++++ util/http.go | 202 +++++++++++++++++++++++++++++++ util/json.go | 19 +++ util/md5.go | 13 ++ util/util.go | 17 +++ 23 files changed, 809 insertions(+) create mode 100644 .gitignore create mode 100644 api/api.go create mode 100644 api/entity.go create mode 100644 client/client.go create mode 100644 go.mod create mode 100644 platform/eleme-union/api.go create mode 100644 platform/eleme-union/client.go create mode 100644 platform/eleme-union/consts.go create mode 100644 platform/eleme-union/index.go create mode 100644 platform/eleme-union/types.go create mode 100644 platform/meituan-csr/api.go create mode 100644 platform/meituan-csr/client.go create mode 100644 platform/meituan-csr/consts.go create mode 100644 platform/meituan-csr/types.go create mode 100644 platform/meituan-union/api.go create mode 100644 platform/meituan-union/client.go create mode 100644 platform/meituan-union/consts.go create mode 100644 platform/meituan-union/index.go create mode 100644 platform/meituan-union/types.go create mode 100644 util/http.go create mode 100644 util/json.go create mode 100644 util/md5.go create mode 100644 util/util.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c08759 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +vendor +.vscode +go.sum diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..56f3100 --- /dev/null +++ b/api/api.go @@ -0,0 +1,20 @@ +package api + +type PromotionSdk interface { + Sign(data map[string]interface{}) string //签名 + GetLink(r *GetLinkRequest) (*GetLinkResponse, error) //推广取链 + GetOrder(r *GetOrderRequest) (*GetOrderResponse, error) //拉取订单信息 +} + +type PromotionConf struct { + AppKey string + AppSecret string + Ext1 string + Ext2 string + Ext3 string + Ext4 string +} + +func NewPromotionSdk(sdk PromotionSdk) PromotionSdk { + return sdk +} diff --git a/api/entity.go b/api/entity.go new file mode 100644 index 0000000..d7f1618 --- /dev/null +++ b/api/entity.go @@ -0,0 +1,51 @@ +package api + +type GetLinkRequest struct { + ActivityId string `json:"activity_id"` //活动ID + Position string `json:"position"` //推广位ID +} + +type GetLinkResponse struct { + H5 string `json:"h5"` + ShortLink string `json:"shortLink"` + DeepLink string `json:"deepLink"` + EvokeLink string `json:"evokeLink"` + WechatApplet string `json:"wechatApplet"` + WechatAppletQrcode string `json:"wechatAppletQrcode"` + AlipayApplet string `json:"alipayApplet"` + Tkl string `json:"tkl"` + Poster string `json:"poster"` +} + +type GetOrderRequest struct { + OrderNo string `json:"orderNo"` + ActId string `json:"actId"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + Page int64 `json:"page"` + PageSize int64 `json:"pageSize"` +} +type OrderItem struct { + OrderNo string `json:"orderNo"` //订单号 + ActId string `json:"actId"` //活动ID + ActName string `json:"actName"` //活动名称 + Sid string `json:"sid"` //Sid + OrderTitle string `json:"orderTitle"` //订标题 + Commission string `json:"commission"` //佣金 + CommissionRate string `json:"commissionRate"` //佣金比率 + OrderTime int64 `json:"orderTime"` //订单时间 + PayTime int64 `json:"payTime"` //支付时间 + ModifiedTime int64 `json:"modifiedTime"` //最后一次更新时间 + OrderPrice string `json:"orderPrice"` //订单金额 + PayPrice string `json:"payPrice"` //支付金额 + RefundPrice string `json:"refundPrice"` //退款金额 + RefundReason string `json:"refundReason"` //退款原因 + Status string `json:"status"` //状态 + Quantity int64 `json:"quantity"` //商品数量 + AppKey string `json:"appKey"` + Ext string `json:"ext"` //拓展参数 +} +type GetOrderResponse struct { + Total int64 `json:"total"` + List []*OrderItem `json:"list"` +} diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..60f4c14 --- /dev/null +++ b/client/client.go @@ -0,0 +1,106 @@ +package client + +import ( + "errors" + "fmt" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + + "chengdu-lenntc/third-platform-sdk/util" +) + +// HttpRequest http请求参数 +type HttpRequest struct { + Headers map[string]string // 请求header参数 + QueryArgs map[string]any // 请求query参数 + BodyArgs map[string]any // 请求body参数 +} + +// HttpResponse http响应结果 +type HttpResponse struct { + Result any // 响应的body数据结构, 必须为指针类型 + RespHeader *http.Header // 响应header +} + +// ThirdClient 第三方平台的client +type ThirdClient interface { + // HttpGet GET请求 + HttpGet(url string, req *HttpRequest, resp *HttpResponse) error + // HttpPost POST请求 + HttpPost(url string, req *HttpRequest, resp *HttpResponse) error + // HttpPut PUT请求 + HttpPut(url string, req *HttpRequest, resp *HttpResponse) error + // HttpDelete DELETE请求 + HttpDelete(url string, req *HttpRequest, resp *HttpResponse) error + // DoHttp 发起http请求 + DoHttp(method string, url string, req *HttpRequest, resp *HttpResponse) error +} + +type ThirdClientImpl struct { + log logx.Logger +} + +func NewThirdClient(log logx.Logger) *ThirdClientImpl { + return &ThirdClientImpl{ + log: log, + } +} + +func (c *ThirdClientImpl) HttpGet(url string, req *HttpRequest, resp *HttpResponse) error { + return c.DoHttp(http.MethodGet, url, req, resp) +} + +func (c *ThirdClientImpl) HttpPost(url string, req *HttpRequest, resp *HttpResponse) error { + return c.DoHttp(http.MethodPost, url, req, resp) +} + +func (c *ThirdClientImpl) HttpPut(url string, req *HttpRequest, resp *HttpResponse) error { + return c.DoHttp(http.MethodPut, url, req, resp) +} + +func (c *ThirdClientImpl) HttpDelete(url string, req *HttpRequest, resp *HttpResponse) error { + return c.DoHttp(http.MethodDelete, url, req, resp) +} + +func (c *ThirdClientImpl) DoHttp(method string, url string, req *HttpRequest, resp *HttpResponse) error { + // 发起请求 + reqConfig := &util.ReqConfig{ + Headers: req.Headers, + QueryArgs: req.QueryArgs, + BodyArgs: req.BodyArgs, + } + hc := util.NewHttpClient(c.log).NewRequest(method, url, reqConfig).Do() + if hc.Error != nil { + return hc.Error + } + var responseHeader *http.Header + if hc.Response != nil { + responseHeader = &hc.Response.Header + } + // 检查http响应错误 + if err := c.checkResponseError(hc.Response); err != nil { + c.log.WithFields([]logx.LogField{{Key: "method", Value: method}, {Key: "url", Value: url}, {Key: "reqConfig", Value: reqConfig}}...). + Errorf("[ThirdClientImpl][DoHttp] checkResponseError err:%s", err) + return err + } + // 获取响应结果 + if resp == nil { + return nil + } + result := resp.Result + if err := hc.Result(result).Error; err != nil { + return err + } + resp.Result = result + resp.RespHeader = responseHeader + return nil +} + +// 检查http响应错误 +func (c *ThirdClientImpl) checkResponseError(r *util.Response) error { + if r.StatusCode >= 200 && r.StatusCode < 300 { + return nil + } + return errors.New(fmt.Sprintf("http response error, status code: %d, status: %s", r.StatusCode, r.Status)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4cb70cc --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module chengdu-lenntc/third-platform-sdk + +go 1.19 + +require ( + github.com/zeromicro/go-zero v1.5.5 +) \ No newline at end of file diff --git a/platform/eleme-union/api.go b/platform/eleme-union/api.go new file mode 100644 index 0000000..6827121 --- /dev/null +++ b/platform/eleme-union/api.go @@ -0,0 +1,41 @@ +package eleme_union + +import ( + "github.com/zeromicro/go-zero/core/logx" +) + +// todo:: 调用第三方平台的api +// Api defines the interface of eleme_union api +type Api interface { + Sign(data map[string]interface{}) string + GetLink() error + GetOrder() error +} + +type ApiImpl struct { + log logx.Logger + client *Client +} + +func NewApiImpl(log logx.Logger, client *Client) Api { + return &ApiImpl{ + log: log, + client: client, + } +} + +// todo:: +func (a *ApiImpl) Sign(data map[string]interface{}) string { + + return "" +} + +// todo:: +func (a *ApiImpl) GetLink() error { + return nil +} + +// todo:: +func (a *ApiImpl) GetOrder() error { + return nil +} diff --git a/platform/eleme-union/client.go b/platform/eleme-union/client.go new file mode 100644 index 0000000..c1ff798 --- /dev/null +++ b/platform/eleme-union/client.go @@ -0,0 +1,54 @@ +package eleme_union + +import ( + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + + "chengdu-lenntc/third-platform-sdk/client" + "chengdu-lenntc/third-platform-sdk/util" +) + +// AuthConfig api鉴权参数 +type AuthConfig struct { +} + +// todo:: 连接第三方平台的client +type Client struct { + client.ThirdClient + log logx.Logger + authConfig AuthConfig +} + +func NewClient(log logx.Logger, conf AuthConfig) *Client { + // todo:: 请求第三方平台的配置参数 + return &Client{ + ThirdClient: client.NewThirdClient(log), + log: log, + authConfig: conf, + } +} + +// todo:: DoHttp 发起http请求 +func (c *Client) DoHttp(method string, url string, req *client.HttpRequest, resp *client.HttpResponse) error { + // todo:: api请求频率限制? + c.apiRateLimit() + // 发起请求 + err := c.ThirdClient.DoHttp(method, url, req, resp) + return err +} + +// todo:: 请求api的频率限制 +func (c *Client) apiRateLimit() { + +} + +// todo:: 检查api请求频率限制 +func (c *Client) checkRateLimit(header *http.Header) error { + return nil +} + +// todo:: 检查http响应错误 +func (c *Client) checkResponseError(r *util.Response) error { + return nil +} diff --git a/platform/eleme-union/consts.go b/platform/eleme-union/consts.go new file mode 100644 index 0000000..2963989 --- /dev/null +++ b/platform/eleme-union/consts.go @@ -0,0 +1 @@ +package eleme_union diff --git a/platform/eleme-union/index.go b/platform/eleme-union/index.go new file mode 100644 index 0000000..2963989 --- /dev/null +++ b/platform/eleme-union/index.go @@ -0,0 +1 @@ +package eleme_union diff --git a/platform/eleme-union/types.go b/platform/eleme-union/types.go new file mode 100644 index 0000000..9b83393 --- /dev/null +++ b/platform/eleme-union/types.go @@ -0,0 +1,3 @@ +package eleme_union + +// todo:: 定义第三方平台api接口的结构体 diff --git a/platform/meituan-csr/api.go b/platform/meituan-csr/api.go new file mode 100644 index 0000000..8b9ceb1 --- /dev/null +++ b/platform/meituan-csr/api.go @@ -0,0 +1,28 @@ +package meituan_csr + +import ( + "fmt" + "sort" + "strings" + + "github.com/zeromicro/go-zero/core/logx" + + "chengdu-lenntc/third-platform-sdk/client" + "chengdu-lenntc/third-platform-sdk/util" +) + +// Api 调用第三方平台的api +type MeituanCsrApi interface { +} + +type meituanApiImpl struct { + log logx.Logger + client *Client +} + +func newMeituanApiImpl(log logx.Logger, client *Client) MeituanCsrApi { + return &meituanApiImpl{ + log: log, + client: client, + } +} \ No newline at end of file diff --git a/platform/meituan-csr/client.go b/platform/meituan-csr/client.go new file mode 100644 index 0000000..566b7d1 --- /dev/null +++ b/platform/meituan-csr/client.go @@ -0,0 +1 @@ +package meituan_csr diff --git a/platform/meituan-csr/consts.go b/platform/meituan-csr/consts.go new file mode 100644 index 0000000..566b7d1 --- /dev/null +++ b/platform/meituan-csr/consts.go @@ -0,0 +1 @@ +package meituan_csr diff --git a/platform/meituan-csr/types.go b/platform/meituan-csr/types.go new file mode 100644 index 0000000..566b7d1 --- /dev/null +++ b/platform/meituan-csr/types.go @@ -0,0 +1 @@ +package meituan_csr diff --git a/platform/meituan-union/api.go b/platform/meituan-union/api.go new file mode 100644 index 0000000..b1f6e33 --- /dev/null +++ b/platform/meituan-union/api.go @@ -0,0 +1,107 @@ +package meituan_union + +import ( + "fmt" + "sort" + "strings" + + "github.com/zeromicro/go-zero/core/logx" + + "chengdu-lenntc/third-platform-sdk/client" + "chengdu-lenntc/third-platform-sdk/util" +) + +// Api 调用第三方平台的api +type MeituanUnionApi interface { + GetLink(params GenerateLinkRequest) (*GenerateLinkResponse, error) + MiniCode(params MiniCodeRequest) (*MimiCodeResponse, error) + GetOrderBySinge(params GetOrderBySingeRequest) (*GetOrderBySingeResponse, error) + GetOrderByBatch(params GetOrderByBatchRequest) (*GetOrderByBatchResponse, error) +} + +type meituanApiImpl struct { + log logx.Logger + client *Client +} + +func newMeituanApiImpl(log logx.Logger, client *Client) MeituanUnionApi { + return &meituanApiImpl{ + log: log, + client: client, + } +} + +func (a *meituanApiImpl) GetLink(params GenerateLinkRequest) (*GenerateLinkResponse, error) { + params.Sign = a.sign(util.StructToMap(params)) + queryArgs := util.StructToMap(params) + req := &client.HttpRequest{Headers: a.client.Headers, QueryArgs: queryArgs} + response := new(GenerateLinkResponse) + if err := a.client.HttpGet(GetLinkUrl, req, &client.HttpResponse{Result: response}); err != nil { + return nil, err + } + return response, nil +} + +func (a *meituanApiImpl) MiniCode(params MiniCodeRequest) (*MimiCodeResponse, error) { + params.Sign = a.sign(util.StructToMap(params)) + queryArgs := util.StructToMap(params) + req := &client.HttpRequest{Headers: a.client.Headers, QueryArgs: queryArgs} + response := new(MimiCodeResponse) + if err := a.client.HttpGet(GetMiniCode, req, &client.HttpResponse{Result: response}); err != nil { + return nil, err + } + return response, nil +} +func (a *meituanApiImpl) GetOrderBySinge(params GetOrderBySingeRequest) (*GetOrderBySingeResponse, error) { + params.Sign = a.sign(util.StructToMap(params)) + queryArgs := util.StructToMap(params) + req := &client.HttpRequest{Headers: a.client.Headers, QueryArgs: queryArgs} + response := new(GetOrderBySingeResponse) + if err := a.client.HttpGet(GetOrderSinge, req, &client.HttpResponse{Result: response}); err != nil { + return nil, err + } + return response, nil +} +func (a *meituanApiImpl) GetOrderByBatch(params GetOrderByBatchRequest) (*GetOrderByBatchResponse, error) { + params.Sign = a.sign(util.StructToMap(params)) + queryArgs := util.StructToMap(params) + req := &client.HttpRequest{Headers: a.client.Headers, QueryArgs: queryArgs} + response := new(GetOrderByBatchResponse) + if err := a.client.HttpGet(GetOrderBatch, req, &client.HttpResponse{Result: response}); err != nil { + return nil, err + } + return response, nil +} + +func (a *meituanApiImpl) sign(params map[string]interface{}) string { + kvPairs := a.getSignStr(params) + sort.Strings(kvPairs) + paramStr := strings.Join(kvPairs, "") + return util.Md5String(a.client.authConfig.SignKey + paramStr + a.client.authConfig.SignKey) +} + +func (a *meituanApiImpl) notifySign(params map[string]interface{}) string { + kvPairs := a.getSignStr(params) + sort.Strings(kvPairs) + paramStr := strings.Join(kvPairs, "") + return util.Md5String(a.client.authConfig.NotifyKey + paramStr + a.client.authConfig.NotifyKey) +} + +func (a *meituanApiImpl) getSignStr(dataMap map[string]any) []string { + var kvPairs []string + for k, v := range dataMap { + key := a.lowerFirstLetter(k) + if key == "sign" || key == "Sign" { + continue + } + kvPairs = append(kvPairs, fmt.Sprintf("%s%v", key, v)) + } + return kvPairs +} + +func (a *meituanApiImpl) lowerFirstLetter(s string) string { + if len(s) == 0 { + return s + } + return strings.ToLower(string(s[0])) + s[1:] +} diff --git a/platform/meituan-union/client.go b/platform/meituan-union/client.go new file mode 100644 index 0000000..2982e5c --- /dev/null +++ b/platform/meituan-union/client.go @@ -0,0 +1,35 @@ +package meituan_union + +import ( + "github.com/zeromicro/go-zero/core/logx" + + "chengdu-lenntc/third-platform-sdk/client" +) + +// AuthConfig api鉴权参数 +type AuthConfig struct { + SignKey string //签名秘钥 + NotifyKey string //回调秘钥 +} + +// 连接第三方平台的client +type Client struct { + client.ThirdClient + log logx.Logger + authConfig AuthConfig + Headers map[string]string +} + +func newClient(log logx.Logger, conf AuthConfig) *Client { + // todo:: 请求第三方平台的配置参数 + return &Client{ + ThirdClient: client.NewThirdClient(log), + log: log, + authConfig: conf, + } +} + +func NewApiClient(log logx.Logger, conf AuthConfig) MeituanUnionApi { + clt := newClient(log, conf) + return newMeituanApiImpl(log, clt) +} diff --git a/platform/meituan-union/consts.go b/platform/meituan-union/consts.go new file mode 100644 index 0000000..1779c5b --- /dev/null +++ b/platform/meituan-union/consts.go @@ -0,0 +1,9 @@ +package meituan_union + +const ( + Domain = "https://openapi.meituan.com" + GetLinkUrl = Domain + "/api/generateLink" + GetMiniCode = Domain + "/api/miniCode" + GetOrderSinge = Domain + "/api/order" + GetOrderBatch = Domain + "/api/orderList" +) diff --git a/platform/meituan-union/index.go b/platform/meituan-union/index.go new file mode 100644 index 0000000..59e27ff --- /dev/null +++ b/platform/meituan-union/index.go @@ -0,0 +1 @@ +package meituan_union diff --git a/platform/meituan-union/types.go b/platform/meituan-union/types.go new file mode 100644 index 0000000..d594ddf --- /dev/null +++ b/platform/meituan-union/types.go @@ -0,0 +1,87 @@ +package meituan_union + +type GenerateLinkRequest struct { + ActId int64 `form:"actId"` + Appkey string `form:"appkey"` + LinkType int64 `form:"linkType"` + ShortLink int64 `form:"shortLink"` + Sid string `form:"sid"` + Sign string `form:"sign"` +} + +type MiniCodeRequest struct { + ActId int64 `form:"actId"` + Appkey string `form:"appkey"` + Sid string `form:"sid"` + Sign string `form:"sign"` + LinkType int64 `form:"linkType"` +} + +type MimiCodeResponse struct { +} + +type GenerateLinkResponse struct { + Status int64 `json:"status"` + Des string `json:"des"` + Data string `json:"data"` +} +type GetOrderBySingeRequest struct { + Appkey string `form:"appkey"` + ActId int64 `form:"actId"` + Full int64 `form:"full"` + Sign string `form:"sign"` + OrderId string `form:"orderId"` +} +type GetOrderBySingeResponse struct { + Status int64 `json:"status"` + Des string `json:"des"` + Data OrderSinge `json:"data"` +} +type OrderSinge struct { + ActId int64 `json:"actId"` + Quantity int64 `json:"quantity"` + OrderId string `json:"orderId"` + PayTime string `json:"paytime"` + ModTime string `json:"modTime"` + PayPrice string `json:"payprice"` + Profit string `json:"profit"` + CpaProfit string `json:"cpaProfit"` + Sid string `json:"sid"` + AppKey string `json:"appkey"` + SmsTitle string `json:"smstitle"` + Status int64 `json:"status"` + RiskOrder int64 `json:"riskOrder"` + RefundProfit string `json:"refundProfit"` + CpaRefundProfit string `json:"cpaRefundProfit"` +} + +type GetOrderByBatchRequest struct { + Appkey string `form:"appkey"` + Ts string `form:"ts"` + ActId string `form:"actId"` + StartTime string `form:"startTime"` + EndTime string `form:"endTime"` + Sign string `form:"sign"` + Page string `form:"page"` + Limit string `form:"limit"` +} +type GetOrderByBatchResponse struct { + Total int64 `json:"total"` + DataList []OrderBatch `json:"dataList"` +} +type OrderBatch struct { + ActId int64 `json:"actId"` + OrderId string `json:"orderId"` + PayTime string `json:"paytime"` + ModTime string `json:"modTime"` + PayPrice string `json:"payprice"` + Profit string `json:"profit"` + CpaProfit string `json:"cpaProfit"` + Sid string `json:"sid"` + AppKey string `json:"appkey"` + SmsTitle string `json:"smstitle"` + Status int64 `json:"status"` + RiskOrder int64 `json:"riskOrder"` + RefundProfit string `json:"refundProfit"` + CpaRefundProfit string `json:"cpaRefundProfit"` +} diff --git a/util/http.go b/util/http.go new file mode 100644 index 0000000..4d47df0 --- /dev/null +++ b/util/http.go @@ -0,0 +1,202 @@ +package util + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + netUrl "net/url" + "strings" + "time" + + "github.com/zeromicro/go-zero/core/logx" +) + +const ( + TimeOut = time.Duration(30) * time.Second // http请求超时时间, 默认30s +) + +type HttpClient struct { + log logx.Logger + ReqConfig *ReqConfig // 请求参数 + TimeOut time.Duration // 超时时间 + Request *http.Request // 请求request + Response *Response // 响应结果 + Error error +} + +// ReqConfig 请求参数 +type ReqConfig struct { + Headers map[string]string + QueryArgs map[string]any + BodyArgs map[string]any +} + +type Response struct { + Status string // e.g. "200 OK" + StatusCode int // e.g. 200 + Header http.Header + Body []byte // body实体,即io.ReadAll(resp.body)后的结果 +} + +func NewHttpClient(log logx.Logger) *HttpClient { + return &HttpClient{ + log: log, + } +} + +// Get GET请求 +// @param url 请求的http地址 +// @param reqConfig 中header、query、body等参数 +func (h *HttpClient) Get(url string, reqConfig *ReqConfig) *HttpClient { + return h.NewRequest(http.MethodGet, url, reqConfig) +} + +// Post POST请求 +func (h *HttpClient) Post(url string, reqConfig *ReqConfig) *HttpClient { + return h.NewRequest(http.MethodPost, url, reqConfig) +} + +// Put PUT请求 +func (h *HttpClient) Put(url string, reqConfig *ReqConfig) *HttpClient { + return h.NewRequest(http.MethodPut, url, reqConfig) +} + +// Delete DELETE请求 +func (h *HttpClient) Delete(url string, reqConfig *ReqConfig) *HttpClient { + return h.NewRequest(http.MethodDelete, url, reqConfig) +} + +// NewRequest 组装请求参数 +func (h *HttpClient) NewRequest(method, url string, reqConfig *ReqConfig) (hc *HttpClient) { + if h.Error != nil { + return h + } + h.ReqConfig = reqConfig + hc = h + // 请求 + req, err := http.NewRequest(method, url, nil) + if err != nil { + h.log.WithFields([]logx.LogField{{Key: "method", Value: method}, {Key: "url", Value: url}, {Key: "reqConfig", Value: reqConfig}}...). + Errorf("[HttpClient][NewRequest] http new request error", err) + hc.Error = err + return + } + hc.Request = req + // 请求参数 + err = hc.requestConfig() + if err != nil { + hc.Error = err + return + } + return +} + +// Do http请求及响应处理 +func (h *HttpClient) Do() (hc *HttpClient) { + if h.Error != nil { + return h + } + hc = h + // http请求 + timeOut := hc.TimeOut + if hc.TimeOut == 0 { + timeOut = TimeOut + } + client := &http.Client{} + client.Timeout = timeOut + // 开始请求 + resp, err := client.Do(hc.Request) + if err != nil { + h.log.WithFields(logx.LogField{Key: "req", Value: hc.Request}). + Errorf("[HttpClient][Do] http client do error", err) + hc.Error = err + return + } + defer resp.Body.Close() + + content, err := io.ReadAll(resp.Body) + if err != nil { + h.log.WithFields(logx.LogField{Key: "req", Value: hc.Request}). + Errorf("[HttpClient][Do] http response ReadAll error", err) + hc.Error = err + return + } + hc.Response = &Response{ + Status: resp.Status, + StatusCode: resp.StatusCode, + Header: resp.Header, + Body: content, + } + + return +} + +// Result 获取http的响应结果 +// @param result 为结构体(指针类型),解析http请求返回数据json.Unmarshal后的结构 +func (h *HttpClient) Result(result any) (hc *HttpClient) { + if h.Error != nil { + return h + } + hc = h + if result == nil { + return + } + if hc.Response == nil || len(hc.Response.Body) == 0 { + return + } + + err := json.Unmarshal(hc.Response.Body, &result) + if err != nil { + h.log.WithFields(logx.LogField{Key: "HttpClient", Value: hc}, logx.LogField{Key: "result", Value: result}). + Errorf("[HttpClient][Result] http response body json unmarshal error", err) + hc.Error = err + return + } + return +} + +// 请求参数 +func (h *HttpClient) requestConfig() error { + if h.Request == nil { + return errors.New("http request is nil") + } + if h.ReqConfig == nil { + return nil + } + // header参数 + if h.ReqConfig.Headers != nil { + for key, value := range h.ReqConfig.Headers { + h.Request.Header.Set(key, value) + } + } + + // 请求query参数 + queryParams := h.Request.URL.Query() + if h.ReqConfig.QueryArgs != nil { + for key, value := range h.ReqConfig.QueryArgs { + queryParams.Add(key, fmt.Sprint(value)) + } + } + h.Request.URL.RawQuery = queryParams.Encode() + // 请求body参数 + if h.ReqConfig.BodyArgs != nil { + switch h.Request.Header.Get("Content-Type") { + case "application/x-www-form-urlencoded": + form := make(netUrl.Values) + for k, v := range h.ReqConfig.BodyArgs { + form.Add(k, fmt.Sprint(v)) + } + h.Request.Body = io.NopCloser(strings.NewReader(form.Encode())) + case "application/json": + data, err := json.Marshal(h.ReqConfig.BodyArgs) + if err != nil { + h.Error = err + return err + } + h.Request.Body = io.NopCloser(strings.NewReader(string(data))) + } + } + return nil +} diff --git a/util/json.go b/util/json.go new file mode 100644 index 0000000..f07e661 --- /dev/null +++ b/util/json.go @@ -0,0 +1,19 @@ +package util + +//// StructToMap 将struct转为map +//func StructToMap(info any) (map[string]any, error) { +// result := make(map[string]any) +// data, err := json.Marshal(info) +// if err != nil { +// return nil, err +// } +// +// d := json.NewDecoder(bytes.NewReader(data)) +// d.UseNumber() +// err = d.Decode(&result) +// //err = json.Unmarshal(data, &result) +// if err != nil { +// return nil, err +// } +// return result, nil +//} diff --git a/util/md5.go b/util/md5.go new file mode 100644 index 0000000..eee9b09 --- /dev/null +++ b/util/md5.go @@ -0,0 +1,13 @@ +package util + +import ( + "crypto/md5" + "encoding/hex" +) + +// Md5String 字符串计算md5 +func Md5String(v string) string { + h := md5.New() + h.Write([]byte(v)) + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..5ab0699 --- /dev/null +++ b/util/util.go @@ -0,0 +1,17 @@ +package util + +import "reflect" + +func StructToMap(obj interface{}) map[string]any { + objValue := reflect.ValueOf(obj) + objType := objValue.Type() + + data := make(map[string]any) + for i := 0; i < objValue.NumField(); i++ { + field := objValue.Field(i) + key := objType.Field(i).Name + value := field.Interface() + data[key] = value + } + return data +}