滴滴联盟的api接入

This commit is contained in:
wukesheng 2024-05-13 23:18:54 +08:00
parent 629a7a223b
commit ac4dff4713
19 changed files with 912 additions and 0 deletions

4
go.mod
View File

@ -6,6 +6,10 @@ require (
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/zeromicro/go-zero v1.5.5 github.com/zeromicro/go-zero v1.5.5
github.com/zywaited/xcopy v1.1.0 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 ( require (

View File

@ -3,6 +3,7 @@ package third_platform_sdk
import ( import (
"github.com/zeromicro/go-zero/core/logx" "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" elemeunion "gitee.com/chengdu-lenntc/third-platform-sdk/platform/eleme-union"
meituancsr "gitee.com/chengdu-lenntc/third-platform-sdk/platform/meituan-csr" meituancsr "gitee.com/chengdu-lenntc/third-platform-sdk/platform/meituan-csr"
meituanunion "gitee.com/chengdu-lenntc/third-platform-sdk/platform/meituan-union" 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 { func NewMeituanUnionApi(log logx.Logger, conf meituanunion.AuthConfig) meituanunion.MeituanUnionApi {
return meituanunion.NewApiClient(log, conf) return meituanunion.NewApiClient(log, conf)
} }
// NewDidiUnionApi 滴滴联盟
func NewDidiUnionApi(log logx.Logger, conf didiunion.AuthConfig) didiunion.DidiUnionApi {
return didiunion.NewApiClient(log, conf)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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" //代驾
)

View File

@ -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
}

View File

@ -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)|模拟订单回调,需先取链得到 dsitype_ 可取 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)| 订单归因问题自查询|

View File

@ -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 模拟订单回调,需先取链得到 dsitype_ 可取 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
}

View File

@ -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"
)

View File

@ -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"`
}

View File

@ -0,0 +1,7 @@
package model
import "time"
type Option struct {
Timeout time.Duration
}

View File

@ -0,0 +1,138 @@
package model
// swagger:model
type ResponseOrderItem struct {
// 标题
Title string `json:"title"`
// 订单id
OrderId string `json:"order_id"`
// 业务线 `159`: 滴滴加油<br>
// `210`: 滴滴网约车<br>
// `393`: 滴滴货运<br>
// `500`: 花小猪<br>
// `120`: 滴滴代驾
ProductId string `json:"product_id"`
// 支付时间
PayTime int64 `json:"pay_time"`
// 支付金额,单位:分
PayPrice int64 `json:"pay_price"`
// CPA类型<br>
// `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`: 滴滴加油<br>
// `210`: 滴滴网约车<br>
// `393`: 滴滴货运<br>
// `500`: 花小猪<br>
// `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"`
}

View File

@ -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"`
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -0,0 +1,9 @@
package util
import (
"github.com/google/uuid"
)
func uuid4() string {
return uuid.New().String()
}