200 lines
4.8 KiB
Go
200 lines
4.8 KiB
Go
package biz
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"xy_sh/internal/config"
|
|
"xy_sh/internal/data/impl"
|
|
"xy_sh/internal/data/model"
|
|
"xy_sh/pkg"
|
|
"xy_sh/pkg/crypto"
|
|
"xy_sh/ymt_v3"
|
|
|
|
"gitea.cdlsxd.cn/self-tools/l_request"
|
|
|
|
"xy_sh/internal/entities"
|
|
)
|
|
|
|
type OrderStore struct {
|
|
orders *impl.OrderImpl
|
|
ymtClient *ymt_v3.Client
|
|
cfg *config.Config
|
|
}
|
|
|
|
func NewOrderStore(orders *impl.OrderImpl, ymtClient *ymt_v3.Client, cfg *config.Config) *OrderStore {
|
|
return &OrderStore{
|
|
orders: orders,
|
|
ymtClient: ymtClient,
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
func (o *OrderStore) CreateOrder(ctx context.Context, req *entities.OrderRequest) (*entities.OrderResponseData, error) {
|
|
var orderInfo model.XyShOrder
|
|
err := o.orders.GetByKey(ctx, "act_order_num", req.ActOrderNum, &orderInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if orderInfo.ID == 0 {
|
|
orderInfo = model.XyShOrder{
|
|
ActOrderNum: req.ActOrderNum,
|
|
GoodsCode: req.GoodsCode,
|
|
ActCode: req.ActCode,
|
|
Account: req.Account,
|
|
Status: entities.StatusSuccess,
|
|
CallBackURL: req.CallbackUrl,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
selfPublicIP, err := pkg.GetPublicIP()
|
|
if err != nil {
|
|
selfPublicIP = "120.55.12.245"
|
|
}
|
|
result, err := o.ymtClient.KeyOrder(&ymt_v3.KeyOrderRequest{
|
|
OutBizNo: req.ActOrderNum,
|
|
ActivityNo: o.cfg.Ymt.ActivityNo,
|
|
Account: req.Account,
|
|
NotifyURL: selfPublicIP + ":" + o.cfg.ServerPort + "/callback/notify",
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
orderInfo.CouponNo = result.URL
|
|
orderInfo.CouponCode = result.Key
|
|
orderInfo.OrderNo = result.TradeNo
|
|
orderInfo.Status = int32(result.Status)
|
|
var validTime time.Time
|
|
if len(result.ValidEndTime) != 0 {
|
|
validTime, err = time.Parse(time.DateTime, result.ValidEndTime)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("解析失败: %v\n", err)
|
|
}
|
|
}
|
|
orderInfo.ExpireTime = validTime
|
|
if orderInfo.ID == 0 {
|
|
err = o.orders.Add(ctx, &orderInfo)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &entities.OrderResponseData{
|
|
OrderNo: result.TradeNo,
|
|
CouponNo: result.URL,
|
|
CouponCode: result.Key,
|
|
Status: o.getStatus(result.Status, result.ValidEndTime, err),
|
|
ExpireTime: result.ValidEndTime,
|
|
}, nil
|
|
}
|
|
|
|
func (o *OrderStore) getStatus(ymtStatus uint32, ymtValidTime string, err error) int {
|
|
if err != nil {
|
|
return entities.StatusFailed
|
|
}
|
|
switch ymtStatus {
|
|
case 2:
|
|
return entities.StatusRedeemed
|
|
case 3:
|
|
return entities.StatusFailed
|
|
default:
|
|
t, err := time.Parse(time.DateTime, ymtValidTime)
|
|
if err != nil {
|
|
fmt.Printf("解析失败: %v\n", err)
|
|
return entities.StatusFailed
|
|
}
|
|
if t.Unix() <= time.Now().Unix() {
|
|
return entities.StatusExpired
|
|
}
|
|
return entities.StatusSuccess
|
|
}
|
|
}
|
|
|
|
func (o *OrderStore) QueryOrder(req *entities.QueryOrderRequest) (*entities.QueryOrderResponseData, error) {
|
|
result, err := o.ymtClient.KeyQuery(&ymt_v3.KeyQueryRequest{
|
|
TradeNo: req.OrderNo,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := &entities.QueryOrderResponseData{
|
|
OrderNo: result.TradeNo,
|
|
Status: o.getStatus(result.Status, result.ValidEndTime, err),
|
|
Account: result.Account,
|
|
}
|
|
|
|
if result.URL != "" {
|
|
resp.CardInfo = &entities.CardInfo{
|
|
CouponNo: result.URL,
|
|
CouponCode: result.Key,
|
|
ExpireTime: result.ValidEndTime,
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (o *OrderStore) HandleCallback(req *entities.CallBack) error {
|
|
callBackData := &entities.CallbackRequest{
|
|
OrderNo: req.Data.TradeNo,
|
|
ActOrderNum: req.Data.OutBizNo,
|
|
Status: o.getStatus(req.Data.Status, req.Data.ValidEndTime, nil),
|
|
Account: req.Data.Account,
|
|
CardInfo: &entities.CardInfo{
|
|
CouponNo: req.Data.Url,
|
|
CouponCode: req.Data.Key,
|
|
ExpireTime: req.Data.ValidEndTime,
|
|
},
|
|
}
|
|
sign, jsonData, err := makeEncryptedRequest(callBackData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
requset := &l_request.Request{
|
|
Method: http.MethodPost,
|
|
Json: jsonData,
|
|
Url: o.cfg.NotifyUrl,
|
|
Headers: map[string]string{
|
|
"Content-Type": "application/json",
|
|
"timestamp": fmt.Sprintf("%d", time.Now().UnixMilli()),
|
|
"sign": sign,
|
|
},
|
|
}
|
|
res, err := requset.Send()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.ToLower(res.Text) != "ok" {
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func makeEncryptedRequest(bizData interface{}) (string, map[string]interface{}, error) {
|
|
bizJSON, err := json.Marshal(bizData)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("业务数据序列化失败: %v", err)
|
|
}
|
|
|
|
encryptedData, err := crypto.SM4CBCEncrypt(bizJSON)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("加密失败: %v", err)
|
|
}
|
|
|
|
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
|
|
|
sign := crypto.GenerateSign(timestamp, encryptedData)
|
|
|
|
reqBody := entities.EncryptedRequest{
|
|
EncryptedData: encryptedData,
|
|
}
|
|
reqJSON, _ := pkg.StructToMap(reqBody)
|
|
return sign, reqJSON, err
|
|
}
|