diff --git a/go.mod b/go.mod
index 5a861eb..21c2864 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,10 @@ require (
github.com/stretchr/testify v1.8.4
github.com/zeromicro/go-zero v1.5.5
github.com/zywaited/xcopy v1.1.0
+ github.com/BurntSushi/toml v1.0.0 // indirect
+ github.com/google/uuid v1.3.0
+ go.uber.org/zap v1.21.0
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
require (
diff --git a/index.go b/index.go
index 184b6f0..fd03b39 100644
--- a/index.go
+++ b/index.go
@@ -3,6 +3,7 @@ package third_platform_sdk
import (
"github.com/zeromicro/go-zero/core/logx"
+ didiunion "gitee.com/chengdu-lenntc/third-platform-sdk/platform/didi-union"
elemeunion "gitee.com/chengdu-lenntc/third-platform-sdk/platform/eleme-union"
meituancsr "gitee.com/chengdu-lenntc/third-platform-sdk/platform/meituan-csr"
meituanunion "gitee.com/chengdu-lenntc/third-platform-sdk/platform/meituan-union"
@@ -22,3 +23,8 @@ func NewMeituanCsrApi(log logx.Logger, conf meituancsr.AuthConfig) meituancsr.Me
func NewMeituanUnionApi(log logx.Logger, conf meituanunion.AuthConfig) meituanunion.MeituanUnionApi {
return meituanunion.NewApiClient(log, conf)
}
+
+// NewDidiUnionApi 滴滴联盟
+func NewDidiUnionApi(log logx.Logger, conf didiunion.AuthConfig) didiunion.DidiUnionApi {
+ return didiunion.NewApiClient(log, conf)
+}
diff --git a/platform/didi-union/api.go b/platform/didi-union/api.go
new file mode 100644
index 0000000..fc6ee53
--- /dev/null
+++ b/platform/didi-union/api.go
@@ -0,0 +1,88 @@
+package didi_union
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/zeromicro/go-zero/core/logx"
+
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/model"
+ sdkutil "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/util"
+)
+
+// DidiUnionApi 调用第三方平台的api
+// Api defines the interface of eleme_union api
+type DidiUnionApi interface {
+ // Sign 签名
+ Sign(data map[string]interface{}) string
+ // GenerateH5Link 生成h5推广链接
+ GenerateH5Link(ctx context.Context, req GenerateLinkRequest, opt model.Option) (*model.LinkResponse, error)
+ // GenerateMiniLink 生成小程序页面推广路径
+ GenerateMiniLink(ctx context.Context, req GenerateLinkRequest, opt model.Option) (*model.LinkResponse, error)
+ // GenerateH5Code 生成h5二维码,需先取链得到dsi
+ GenerateH5Code(ctx context.Context, req GenerateCodeRequest, opt model.Option) (*model.QrcodeResponse, error)
+ // GenerateMiniCode 生成小程序太阳码,需先取链得到dsi
+ GenerateMiniCode(ctx context.Context, req GenerateCodeRequest, opt model.Option) (*model.QrcodeResponse, error)
+ // GeneratePoster 生成推广海报,需先取链得到dsi
+ GeneratePoster(ctx context.Context, req GeneratePosterRequest, opt model.Option) (*model.PosterResponse, error)
+ // QueryOrderList 查询订单列表
+ QueryOrderList(ctx context.Context, req QueryOrderListRequest, opt model.Option) (*model.OrderResponse, error)
+ // SelfQueryOrder 订单归因问题自查询
+ SelfQueryOrder(ctx context.Context, req SelfQueryOrderRequest, opt model.Option) (*model.SelfQueryResponse, error)
+}
+
+type didiUnionApiImpl struct {
+ log logx.Logger
+ client *Client
+}
+
+func newDidiUnionApiImpl(log logx.Logger, client *Client) DidiUnionApi {
+ return &didiUnionApiImpl{
+ log: log,
+ client: client,
+ }
+}
+
+// Sign 签名
+func (d *didiUnionApiImpl) Sign(params map[string]any) string {
+ params[sdkutil.AppKey] = d.client.authConfig.AppKey
+ params[sdkutil.Timestamp] = fmt.Sprintf("%d", time.Now().Unix())
+ sign := sdkutil.GetSign(params, d.client.authConfig.AppSecret)
+ return sign
+}
+
+// GenerateH5Link 生成h5推广链接
+func (d *didiUnionApiImpl) GenerateH5Link(ctx context.Context, req GenerateLinkRequest, opt model.Option) (*model.LinkResponse, error) {
+ return d.client.clt.GenerateH5Link(ctx, req.ActivityID, req.PromotionID, req.SourceID, opt)
+}
+
+// GenerateMiniLink 生成小程序页面推广路径
+func (d *didiUnionApiImpl) GenerateMiniLink(ctx context.Context, req GenerateLinkRequest, opt model.Option) (*model.LinkResponse, error) {
+ return d.client.clt.GenerateMiniLink(ctx, req.ActivityID, req.PromotionID, req.SourceID, opt)
+}
+
+// GenerateH5Code 生成h5二维码,需先取链得到dsi
+func (d *didiUnionApiImpl) GenerateH5Code(ctx context.Context, req GenerateCodeRequest, opt model.Option) (*model.QrcodeResponse, error) {
+ return d.client.clt.GenerateH5Code(ctx, req.Dsi, req.SourceID, opt)
+}
+
+// GenerateMiniCode 生成小程序太阳码,需先取链得到dsi
+func (d *didiUnionApiImpl) GenerateMiniCode(ctx context.Context, req GenerateCodeRequest, opt model.Option) (*model.QrcodeResponse, error) {
+ return d.client.clt.GenerateMiniCode(ctx, req.Dsi, req.SourceID, opt)
+}
+
+// GeneratePoster 生成推广海报,需先取链得到dsi
+func (d *didiUnionApiImpl) GeneratePoster(ctx context.Context, req GeneratePosterRequest, opt model.Option) (*model.PosterResponse, error) {
+ return d.client.clt.GeneratePoster(ctx, req.Dsi, req.SourceID, opt)
+}
+
+// QueryOrderList 查询订单列表
+func (d *didiUnionApiImpl) QueryOrderList(ctx context.Context, req QueryOrderListRequest, opt model.Option) (*model.OrderResponse, error) {
+ return d.client.clt.QueryOrderList(ctx, req.StartTime, req.EndTime, string(req.Typ), req.Page, req.Size, opt)
+}
+
+// SelfQueryOrder 订单归因问题自查询
+func (d *didiUnionApiImpl) SelfQueryOrder(ctx context.Context, req SelfQueryOrderRequest, opt model.Option) (*model.SelfQueryResponse, error) {
+ return d.client.clt.SelfQueryOrder(ctx, req.OrderID, opt)
+}
diff --git a/platform/didi-union/api_test.go b/platform/didi-union/api_test.go
new file mode 100644
index 0000000..b8efa82
--- /dev/null
+++ b/platform/didi-union/api_test.go
@@ -0,0 +1,37 @@
+package didi_union
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+// api-单元测试
+type apiClientSuite struct {
+ suite.Suite
+ api DidiUnionApi
+}
+
+func TestApiClient(t *testing.T) {
+ suite.Run(t, new(apiClientSuite))
+}
+
+func (a *apiClientSuite) SetupSuite() {
+ log := logx.WithContext(context.Background())
+ apiClient := NewApiClient(log, AuthConfig{
+ AppKey: "2M0QUa0o6ER8nuX1",
+ AppSecret: "obvJ5mmV45ZWA3YpO95njR1xH62JT50h",
+ })
+ a.api = apiClient
+}
+
+func (a *apiClientSuite) Test_Sign() {
+ data := map[string]interface{}{
+ "method": "test",
+ }
+
+ sign := a.api.Sign(data)
+ a.T().Logf("=====[TestSign] sign: %s", sign)
+}
diff --git a/platform/didi-union/client.go b/platform/didi-union/client.go
new file mode 100644
index 0000000..06e5fd0
--- /dev/null
+++ b/platform/didi-union/client.go
@@ -0,0 +1,34 @@
+package didi_union
+
+import (
+ "github.com/zeromicro/go-zero/core/logx"
+
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/client"
+)
+
+// AuthConfig api鉴权参数
+type AuthConfig struct {
+ AppKey string // 应用key
+ AppSecret string // 应用秘钥
+}
+
+// 连接第三方平台的client
+type Client struct {
+ clt client.UnionClient
+ log logx.Logger
+ authConfig AuthConfig
+}
+
+func NewApiClient(log logx.Logger, conf AuthConfig) DidiUnionApi {
+ clt := newClient(log, conf)
+ return newDidiUnionApiImpl(log, clt)
+}
+
+func newClient(log logx.Logger, conf AuthConfig) *Client {
+ uc := client.NewUnionClient(conf.AppKey, conf.AppSecret)
+ return &Client{
+ clt: uc,
+ log: log,
+ authConfig: conf,
+ }
+}
diff --git a/platform/didi-union/consts.go b/platform/didi-union/consts.go
new file mode 100644
index 0000000..ba00bf1
--- /dev/null
+++ b/platform/didi-union/consts.go
@@ -0,0 +1,14 @@
+package didi_union
+
+// OrderType 订单类型
+type OrderType string
+
+// 订单查询type枚举值
+const (
+ OrderTypeAll OrderType = "" //全部
+ OrderTypeEnergy OrderType = "energy" //滴滴加油
+ OrderTypeCar OrderType = "online_car" //网约车
+ OrderTypeFreight OrderType = "freight" //货运
+ OrderTypeHxz OrderType = "king_flower" //花小猪
+ OrderTypeDaijia OrderType = "daijia" //代驾
+)
diff --git a/platform/didi-union/types.go b/platform/didi-union/types.go
new file mode 100644
index 0000000..b9d231d
--- /dev/null
+++ b/platform/didi-union/types.go
@@ -0,0 +1,40 @@
+package didi_union
+
+import "time"
+
+// GenerateLinkRequest 生成推广链接的请求
+type GenerateLinkRequest struct {
+ ActivityID int64 // 活动id
+ PromotionID int64 // 推广位id
+ SourceID string // 来源id (用于标明订单来源)
+}
+
+// 生成推广链接的响应
+type GenerateLinkResponse struct {
+}
+
+// GenerateCodeRequest 生成二维码的请求
+type GenerateCodeRequest struct {
+ Dsi string // 活动ID+推广位ID决定一个DSI (需先取链得到dsi)
+ SourceID string // 来源id (用于标明订单来源)
+}
+
+// GeneratePosterRequest 生成海报的请求
+type GeneratePosterRequest struct {
+ Dsi string // 活动ID+推广位ID决定一个DSI (需先取链得到dsi)
+ SourceID string // 来源id (用于标明订单来源)
+}
+
+// QueryOrderListRequest 订单列表请求
+type QueryOrderListRequest struct {
+ StartTime time.Time // 开始时间
+ EndTime time.Time // 结束时间
+ Typ OrderType // 订单类型,枚举值见 OrderType
+ Page int // 页码 (最大值为100)
+ Size int // 每页数量(最大值为100)
+}
+
+// SelfQueryOrderRequest 自查询订单请求
+type SelfQueryOrderRequest struct {
+ OrderID string // 订单ID
+}
diff --git a/sdk/dunion-go-sdk/README.md b/sdk/dunion-go-sdk/README.md
new file mode 100644
index 0000000..571101b
--- /dev/null
+++ b/sdk/dunion-go-sdk/README.md
@@ -0,0 +1,47 @@
+# 滴滴联盟 openAPI go-sdk
+
+引入mod
+```
+go get gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk@master
+```
+使用方法
+```
+c := client.NewUnionClient("appkey", "accesskey")
+//日志可选,将在指定目录生成日志
+util.InitLogger("./log/union.log")
+
+//或者使用日志注入的方式,需实现两个接口函数:
+//Infof(template string, args ...interface{})
+//Errorf(template string, args ...interface{})
+//然后调用
+//util.SetLogger(yourLogger)
+
+//设置全局超时时间
+//util.SetTimeoutDuration(2*time.Second)
+
+//或者设置单个接口的超时时间
+//link, err := c.GenerateH5Link(context.Background(), 6133, 6834408369283047676, "d", model.Option{Timeout: 2*time.Second})
+
+link, err := c.GenerateH5Link(context.Background(), 6133, 6834408369283047676, "d")
+if err != nil {
+ fmt.Println(err)
+ return
+}
+```
+
+函数一览
+
+| 函数原型 | 用途 |
+| ---- | ---- |
+| GenerateH5Link(activityID, promotionID int64, sourceID string) (*model.LinkResponse, error) | 生成h5推广链接 |
+| GenerateMiniLink(activityID, promotionID int64, sourceID string) (*model.LinkResponse, error) | 生成小程序页面推广路径|
+| GenerateH5Code(dsi, sourceID string) (*model.QrcodeResponse, error)|生成h5二维码,需先取链得到dsi|
+| GenerateMiniCode(dsi, sourceID string) (*model.QrcodeResponse, error)|生成小程序太阳码,需先取链得到dsi|
+| GeneratePoster(dsi, sourceID string) (*model.PosterResponse, error)|生成推广海报,需先取链得到dsi|
+| QueryOrderList(startTime, endTime time.Time, type_ string, page, size int) (*model.OrderResponse, error)|查询订单列表,type_可用枚举见 const.OrderTypeEnergy等|
+| MockOrderCallback(dsi string, sourceID string, type_ int) (*model.OrderCallbackResponse, error)|模拟订单回调,需先取链得到 dsi,type_ 可取 consts.MockPay 或 consts.MockRefund; 需在后台配置回调地址|
+| GenerateH5CodeDirectly(activityID, promotionID int64, sourceID string) (*model.QrcodeResponse, error)|直接生成h5推广二维码,会内置请求一次取链接口|
+| GenerateMiniCodeDirectly(activityID, promotionID int64, sourceID string) (*model.QrcodeResponse, error)|直接生成小程序推广太阳码,会内置请求一次取链接口|
+| GeneratePosterDirectly(activityID, promotionID int64, sourceID string) (*model.PosterResponse, error)|直接生成推广海报,会内置请求一次取链接口|
+| SelfQueryOrder(orderID string)(*model.SelfQueryResponse, error)| 订单归因问题自查询|
+
diff --git a/sdk/dunion-go-sdk/client/client.go b/sdk/dunion-go-sdk/client/client.go
new file mode 100644
index 0000000..c897316
--- /dev/null
+++ b/sdk/dunion-go-sdk/client/client.go
@@ -0,0 +1,202 @@
+package client
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "time"
+
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/const"
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/model"
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/util"
+)
+
+type client struct {
+ AppKey string
+ AccessKey string
+}
+
+type UnionClient interface {
+ GenerateH5Link(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.LinkResponse, error)
+ GenerateMiniLink(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.LinkResponse, error)
+ GenerateH5Code(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error)
+ GenerateMiniCode(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error)
+ GeneratePoster(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.PosterResponse, error)
+ QueryOrderList(ctx context.Context, startTime, endTime time.Time, type_ string, page, size int, opt ...model.Option) (*model.OrderResponse, error)
+ GenerateH5CodeDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error)
+ MockOrderCallback(ctx context.Context, dsi string, sourceID string, type_ int, opt ...model.Option) (*model.OrderCallbackResponse, error)
+ GenerateMiniCodeDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error)
+ GeneratePosterDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.PosterResponse, error)
+ SelfQueryOrder(ctx context.Context, orderID string, opt ...model.Option) (*model.SelfQueryResponse, error)
+}
+
+func NewUnionClient(appKey string, accessKey string) UnionClient {
+ return &client{
+ AppKey: appKey,
+ AccessKey: accessKey,
+ }
+}
+
+// GenerateH5Link 生成h5推广链接
+func (s client) GenerateH5Link(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.LinkResponse, error) {
+ body := map[string]interface{}{
+ "activity_id": activityID,
+ "link_type": "h5",
+ "promotion_id": promotionID,
+ "source_id": sourceID,
+ }
+ response, err := util.Post(ctx, s.AppKey, s.AccessKey, consts.GenerateLinkUrl, body, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.LinkResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// GenerateMiniLink 生成小程序页面推广路径
+func (s client) GenerateMiniLink(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.LinkResponse, error) {
+ body := map[string]interface{}{
+ "activity_id": activityID,
+ "link_type": "mini",
+ "promotion_id": promotionID,
+ "source_id": sourceID,
+ }
+ response, err := util.Post(ctx, s.AppKey, s.AccessKey, consts.GenerateLinkUrl, body, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.LinkResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// GenerateH5Code 生成h5二维码,需先取链得到dsi
+func (s client) GenerateH5Code(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error) {
+ param := map[string]interface{}{
+ "dsi": dsi,
+ "source_id": sourceID,
+ "type": "h5",
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.GenerateQrCodeUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.QrcodeResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// GenerateMiniCode 生成小程序太阳码,需先取链得到dsi
+func (s client) GenerateMiniCode(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error) {
+ param := map[string]interface{}{
+ "dsi": dsi,
+ "source_id": sourceID,
+ "type": "mini",
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.GenerateQrCodeUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.QrcodeResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// GeneratePoster 生成推广海报,需先取链得到dsi
+func (s client) GeneratePoster(ctx context.Context, dsi, sourceID string, opt ...model.Option) (*model.PosterResponse, error) {
+ param := map[string]interface{}{
+ "dsi": dsi,
+ "source_id": sourceID,
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.GeneratePosterUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.PosterResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// QueryOrderList 查询订单列表,type_可用枚举见 const.OrderTypeEnergy等
+func (s client) QueryOrderList(ctx context.Context, startTime, endTime time.Time, type_ string, page, size int, opt ...model.Option) (*model.OrderResponse, error) {
+ if page < 0 || page > 100 || size < 0 || size > 100 {
+ return nil, errors.New("分页参数不合法")
+ }
+ param := map[string]interface{}{
+ "pay_start_time": startTime.Unix(),
+ "pay_end_time": endTime.Unix(),
+ "page": page,
+ "size": size,
+ }
+ if len(type_) > 0 {
+ param["type"] = type_
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.QueryOrderUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.OrderResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// MockOrderCallback 模拟订单回调,需先取链得到 dsi,type_ 可取 consts.MockPay 或 consts.MockRefund; 需在后台配置回调地址
+func (s client) MockOrderCallback(ctx context.Context, dsi string, sourceID string, type_ int, opt ...model.Option) (*model.OrderCallbackResponse, error) {
+ param := map[string]interface{}{
+ "dsi": dsi,
+ "source_id": sourceID,
+ "type": type_,
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.MockOrderUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.OrderCallbackResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
+
+// GenerateH5CodeDirectly 直接生成h5推广二维码,会内置请求一次取链接口
+func (s client) GenerateH5CodeDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error) {
+ link, err := s.GenerateMiniLink(ctx, activityID, promotionID, sourceID, opt...)
+ if err != nil {
+ return nil, err
+ }
+ dsi := link.Data.DSI
+ return s.GenerateH5Code(ctx, dsi, sourceID, opt...)
+}
+
+// GenerateMiniCodeDirectly 直接生成小程序推广太阳码,会内置请求一次取链接口
+func (s client) GenerateMiniCodeDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.QrcodeResponse, error) {
+ link, err := s.GenerateMiniLink(ctx, activityID, promotionID, sourceID, opt...)
+ if err != nil {
+ return nil, err
+ }
+ dsi := link.Data.DSI
+ return s.GenerateMiniCode(ctx, dsi, sourceID, opt...)
+}
+
+// GeneratePosterDirectly 直接生成推广海报,会内置请求一次取链接口
+func (s client) GeneratePosterDirectly(ctx context.Context, activityID, promotionID int64, sourceID string, opt ...model.Option) (*model.PosterResponse, error) {
+ link, err := s.GenerateMiniLink(ctx, activityID, promotionID, sourceID, opt...)
+ if err != nil {
+ return nil, err
+ }
+ dsi := link.Data.DSI
+ return s.GeneratePoster(ctx, dsi, sourceID, opt...)
+}
+
+// SelfQueryOrder 订单归因问题自查询
+func (s client) SelfQueryOrder(ctx context.Context, orderID string, opt ...model.Option) (*model.SelfQueryResponse, error) {
+ param := map[string]interface{}{
+ "order_id": orderID,
+ }
+ response, err := util.Get(ctx, s.AppKey, s.AccessKey, consts.SelfQueryUrl, param, opt...)
+ if err != nil {
+ return nil, err
+ }
+ result := &model.SelfQueryResponse{}
+ err = json.Unmarshal(response, result)
+ return result, err
+}
diff --git a/sdk/dunion-go-sdk/const/const.go b/sdk/dunion-go-sdk/const/const.go
new file mode 100644
index 0000000..8497c88
--- /dev/null
+++ b/sdk/dunion-go-sdk/const/const.go
@@ -0,0 +1,32 @@
+package consts
+
+const (
+ GenerateLinkUrl = "https://union.didi.cn/openapi/v1.0/link/generate"
+ GenerateQrCodeUrl = "https://union.didi.cn/openapi/v1.0/code/generate"
+ GeneratePosterUrl = "https://union.didi.cn/openapi/v1.0/poster/generate"
+ QueryOrderUrl = "https://union.didi.cn/openapi/v1.0/order/list"
+ MockOrderUrl = "https://union.didi.cn/openapi/v1.0/orderMock/callback"
+ SelfQueryUrl = "https://union.didi.cn/openapi/v1.0/order/selfQuery"
+)
+
+// 订单查询type枚举值
+const (
+ OrderTypeAll = "" //全部
+ OrderTypeEnergy = "energy" //滴滴加油
+ OrderTypeCar = "online_car" //网约车
+ OrderTypeFreight = "freight" //货运
+ OrderTypeHxz = "king_flower" //花小猪
+ OrderTypeDaijia = "daijia" //代驾
+)
+
+// mock订单回调类型枚举值
+const (
+ MockPay = 0 //支付
+ MockRefund = 1 //退款
+)
+
+const (
+ UserAgent = "User-Agent"
+ SDKVersion = "dunion-go-openapi-sdk-1.0"
+ TraceID = "Didi-Header-Rid"
+)
diff --git a/sdk/dunion-go-sdk/model/link.go b/sdk/dunion-go-sdk/model/link.go
new file mode 100644
index 0000000..665f713
--- /dev/null
+++ b/sdk/dunion-go-sdk/model/link.go
@@ -0,0 +1,17 @@
+package model
+
+//swagger:model
+type LinkResponse struct {
+ Response
+ Data struct {
+ //生成的链接
+ //example: https://v.didi.cn/p/abcd
+ Link string `json:"link"`
+ //实例ID,可通过此ID去生成海报或者二维码
+ DSI string `json:"dsi"`
+ //小程序appid
+ AppId string `json:"app_id,omitempty"`
+ //小程序原始ID
+ AppSource string `json:"app_source,omitempty"`
+ } `json:"data"`
+}
diff --git a/sdk/dunion-go-sdk/model/option.go b/sdk/dunion-go-sdk/model/option.go
new file mode 100644
index 0000000..bca8d6c
--- /dev/null
+++ b/sdk/dunion-go-sdk/model/option.go
@@ -0,0 +1,7 @@
+package model
+
+import "time"
+
+type Option struct {
+ Timeout time.Duration
+}
diff --git a/sdk/dunion-go-sdk/model/order.go b/sdk/dunion-go-sdk/model/order.go
new file mode 100644
index 0000000..14e77fa
--- /dev/null
+++ b/sdk/dunion-go-sdk/model/order.go
@@ -0,0 +1,138 @@
+package model
+
+// swagger:model
+type ResponseOrderItem struct {
+ // 标题
+ Title string `json:"title"`
+ // 订单id
+ OrderId string `json:"order_id"`
+ // 业务线 `159`: 滴滴加油
+ // `210`: 滴滴网约车
+ // `393`: 滴滴货运
+ // `500`: 花小猪
+ // `120`: 滴滴代驾
+ ProductId string `json:"product_id"`
+ // 支付时间
+ PayTime int64 `json:"pay_time"`
+ // 支付金额,单位:分
+ PayPrice int64 `json:"pay_price"`
+ // CPA类型
+ // `cpa_normal`: 普通CPA(新用户奖励)
+ RefundPrice int64 `json:"refund_price"`
+ // 退款时间,秒级时间戳
+ RefundTime int64 `json:"refund_time"`
+ // CPS返佣金额,单位:分
+ CpsProfit int64 `json:"cps_profit"`
+ // CPA返佣金额,单位:分
+ CpaProfit int64 `json:"cpa_profit"`
+ // CPA类型
+ CpaType string `json:"cpa_type"`
+ // 推送状态: 1.已预估归因 2.预估订单已推送 3.预估订单推送失败 4.结算已提交 5.结算提交中 6.结算取消 7.结算成功 8.结算失败
+ Status int `json:"status"`
+ // 推广位ID
+ PromotionId int `json:"promotion_id"`
+ // 来源ID
+ SourceId string `json:"source_id"`
+ // 是否被风控
+ IsRisk int `json:"is_risk"`
+ // 下单用户openUID
+ // example:ecca7d66c984706aa94a15b656db2538
+ OpenUID string `json:"open_uid" structs:"open_uid"`
+ // 订单状态 `2`:已付款
+ // `8`:已退款
+ // example:2
+ OrderStatus int `json:"order_status" structs:"order_status"`
+}
+
+//swagger:model
+type OrderResponse struct {
+ Response
+ Data struct {
+ // 总数量
+ // example: 1
+ Total int `json:"total"`
+ OrderList []*ResponseOrderItem `json:"order_list"`
+ } `json:"data"`
+}
+
+//swagger:model
+type OrderCallbackResponse struct {
+ Response
+ Data struct {
+ // 标题
+ Title string `json:"title"`
+ // 订单id
+ OrderId string `json:"order_id"`
+ // 业务线 `159`: 滴滴加油
+ // `210`: 滴滴网约车
+ // `393`: 滴滴货运
+ // `500`: 花小猪
+ // `120`: 滴滴代驾
+ ProductId string `json:"product_id"`
+ // 支付时间
+ PayTime int64 `json:"pay_time"`
+ // 支付金额,单位:分
+ PayPrice int64 `json:"pay_price"`
+ // 退款金额,单位:分
+ RefundPrice int64 `json:"refund_price"`
+ // 退款时间,秒级时间戳
+ RefundTime int64 `json:"refund_time"`
+ // CPS返佣金额,单位:分
+ CpsProfit int64 `json:"cps_profit"`
+ // CPA返佣金额,单位:分
+ CpaProfit int64 `json:"cpa_profit"`
+ // CPA类型
+ CpaType string `json:"cpa_type"`
+ // 推送状态: 1.已预估归因 2.预估订单已推送 3.预估订单推送失败 4.结算已提交 5.结算提交中 6.结算取消 7.结算成功 8.结算失败
+ Status int `json:"status"`
+ // 推广位ID
+ PromotionId string `json:"promotion_id"`
+ // 来源ID
+ SourceId string `json:"source_id"`
+ // 是否被风控
+ IsRisk int `json:"is_risk"`
+ } `json:"data"`
+}
+
+type SelfQueryResponse struct {
+ Response
+ Data ResponseOrderSelfQuery `json:"data"`
+}
+
+//swagger:model
+type ResponseOrderSelfQuery struct {
+ // 推广成功列表
+ EstimateSuccessList []*EstimateSuccessData `json:"estimate_success_list,omitempty" structs:"estimate_success_list"`
+ // 推广失败列表
+ EstimateFailList []*EstimateFailData `json:"estimate_fail_list,omitempty" structs:"estimate_fail_list"`
+}
+
+//swagger:model
+type EstimateSuccessData struct {
+ // 推广成功时间
+ // example:2022-01-10 10:30:00
+ EstimateTime string `json:"estimate_time" structs:"estimate_time"`
+ // 推广渠道
+ // example:当前登陆账号注册的公司名
+ EstimateChannel string `json:"estimate_channel" structs:"estimate_channel"`
+ // 领券状态 `1`:成功
+ // `2`:失败
+ // example:1
+ ReceiveStatus int `json:"receive_status" structs:"receive_status"`
+ // 领券时间
+ // example:2022-01-10 10:00:00
+ ReceiveTime string `json:"receive_time" structs:"receive_time"`
+ // 业务线名称
+ // example:网约车
+ SceneName string `json:"scene_name" structs:"scene_name"`
+}
+
+//swagger:model
+type EstimateFailData struct {
+ // 失败原因
+ // example:未查询到有效绑定关系
+ FailReason string `json:"fail_reason" structs:"fail_reason"`
+ // 业务线名称
+ // example:网约车
+ SceneName string `json:"scene_name" structs:"scene_name"`
+}
diff --git a/sdk/dunion-go-sdk/model/poster.go b/sdk/dunion-go-sdk/model/poster.go
new file mode 100644
index 0000000..de265e1
--- /dev/null
+++ b/sdk/dunion-go-sdk/model/poster.go
@@ -0,0 +1,21 @@
+package model
+
+// swagger:model
+type QrcodeResponse struct {
+ Response
+ Data struct {
+ //生成的二维码链接
+ //example: https://example.com/img.jpg
+ CodeLink string `json:"code_link"`
+ } `json:"data"`
+}
+
+// swagger:model
+type PosterResponse struct {
+ Response
+ Data struct {
+ //生成的海报链接
+ //example: https://example.com/img.jpg
+ PosterLink string `json:"poster_link"`
+ } `json:"data"`
+}
diff --git a/sdk/dunion-go-sdk/model/response.go b/sdk/dunion-go-sdk/model/response.go
new file mode 100644
index 0000000..4634ef7
--- /dev/null
+++ b/sdk/dunion-go-sdk/model/response.go
@@ -0,0 +1,13 @@
+package model
+
+import "fmt"
+
+type Response struct {
+ Errno int64 `json:"errno"`
+ ErrMsg string `json:"errmsg"`
+ TraceID string `json:"traceid"`
+}
+
+func (r Response) ErrorMsg() string {
+ return fmt.Sprintf("错误码:%d, 错误信息: %s, traceID: %s", r.Errno, r.ErrMsg, r.TraceID)
+}
diff --git a/sdk/dunion-go-sdk/util/auth.go b/sdk/dunion-go-sdk/util/auth.go
new file mode 100644
index 0000000..496fc6e
--- /dev/null
+++ b/sdk/dunion-go-sdk/util/auth.go
@@ -0,0 +1,42 @@
+package util
+
+import (
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net/url"
+ "sort"
+ "strings"
+)
+
+func GetSign(params map[string]interface{}, accessKey string) string {
+ // key排序
+ arr := sort.StringSlice{}
+ for k := range params {
+ if k != "sign" {
+ arr = append(arr, k)
+ }
+ }
+ arr.Sort()
+ // 参数拼接
+ var build strings.Builder
+ for idx, k := range arr {
+ if idx != 0 {
+ build.WriteString("&")
+ }
+ build.WriteString(fmt.Sprintf("%s=%v", k, params[k]))
+ }
+ build.WriteString(accessKey)
+ // URL encode
+ sourceStr := url.QueryEscape(build.String())
+ // sha1加密
+ h := sha1.New()
+ _, _ = io.WriteString(h, sourceStr)
+ shaStr := hex.EncodeToString(h.Sum([]byte("")))
+ // 返回base64字符串
+ b64Str := base64.StdEncoding.EncodeToString([]byte(shaStr))
+ // base64字符串含有=和/,再一次URL encode
+ return url.QueryEscape(b64Str)
+}
diff --git a/sdk/dunion-go-sdk/util/log.go b/sdk/dunion-go-sdk/util/log.go
new file mode 100644
index 0000000..f21e45c
--- /dev/null
+++ b/sdk/dunion-go-sdk/util/log.go
@@ -0,0 +1,46 @@
+package util
+
+import (
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "gopkg.in/natefinch/lumberjack.v2"
+)
+
+type DunionLogger interface {
+ Infof(template string, args ...interface{})
+ Errorf(template string, args ...interface{})
+}
+
+// var unionLogger *zap.SugaredLogger
+var unionLogger DunionLogger
+
+func SetLogger(logger DunionLogger) {
+ unionLogger = logger
+}
+
+func InitLogger(logPath string) {
+ writeSyncer := getLogWriter(logPath)
+ encoder := getEncoder()
+ core := zapcore.NewCore(encoder, writeSyncer, zapcore.InfoLevel)
+
+ logger := zap.New(core, zap.AddCaller())
+ unionLogger = logger.Sugar()
+}
+
+func getEncoder() zapcore.Encoder {
+ encoderConfig := zap.NewProductionEncoderConfig()
+ encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+ encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
+ return zapcore.NewConsoleEncoder(encoderConfig)
+}
+
+func getLogWriter(logPath string) zapcore.WriteSyncer {
+ lumberJackLogger := &lumberjack.Logger{
+ Filename: logPath,
+ MaxSize: 500,
+ MaxBackups: 5,
+ MaxAge: 30,
+ Compress: false,
+ }
+ return zapcore.AddSync(lumberJackLogger)
+}
diff --git a/sdk/dunion-go-sdk/util/request.go b/sdk/dunion-go-sdk/util/request.go
new file mode 100644
index 0000000..5a56cd3
--- /dev/null
+++ b/sdk/dunion-go-sdk/util/request.go
@@ -0,0 +1,115 @@
+package util
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ netUrl "net/url"
+ "time"
+
+ consts "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/const"
+ "gitee.com/chengdu-lenntc/third-platform-sdk/sdk/dunion-go-sdk/model"
+)
+
+const (
+ AppKey = "App-Key"
+ Timestamp = "Timestamp"
+ Sign = "Sign"
+)
+
+var globalTimeoutDuration = 2 * time.Second
+
+func SetTimeoutDuration(timeout time.Duration) {
+ globalTimeoutDuration = timeout
+}
+
+func Post(ctx context.Context, appKey, accessKey, url string, body map[string]interface{}, opt ...model.Option) ([]byte, error) {
+ header := map[string]string{
+ AppKey: appKey,
+ Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
+ }
+ params := make(map[string]interface{})
+ for k, v := range body {
+ params[k] = v
+ }
+ for k, v := range header {
+ params[k] = v
+ }
+ header[Sign] = GetSign(params, accessKey)
+
+ bodyBytes, _ := json.Marshal(body)
+ reqReader := bytes.NewReader(bodyBytes)
+ req, _ := http.NewRequest("POST", url, reqReader)
+ traceID := uuid4()
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set(consts.TraceID, traceID)
+ req.Header.Set(consts.UserAgent, consts.SDKVersion)
+ if unionLogger != nil {
+ unionLogger.Infof("url=%s||headers=%v||data=%v", url, req.Header, body)
+ }
+ for key, value := range header {
+ req.Header.Set(key, value)
+ }
+ timeout := globalTimeoutDuration
+ if len(opt) > 0 {
+ timeout = opt[0].Timeout
+ }
+ client := &http.Client{Timeout: timeout}
+ response, err := client.Do(req)
+ if err != nil {
+ if unionLogger != nil {
+ unionLogger.Errorf("url=%s||headers=%v||data=%v||err=%v", url, req.Header, body, err)
+ }
+ return nil, err
+ }
+ return ioutil.ReadAll(response.Body)
+}
+
+func Get(ctx context.Context, appKey, accessKey, url string, param map[string]interface{}, opt ...model.Option) ([]byte, error) {
+ header := map[string]string{
+ AppKey: appKey,
+ Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
+ }
+ params := make(map[string]interface{})
+ for k, v := range param {
+ params[k] = v
+ }
+ for k, v := range header {
+ params[k] = v
+ }
+ header[Sign] = GetSign(params, accessKey)
+ query := netUrl.Values{}
+ for k, v := range params {
+ query.Add(k, fmt.Sprintf("%v", v))
+ }
+ if query.Encode() != "" {
+ url = url + "?" + query.Encode()
+ }
+
+ req, _ := http.NewRequest("GET", url, nil)
+ traceID := uuid4()
+ req.Header.Set(consts.TraceID, traceID)
+ req.Header.Set(consts.UserAgent, consts.SDKVersion)
+ if unionLogger != nil {
+ unionLogger.Infof("url=%s||headers=%v||data=%v", url, req.Header, param)
+ }
+ for key, value := range header {
+ req.Header.Set(key, value)
+ }
+ timeout := globalTimeoutDuration
+ if len(opt) > 0 {
+ timeout = opt[0].Timeout
+ }
+ client := &http.Client{Timeout: timeout}
+ response, err := client.Do(req)
+ if err != nil {
+ if unionLogger != nil {
+ unionLogger.Errorf("url=%s||headers=%v||data=%v||err=%v", url, req.Header, param, err)
+ }
+ return nil, err
+ }
+ return ioutil.ReadAll(response.Body)
+}
diff --git a/sdk/dunion-go-sdk/util/uuid.go b/sdk/dunion-go-sdk/util/uuid.go
new file mode 100644
index 0000000..4143920
--- /dev/null
+++ b/sdk/dunion-go-sdk/util/uuid.go
@@ -0,0 +1,9 @@
+package util
+
+import (
+ "github.com/google/uuid"
+)
+
+func uuid4() string {
+ return uuid.New().String()
+}