1.咪咕钻石会员优化,提交接口返回消息改成异步,此接口不再记录订单数据;

2.回调通知增加失败通知;
This commit is contained in:
chenlin 2025-05-14 18:03:52 +08:00
parent ee6746e9d8
commit eae0fceb11
5 changed files with 223 additions and 133 deletions

View File

@ -7,7 +7,6 @@ import (
"github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/logger"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/common/apis" "go-admin/common/apis"
"go-admin/tools"
"go-admin/tools/app" "go-admin/tools/app"
"net/http" "net/http"
"time" "time"
@ -201,23 +200,23 @@ func (e MiGuDeployService) SubmitOrder(c *gin.Context) {
} }
if resp.Data.LinkId != "" { // 提交订阅成功则记录到订单表 if resp.Data.LinkId != "" { // 提交订阅成功则记录到订单表
// 记录到订单列表 //// 记录到订单列表
var orderInfo models.MgOrder //var orderInfo models.MgOrder
orderInfo.ProductID = models.ProductID //orderInfo.ProductID = models.ProductID
orderInfo.ChannelCode = req.Channel //orderInfo.ChannelCode = req.Channel
orderInfo.OrderSerial = logInfo.OutTradeNo //orderInfo.OrderSerial = logInfo.OutTradeNo
nowTime := time.Now() //nowTime := time.Now()
orderInfo.SubscribeTime = &nowTime //orderInfo.SubscribeTime = &nowTime
orderInfo.PhoneNumber = req.Phone //orderInfo.PhoneNumber = req.Phone
orderInfo.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, req.Phone) //orderInfo.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, req.Phone)
orderInfo.ExternalOrderID = resp.Data.LinkId // 外部平台订单号咪咕返回的linkId) //orderInfo.ExternalOrderID = resp.Data.LinkId // 外部平台订单号咪咕返回的linkId)
//
err = e.Orm.Create(&orderInfo).Error //err = e.Orm.Create(&orderInfo).Error
if err != nil { //if err != nil {
logger.Info("Create MgTransactionLog err:", err) // logger.Info("Create MgTransactionLog err:", err)
//app.Error(c, http.StatusBadRequest, err, err.Error()) // //app.Error(c, http.StatusBadRequest, err, err.Error())
//return // //return
} //}
} }
fmt.Println("SubmitOrder-end") fmt.Println("SubmitOrder-end")

View File

@ -6,7 +6,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/logger" "github.com/go-admin-team/go-admin-core/logger"
"go-admin/app/admin/models" "go-admin/app/admin/models"
"go-admin/tools"
"go-admin/tools/app" "go-admin/tools/app"
"net/http" "net/http"
"time" "time"
@ -235,24 +234,24 @@ func (e MiGuDeployService) SubmitOrderEx(c *gin.Context) {
} }
if resp.Data.LinkId != "" { // 提交订阅成功则记录到订单表 if resp.Data.LinkId != "" { // 提交订阅成功则记录到订单表
// 记录到订单列表 //// 记录到订单列表
var orderInfo models.MgOrder //var orderInfo models.MgOrder
orderInfo.ProductID = models.ProductID //orderInfo.ProductID = models.ProductID
orderInfo.ChannelCode = req.Channel //orderInfo.ChannelCode = req.Channel
orderInfo.OrderSerial = logInfo.OutTradeNo //orderInfo.OrderSerial = logInfo.OutTradeNo
nowTime := time.Now() //nowTime := time.Now()
orderInfo.SubscribeTime = &nowTime //orderInfo.SubscribeTime = &nowTime
orderInfo.PhoneNumber = req.Phone //orderInfo.PhoneNumber = req.Phone
orderInfo.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, req.Phone) //orderInfo.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, req.Phone)
orderInfo.ExternalOrderID = resp.Data.LinkId // 外部平台订单号咪咕返回的linkId) //orderInfo.ExternalOrderID = resp.Data.LinkId // 外部平台订单号咪咕返回的linkId)
orderInfo.ChannelTradeNo = req.OutTradeNo // 渠道订单号 //orderInfo.ChannelTradeNo = req.OutTradeNo // 渠道订单号
//
err = e.Orm.Create(&orderInfo).Error //err = e.Orm.Create(&orderInfo).Error
if err != nil { //if err != nil {
logger.Info("Create MgTransactionLog err:", err) // logger.Info("Create MgTransactionLog err:", err)
app.MiGuError(c, http.StatusBadRequest, err, err.Error()) // app.MiGuError(c, http.StatusBadRequest, err, err.Error())
return // return
} //}
resp.Data.LinkId = outTradeNo resp.Data.LinkId = outTradeNo
} }

View File

@ -25,13 +25,15 @@ import (
// @Router /api/v1/notice/subscribe [get] // @Router /api/v1/notice/subscribe [get]
func (e MiGuDeployService) SubscribeNotice(c *gin.Context) { func (e MiGuDeployService) SubscribeNotice(c *gin.Context) {
fmt.Println("SubscribeNotice-start") fmt.Println("SubscribeNotice-start")
logger.Info("订购成功通知-start") logger.Info("订购通知-start")
fmt.Println("URL:", c.Request.URL.String()) fmt.Println("URL:", c.Request.URL.String())
orderId := c.Query("orderId") // 对应linkId orderId := c.Query("orderId") // 对应linkId
extData := c.Query("extData") // 对应outTradeNo extData := c.Query("extData") // 对应outTradeNo
status := c.Query("status") // status: 1成功 不存在status也认为是订购成功通知 status := c.Query("status") // status: 1成功 -1: 失败
rsCode := c.Query("rsCode") // 失败编码
rsMsg := c.Query("rsMsg") // 失败原因
fmt.Println("orderId:", orderId) fmt.Println("orderId:", orderId)
fmt.Println("extData:", extData) fmt.Println("extData:", extData)
@ -44,7 +46,7 @@ func (e MiGuDeployService) SubscribeNotice(c *gin.Context) {
return return
} }
if !(status == "1" || status == "") { if !(status == "1" || status == "-1") {
logger.Error("status is", status) logger.Error("status is", status)
app.MiGuNoticeOK(c, fmt.Sprintf("failed,status is:%s", status)) app.MiGuNoticeOK(c, fmt.Sprintf("failed,status is:%s", status))
return return
@ -58,25 +60,128 @@ func (e MiGuDeployService) SubscribeNotice(c *gin.Context) {
return return
} }
var orderInfo models.MgOrder if status == "1" { // 订购成功
for i := 0; i < 3; i++ { fmt.Println("订购成功通知status is 1")
// 查询订单表是否有记录 logger.Info("订购成功通知status is 1")
err = e.Orm.Table("mg_order").Where("external_order_id = ?", orderId).First(&orderInfo).Error var orderInfo models.MgOrder
if err != nil && err.Error() != "record not found" { for i := 0; i < 3; i++ {
logger.Errorf("SubscribeNotice query mg_order err:", err.Error()) // 查询订单表是否有记录
app.MiGuNoticeOK(c, "failed") err = e.Orm.Table("mg_order").Where("external_order_id = ?", orderId).First(&orderInfo).Error
return if err != nil && err.Error() != "record not found" {
logger.Errorf("SubscribeNotice query mg_order err:", err.Error())
app.MiGuNoticeOK(c, "failed")
return
}
if errors.Is(err, gorm.ErrRecordNotFound) { // 没有查询到记录循环查询3次
time.Sleep(800 * time.Millisecond)
} else if err == nil { // 查询到记录,则跳出循环
break
}
} }
if errors.Is(err, gorm.ErrRecordNotFound) { // 没有查询到记录循环查询3次 var checkPhoneNum string
time.Sleep(800 * time.Millisecond) if errors.Is(err, gorm.ErrRecordNotFound) { // 订单表没有记录
} else if err == nil { // 查询到记录,则跳出循环 // 查询交易流水表
break var logInfo models.MgTransactionLog
} err = e.Orm.Table("mg_transaction_log").
} Where("link_id = ?", orderId).First(&logInfo).Error
if err != nil {
logger.Errorf("SubscribeNotice query mg_transaction_log err:", err.Error())
app.MiGuNoticeOK(c, "failed")
return
}
checkPhoneNum = logInfo.PhoneNumber
// 插入订单表
var inOrder models.MgOrder
inOrder.CreatedAt = logInfo.CreatedAt
inOrder.UpdatedAt = logInfo.UpdatedAt
inOrder.ProductID = logInfo.ProductID
inOrder.ChannelCode = logInfo.ChannelCode
inOrder.OrderSerial = logInfo.OutTradeNo
strTime := time.Now()
inOrder.SubscribeTime = &strTime
inOrder.PhoneNumber = logInfo.PhoneNumber
inOrder.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, logInfo.PhoneNumber)
inOrder.ExternalOrderID = logInfo.LinkId
inOrder.ChannelTradeNo = logInfo.ChannelTradeNo
inOrder.State = models.SubscribeOK
err = e.Orm.Create(&inOrder).Error
if err != nil {
logger.Info("Create MgOrder err:", err)
app.MiGuError(c, http.StatusBadRequest, err, err.Error())
return
}
orderInfo.ChannelCode = inOrder.ChannelCode
orderInfo.OrderSerial = inOrder.OrderSerial
orderInfo.ChannelTradeNo = inOrder.ChannelTradeNo
} else { // 订单表有记录
checkPhoneNum = orderInfo.PhoneNumber
if orderInfo.State != 1 {
err = e.Orm.Table("mg_order").Where("external_order_id = ?", orderId).Updates(map[string]interface{}{
"state": models.SubscribeOK,
"subscribe_time": time.Now(),
"updated_at": time.Now(),
}).Error
if err != nil {
logger.Errorf("SubscribeNotice update mg_order err:", err.Error())
app.MiGuNoticeOK(c, "failed")
return
}
}
}
// 判断是否为子渠道订购,是的话需要通知子渠道
if models.IsValidChannelEx(orderInfo.ChannelCode, e.Orm) {
channelInfo, err := models.GetChannelInfoByChannelCode(orderInfo.ChannelCode, e.Orm)
if err != nil {
fmt.Println("SubscribeNotice GetChannelInfoByChannelCode err:", err)
fmt.Println("SubscribeNotice sub_channel_code is:", orderInfo.ChannelCode)
logger.Errorf("SubscribeNotice GetChannelInfoByChannelCode err, sub_channel_code is:", orderInfo.ChannelCode)
}
if channelInfo.SubscribeURL != "" {
for i := 0; i < 3; i++ {
resp, err := models.NoticeSubChannel(channelInfo.SubscribeURL, orderInfo.OrderSerial,
orderInfo.ChannelTradeNo, "1", channelInfo.MainChannelCode, channelInfo.Remarks,
"", "")
if err != nil {
fmt.Println("NoticeSubChannel err:", err)
fmt.Println("i is:", i)
continue
}
if resp != "success" {
continue
} else {
break
}
}
} else {
fmt.Println("SubscribeNotice SubscribeURL is null, sub_channel_code is:", orderInfo.ChannelCode)
logger.Error("SubscribeNotice SubscribeURL is null, sub_channel_code is:", orderInfo.ChannelCode)
}
}
var req models.QueryRightsInfoReq
req.AppChannelList = append(req.AppChannelList, models.ChannelCode)
req.Mobile = checkPhoneNum
resp, err := models.MiGuQueryRightsInfo(&req)
if err != nil {
fmt.Println("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
logger.Errorf("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
}
fmt.Println("CheckOrderState MiGuQueryRightsInfo resp:", resp)
fmt.Println("订购成功通知-end")
logger.Info("订购成功通知-end")
} else if status == "-1" { // 订购失败
fmt.Println("订购失败通知status is -1")
logger.Info("订购失败通知status is -1")
var checkPhoneNum string
if errors.Is(err, gorm.ErrRecordNotFound) { // 订单表没有记录
// 查询交易流水表 // 查询交易流水表
var logInfo models.MgTransactionLog var logInfo models.MgTransactionLog
err = e.Orm.Table("mg_transaction_log"). err = e.Orm.Table("mg_transaction_log").
@ -87,93 +192,59 @@ func (e MiGuDeployService) SubscribeNotice(c *gin.Context) {
return return
} }
checkPhoneNum = logInfo.PhoneNumber // 解析错误信息
msg, _ := tools.DecodeURL(rsMsg)
// 插入订单表 // 更新交易流水表
var inOrder models.MgOrder err = e.Orm.Table("mg_transaction_log").Where("link_id = ?", orderId).Updates(map[string]interface{}{
inOrder.CreatedAt = logInfo.CreatedAt "result": rsCode,
inOrder.UpdatedAt = logInfo.UpdatedAt "reason": msg,
inOrder.ProductID = logInfo.ProductID "updated_at": time.Now(),
inOrder.ChannelCode = logInfo.ChannelCode }).Error
inOrder.OrderSerial = logInfo.OutTradeNo
strTime := time.Now()
inOrder.SubscribeTime = &strTime
inOrder.PhoneNumber = logInfo.PhoneNumber
inOrder.SM4PhoneNumber, _ = tools.SM4Encrypt(models.SM4KEy, logInfo.PhoneNumber)
inOrder.ExternalOrderID = logInfo.LinkId
inOrder.ChannelTradeNo = logInfo.ChannelTradeNo
inOrder.State = models.SubscribeOK
err = e.Orm.Create(&inOrder).Error
if err != nil { if err != nil {
logger.Info("Create MgOrder err:", err) logger.Errorf("SubscribeNotice update mg_transaction_log err:", err.Error())
app.MiGuError(c, http.StatusBadRequest, err, err.Error()) app.MiGuNoticeOK(c, "failed")
return return
} }
orderInfo.ChannelCode = inOrder.ChannelCode
orderInfo.OrderSerial = inOrder.OrderSerial // 判断是否为子渠道订购,是的话需要通知子渠道
orderInfo.ChannelTradeNo = inOrder.ChannelTradeNo if models.IsValidChannelEx(logInfo.ChannelCode, e.Orm) {
} else { // 订单表有记录 channelInfo, err := models.GetChannelInfoByChannelCode(logInfo.ChannelCode, e.Orm)
checkPhoneNum = orderInfo.PhoneNumber
if orderInfo.State != 1 {
err = e.Orm.Table("mg_order").Where("external_order_id = ?", orderId).Updates(map[string]interface{}{
"state": models.SubscribeOK,
"subscribe_time": time.Now(),
"updated_at": time.Now(),
}).Error
if err != nil { if err != nil {
logger.Errorf("SubscribeNotice update mg_order err:", err.Error()) fmt.Println("SubscribeNotice GetChannelInfoByChannelCode err:", err)
app.MiGuNoticeOK(c, "failed") fmt.Println("SubscribeNotice sub_channel_code is:", logInfo.ChannelCode)
return logger.Errorf("SubscribeNotice GetChannelInfoByChannelCode err, sub_channel_code is:", logInfo.ChannelCode)
}
}
}
// 判断是否为子渠道订购,是的话需要通知子渠道
if models.IsValidChannelEx(orderInfo.ChannelCode, e.Orm) {
channelInfo, err := models.GetChannelInfoByChannelCode(orderInfo.ChannelCode, e.Orm)
if err != nil {
fmt.Println("SubscribeNotice GetChannelInfoByChannelCode err:", err)
fmt.Println("SubscribeNotice sub_channel_code is:", orderInfo.ChannelCode)
logger.Errorf("SubscribeNotice GetChannelInfoByChannelCode err, sub_channel_code is:", orderInfo.ChannelCode)
}
if channelInfo.SubscribeURL != "" {
for i := 0; i < 3; i++ {
resp, err := models.NoticeSubChannel(channelInfo.SubscribeURL, orderInfo.OrderSerial,
orderInfo.ChannelTradeNo, "1", channelInfo.MainChannelCode, channelInfo.Remarks)
if err != nil {
fmt.Println("NoticeSubChannel err:", err)
fmt.Println("i is:", i)
continue
}
if resp != "success" {
continue
} else {
break
}
} }
} else { if channelInfo.SubscribeURL != "" {
fmt.Println("SubscribeNotice SubscribeURL is null, sub_channel_code is:", orderInfo.ChannelCode) for i := 0; i < 3; i++ {
logger.Error("SubscribeNotice SubscribeURL is null, sub_channel_code is:", orderInfo.ChannelCode) resp, err := models.NoticeSubChannel(channelInfo.SubscribeURL, logInfo.OutTradeNo,
logInfo.ChannelTradeNo, "-1", channelInfo.MainChannelCode, channelInfo.Remarks,
rsCode, rsMsg)
if err != nil {
fmt.Println("NoticeSubChannel err:", err)
fmt.Println("i is:", i)
continue
}
if resp != "success" {
continue
} else {
break
}
}
} else {
fmt.Println("SubscribeNotice SubscribeURL is null, sub_channel_code is:", logInfo.ChannelCode)
logger.Error("SubscribeNotice SubscribeURL is null, sub_channel_code is:", logInfo.ChannelCode)
}
} }
}
var req models.QueryRightsInfoReq fmt.Println("订购失败通知-end")
req.AppChannelList = append(req.AppChannelList, models.ChannelCode) logger.Info("订购失败通知-end")
req.Mobile = checkPhoneNum
resp, err := models.MiGuQueryRightsInfo(&req)
if err != nil {
fmt.Println("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
logger.Errorf("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
} }
fmt.Println("CheckOrderState MiGuQueryRightsInfo resp:", resp)
fmt.Println("SubscribeNotice-end") fmt.Println("SubscribeNotice-end")
logger.Info("订购成功通知-end") logger.Info("订购通知-end")
app.MiGuNoticeOK(c, "success") app.MiGuNoticeOK(c, "success")
return return
} }
@ -255,7 +326,8 @@ func (e MiGuDeployService) UnsubscribeNotice(c *gin.Context) {
if channelInfo.SubscribeURL != "" { if channelInfo.SubscribeURL != "" {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
resp, err := models.NoticeSubChannel(channelInfo.SubscribeURL, orderInfo.OrderSerial, resp, err := models.NoticeSubChannel(channelInfo.SubscribeURL, orderInfo.OrderSerial,
orderInfo.ChannelTradeNo, "2", channelInfo.MainChannelCode, channelInfo.Remarks) orderInfo.ChannelTradeNo, "2", channelInfo.MainChannelCode, channelInfo.Remarks,
"", "")
if err != nil { if err != nil {
continue continue
} }

View File

@ -707,7 +707,7 @@ func MiGuQueryRightsInfo(r *QueryRightsInfoReq) (QueryRightsInfoResp, error) {
} }
// NoticeSubChannel 回调通知接口 (GET 请求返回string类型响应头为text/plain) // NoticeSubChannel 回调通知接口 (GET 请求返回string类型响应头为text/plain)
func NoticeSubChannel(baseUrl, linkId, extData, status, mainChannelCode, remarks string) (string, error) { func NoticeSubChannel(baseUrl, linkId, extData, status, mainChannelCode, remarks, rsCode, rsMsg string) (string, error) {
// 构建 GET 请求的 URL // 构建 GET 请求的 URL
requestUrl, err := url.Parse(baseUrl) requestUrl, err := url.Parse(baseUrl)
if err != nil { if err != nil {
@ -719,6 +719,8 @@ func NoticeSubChannel(baseUrl, linkId, extData, status, mainChannelCode, remarks
query.Set("orderId", linkId) query.Set("orderId", linkId)
query.Set("extData", extData) query.Set("extData", extData)
query.Set("status", status) query.Set("status", status)
query.Set("rsCode", rsCode)
query.Set("rsMsg", rsMsg)
if strings.Contains(remarks, AddRemark) { if strings.Contains(remarks, AddRemark) {
query.Set("channel", mainChannelCode) query.Set("channel", mainChannelCode)
@ -912,6 +914,7 @@ func CheckOrderState() {
err := database.Db.Where("is_one_hour_cancel != 1"). err := database.Db.Where("is_one_hour_cancel != 1").
Where("created_at >= ?", oneHourAgo). Where("created_at >= ?", oneHourAgo).
Where("product_id = 1"). // 只查询产品1的情况
Order("created_at desc"). Order("created_at desc").
Find(&orderList).Error Find(&orderList).Error

View File

@ -9,6 +9,7 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gorm" "gorm.io/gorm"
"log" "log"
"net/url"
"runtime" "runtime"
"strconv" "strconv"
) )
@ -81,3 +82,19 @@ func GetOrm(c *gin.Context) (*gorm.DB, error) {
return nil, errors.New(fmt.Sprintf("msgID[%s], db connect not exist", msgID)) return nil, errors.New(fmt.Sprintf("msgID[%s], db connect not exist", msgID))
} }
} }
// DecodeURL 自动多次 URL 解码,直到结果不再变化
func DecodeURL(input string) (string, error) {
prev := ""
current := input
var err error
for current != prev {
prev = current
current, err = url.QueryUnescape(prev)
if err != nil {
return "", err
}
}
return current, nil
}