cmb
This commit is contained in:
parent
9b8823cc29
commit
9851c1f61b
|
|
@ -6,5 +6,7 @@ import (
|
|||
)
|
||||
|
||||
type CmbMixRepo interface {
|
||||
OrderVerify(ctx context.Context, req *v1.CmbRequest, funcName string) (*v1.CmbOrderRequest, error)
|
||||
ProductQueryVerify(ctx context.Context, req *v1.CmbRequest, funcName string) (*v1.CmbQueryProductRequest, error)
|
||||
GetRequestData(ctx context.Context, funcName, bizJsonStr string) (*v1.CmbRequest, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package mixrepoimpl
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -20,28 +22,59 @@ func NewCmbMixRepoImpl(bc *conf.Bootstrap) mixrepos.CmbMixRepo {
|
|||
return &CmbMixRepoImpl{bc: bc}
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetRequestData(_ context.Context, funcName, bizJsonStr string) (*v1.CmbRequest, error) {
|
||||
// 我们的sm2 公钥加密
|
||||
// 请求到我们这边 使用 我们的私钥解密
|
||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizJsonStr)
|
||||
func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest, funcName string) (*v1.CmbOrderRequest, error) {
|
||||
bizStr, err := s.Verify(ctx, req, funcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
date := time.Now().Format("20060102150405")
|
||||
|
||||
reply := &v1.CmbRequest{
|
||||
Mid: s.bc.Cmb.Mid,
|
||||
Aid: s.bc.Cmb.Aid,
|
||||
Date: date,
|
||||
Random: string(cmb.RandomBytes(16)),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: encryptBody,
|
||||
Sign: "",
|
||||
var resp *v1.CmbOrderRequest
|
||||
if err = json.Unmarshal([]byte(bizStr), &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvRows := helper.SortStructFieldsByKey(reply)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest, funcName string) (*v1.CmbQueryProductRequest, error) {
|
||||
bizStr, err := s.Verify(ctx, req, funcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp *v1.CmbQueryProductRequest
|
||||
if err = json.Unmarshal([]byte(bizStr), &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) Verify(ctx context.Context, req *v1.CmbRequest, funcName string) (string, error) {
|
||||
str, err := s.getSignStr(ctx, req, funcName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !b {
|
||||
return "", errors.New("签名验证失败")
|
||||
}
|
||||
|
||||
bizStr, err := cmb.Decrypt(s.bc.Cmb.Sm2Prk, req.EncryptBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return bizStr, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) getSignStr(_ context.Context, req *v1.CmbRequest, funcName string) (string, error) {
|
||||
kvRows := helper.SortStructFieldsByKey(req)
|
||||
|
||||
var strToBeSigned strings.Builder
|
||||
for _, kv := range kvRows {
|
||||
|
|
@ -54,15 +87,75 @@ func (s *CmbMixRepoImpl) GetRequestData(_ context.Context, funcName, bizJsonStr
|
|||
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%s?%s", funcName, strings.TrimRight(strToBeSigned.String(), "&"))
|
||||
return fmt.Sprintf("%s?%s", funcName, strings.TrimRight(strToBeSigned.String(), "&")), nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetRequestData(ctx context.Context, funcName, bizJsonStr string) (*v1.CmbRequest, error) {
|
||||
|
||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizJsonStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
date := time.Now().Format("20060102150405")
|
||||
|
||||
req := &v1.CmbRequest{
|
||||
Mid: s.bc.Cmb.Mid,
|
||||
Aid: s.bc.Cmb.Aid,
|
||||
Date: date,
|
||||
Random: string(cmb.RandomBytes(16)),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: encryptBody,
|
||||
Sign: "",
|
||||
}
|
||||
|
||||
str, err := s.getSignStr(ctx, req, funcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sing, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply.Sign = sing
|
||||
req.Sign = sing
|
||||
|
||||
return reply, nil
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetResponseData(ctx context.Context, funcName, bizJsonStr string) (*v1.CmbRequest, error) {
|
||||
|
||||
encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizJsonStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
date := time.Now().Format("20060102150405")
|
||||
|
||||
req := &v1.CmbRequest{
|
||||
Mid: s.bc.Cmb.Mid,
|
||||
Aid: s.bc.Cmb.Aid,
|
||||
Date: date,
|
||||
Random: string(cmb.RandomBytes(16)),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: encryptBody,
|
||||
Sign: "",
|
||||
}
|
||||
|
||||
str, err := s.getSignStr(ctx, req, funcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sing, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Sign = sing
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) Request(ctx context.Context, funcName, bizJsonStr string) (*v1.CmbRequest, error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Headers http.Header
|
||||
|
||||
StatusCodeFunc func(int) bool
|
||||
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func NewOptions(options ...Option) *Options {
|
||||
o := &Options{
|
||||
Headers: http.Header{
|
||||
"Content-Type": []string{"application/json"},
|
||||
},
|
||||
|
||||
StatusCodeFunc: func(code int) bool {
|
||||
return code == http.StatusOK
|
||||
},
|
||||
|
||||
Timeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(options *Options) {
|
||||
options.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeaders(headers http.Header) Option {
|
||||
return func(options *Options) {
|
||||
options.Headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
func WithStatusCodeFunc(statusCodeFunc func(int) bool) Option {
|
||||
return func(options *Options) {
|
||||
options.StatusCodeFunc = statusCodeFunc
|
||||
}
|
||||
}
|
||||
|
||||
func Post(ctx context.Context, url string, body []byte, options ...Option) (http.Header, []byte, error) {
|
||||
return Request(ctx, http.MethodPost, url, body, NewOptions(options...))
|
||||
}
|
||||
|
||||
func Get(ctx context.Context, url string, options ...Option) (http.Header, []byte, error) {
|
||||
return Request(ctx, http.MethodGet, url, nil, NewOptions(options...))
|
||||
}
|
||||
|
||||
func Put(ctx context.Context, url string, body []byte, options ...Option) (http.Header, []byte, error) {
|
||||
return Request(ctx, http.MethodPut, url, body, NewOptions(options...))
|
||||
}
|
||||
|
||||
func Request(_ context.Context, method, url string, body []byte, o *Options) (http.Header, []byte, error) {
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("创建HTTP请求失败: %w", err)
|
||||
}
|
||||
req.Header = o.Headers
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: o.Timeout,
|
||||
}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("发送HTTP请求失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("读取响应体失败: %w", err)
|
||||
}
|
||||
|
||||
if !o.StatusCodeFunc(resp.StatusCode) {
|
||||
return nil, nil, fmt.Errorf("请求异常:%s", resp.Status)
|
||||
}
|
||||
|
||||
return resp.Header, bodyBytes, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Get(t *testing.T) {
|
||||
uri := "https://gateway.dev.cdlsxd.cn/adminyx/admin/v1/key_batch/list"
|
||||
|
||||
uv := url.Values{}
|
||||
uv.Set("page", "1")
|
||||
uv.Set("limit", "2")
|
||||
|
||||
h := http.Header{
|
||||
"Content-Type": []string{"application/x-www-form-urlencoded"},
|
||||
}
|
||||
respHeader, respBody, err := Get(context.Background(), uri+"?"+uv.Encode(), WithHeaders(h))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("响应体:", string(respBody))
|
||||
t.Logf("响应头:", respHeader)
|
||||
}
|
||||
|
||||
func Test_RequestHeaders(t *testing.T) {
|
||||
uri := "http://example.com/api"
|
||||
body := []byte("request body")
|
||||
|
||||
h := http.Header{
|
||||
"Content-Type": []string{"application/json"},
|
||||
"Authorization": []string{"Bearer token"},
|
||||
}
|
||||
|
||||
respHeader, respBody, err := Post(context.Background(), uri, body, WithTimeout(10*time.Second), WithHeaders(h))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("响应体:", string(respBody))
|
||||
t.Logf("响应头:", respHeader)
|
||||
}
|
||||
|
||||
func Test_RequestStatusCode(t *testing.T) {
|
||||
uri := "http://example.com/api/update"
|
||||
body := []byte("update data")
|
||||
|
||||
isSuccess := func(code int) bool {
|
||||
return code == http.StatusOK || code == http.StatusCreated
|
||||
}
|
||||
|
||||
respHeader, respBody, err := Put(context.Background(), uri, body, WithStatusCodeFunc(isSuccess))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("响应体:", string(respBody))
|
||||
t.Logf("响应头:", respHeader)
|
||||
}
|
||||
|
|
@ -40,8 +40,8 @@ func NewHTTPServer(
|
|||
cmb.POST("/v1/orderMock", voucherService.CmbOrderMock)
|
||||
cmb.POST("/v1/order", voucherService.CmbOrder)
|
||||
|
||||
cmb.POST("/v1/productQueryMock", voucherService.CmbProductQueryMock)
|
||||
cmb.POST("/v1/productQuery", voucherService.CmbProductQuery)
|
||||
cmb.POST("/v1/product/QueryMock", voucherService.CmbProductQueryMock)
|
||||
cmb.POST("/v1/product/query", voucherService.CmbProductQuery)
|
||||
|
||||
return srv
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,81 +7,87 @@ import (
|
|||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
const (
|
||||
cmbOrderFuncName = "/voucher/cmb/v1/order"
|
||||
cmbProductQueryFuncName = "/voucher/cmb/v1/product/query"
|
||||
)
|
||||
|
||||
func (s *VoucherService) CmbOrder(ctx http.Context) error {
|
||||
|
||||
var req v1.CmbOrderReply
|
||||
if err := ctx.BindForm(&req); err != nil {
|
||||
reply, err := s.cmbOrder(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reply := &v1.CmbReply{
|
||||
//RespCode: "1000",
|
||||
//RespMsg: "成功",
|
||||
//Date: "",
|
||||
//KeyAlias: "",
|
||||
//CmbKeyAlias: "",
|
||||
//EncryptBody: "",
|
||||
//Sign: "",
|
||||
}
|
||||
|
||||
return ctx.JSON(200, reply)
|
||||
}
|
||||
|
||||
func (s *VoucherService) cmbOrder(ctx http.Context) (string, error) {
|
||||
func (s *VoucherService) cmbOrder(ctx http.Context) (*v1.CmbOrderReply, error) {
|
||||
|
||||
var req v1.CmbRequest
|
||||
var req *v1.CmbRequest
|
||||
if err := ctx.BindForm(&req); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo 签名验证
|
||||
bizContent, err := s.CmbMixRepo.OrderVerify(ctx, req, cmbOrderFuncName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
boReq := &bo.OrderCreateReqBo{
|
||||
//OutBizNo: req.TransactionId,
|
||||
//ProductNo: req.ActivityId,
|
||||
//Account: req.CmbUid,
|
||||
OutBizNo: bizContent.TransactionId,
|
||||
ProductNo: bizContent.ActivityId,
|
||||
Account: bizContent.CmbUid,
|
||||
AccountType: vo.OrderAccountTypeOpenId,
|
||||
}
|
||||
|
||||
orderNo, err := s.VoucherBiz.CmbOrder(ctx, boReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orderNo, nil
|
||||
return &v1.CmbOrderReply{
|
||||
CodeNo: orderNo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *VoucherService) CmbProductQuery(ctx http.Context) error {
|
||||
|
||||
var req v1.CmbQueryProductRequest
|
||||
if err := ctx.BindForm(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q, err := s.VoucherBiz.CmbProductQuery(ctx, req.ActivityId)
|
||||
reply, err := s.cmbProductQuery(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 数据构造 加签 返回
|
||||
rep := &v1.CmbQueryProductReply{
|
||||
//RespCode: "",
|
||||
//RespMsg: "",
|
||||
//Date: "",
|
||||
//KeyAlias: "",
|
||||
//CmbKeyAlias: "",
|
||||
//EncryptBody: "",
|
||||
//Sign: "",
|
||||
ActivityName: "",
|
||||
ActivityId: *q.StockId,
|
||||
return ctx.JSON(200, reply)
|
||||
}
|
||||
|
||||
func (s *VoucherService) cmbProductQuery(ctx http.Context) (*v1.CmbQueryProductReply, error) {
|
||||
var req *v1.CmbRequest
|
||||
if err := ctx.BindForm(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bizContent, err := s.CmbMixRepo.ProductQueryVerify(ctx, req, cmbProductQueryFuncName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wechatResp, err := s.VoucherBiz.CmbProductQuery(ctx, bizContent.ActivityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &v1.CmbQueryProductReply{
|
||||
ActivityName: *wechatResp.GoodsName,
|
||||
ActivityId: "",
|
||||
Amount: "",
|
||||
MinAmount: "",
|
||||
AvailableType: "",
|
||||
|
|
@ -90,7 +96,5 @@ func (s *VoucherService) CmbProductQuery(ctx http.Context) error {
|
|||
EndTime: "",
|
||||
AvailableStock: "",
|
||||
Detail: "",
|
||||
}
|
||||
|
||||
return ctx.JSON(200, rep)
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue