cmb
This commit is contained in:
parent
982e1c3877
commit
4350b11ec6
|
|
@ -23,3 +23,9 @@ enum Err {
|
||||||
// 通用错误
|
// 通用错误
|
||||||
COMMON = 4 [(errors.code) = 555];
|
COMMON = 4 [(errors.code) = 555];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum NotifyConsumeErr{
|
||||||
|
option (errors.default_code) = 1;
|
||||||
|
// 需要重试通知错误
|
||||||
|
NeedRetryNotify = 0 [(errors.code) = 500];
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,12 @@ rocketMQ:
|
||||||
isOpenConsumer: false #是否启动消费 true/false
|
isOpenConsumer: false #是否启动消费 true/false
|
||||||
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||||
RetryCnt: 3 #重试次数,不配置默认38
|
RetryCnt: 3 #重试次数,不配置默认38
|
||||||
|
notifyRetry: # 重试延迟队列
|
||||||
|
topic: voucher_order_notifyRetry
|
||||||
|
group: voucher_order_notifyRetry_group
|
||||||
|
isOpenConsumer: false #是否启动消费 true/false
|
||||||
|
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||||
|
RetryCnt: 3 #重试次数,不配置默认38
|
||||||
|
|
||||||
wechatNotifyMQ:
|
wechatNotifyMQ:
|
||||||
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ rocketMQ:
|
||||||
isOpenConsumer: false #是否启动消费 true/false
|
isOpenConsumer: false #是否启动消费 true/false
|
||||||
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||||
RetryCnt: 3 #重试次数,不配置默认38
|
RetryCnt: 3 #重试次数,不配置默认38
|
||||||
|
notifyRetry: # 重试延迟队列
|
||||||
|
topic: voucher_order_notifyRetry
|
||||||
|
group: voucher_order_notifyRetry_group
|
||||||
|
isOpenConsumer: false #是否启动消费 true/false
|
||||||
|
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||||
|
RetryCnt: 3 #重试次数,不配置默认38
|
||||||
|
|
||||||
wechatNotifyMQ:
|
wechatNotifyMQ:
|
||||||
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ type OrderNotifyBo struct {
|
||||||
OutRequestNo string
|
OutRequestNo string
|
||||||
Status vo.OrderNotifyStatus
|
Status vo.OrderNotifyStatus
|
||||||
Request string
|
Request string
|
||||||
|
Event vo.OrderNotifyEvent
|
||||||
|
Channel vo.Channel
|
||||||
|
Type vo.OrderType
|
||||||
Responses string
|
Responses string
|
||||||
Remark string
|
Remark string
|
||||||
NotifyUrl string
|
NotifyUrl string
|
||||||
|
|
|
||||||
|
|
@ -54,18 +54,18 @@ func (v *VoucherBiz) CmbQuery(ctx context.Context, orderNo string) (reps *v1.Cmb
|
||||||
|
|
||||||
err = lock.NewMutex(v.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
err = lock.NewMutex(v.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||||
|
|
||||||
orderWechat, err := v.OrderWechatRepo.GetLastByOrderNo(ctx, orderNo)
|
order, err := v.OrderRepo.GetByOrderNo(ctx, orderNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := orderWechat.Status.GetCmbStatusText()
|
status, err := order.Status.GetCmbStatusText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
reps = &v1.CmbQueryReply{
|
reps = &v1.CmbQueryReply{
|
||||||
Ticket: orderWechat.OrderNo,
|
Ticket: order.OrderNo,
|
||||||
Status: status.GetValue(),
|
Status: status.GetValue(),
|
||||||
TransDate: time.Now().Format("20060102150405"),
|
TransDate: time.Now().Format("20060102150405"),
|
||||||
OrgNo: v.bc.Cmb.OrgNo,
|
OrgNo: v.bc.Cmb.OrgNo,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package cmb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
err2 "voucher/api/err"
|
||||||
|
v1 "voucher/api/v1"
|
||||||
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Cmb) bizContent(_ context.Context, orderNotify *bo.OrderNotifyBo) (string, error) {
|
||||||
|
|
||||||
|
cmbStatus, err := orderNotify.Event.GetCmbStatusText()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &v1.CmbNotifyRequest{
|
||||||
|
Ticket: orderNotify.OrderNo,
|
||||||
|
Status: cmbStatus.GetValue(),
|
||||||
|
TransDate: time.Now().Format("20060102150405"),
|
||||||
|
OrgNo: v.bc.Cmb.OrgNo,
|
||||||
|
Ext: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
bizJsonBytes, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bizJsonBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Cmb) notifyCreate(ctx context.Context, req *bo.OrderNotifyBo) (*v1.CmbRequest, *bo.OrderNotifyBo, error) {
|
||||||
|
|
||||||
|
bizContent, err := v.bizContent(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := v.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{
|
||||||
|
FuncName: vo.CmbNotifyFuncName,
|
||||||
|
BizContent: bizContent,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBytes, err := json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Request = string(requestBytes)
|
||||||
|
|
||||||
|
orderNotify, err := v.OrderNotifyRepo.Create(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return request, orderNotify, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Cmb) notifySuccess(ctx context.Context, notifyId uint64, bizStr string) error {
|
||||||
|
|
||||||
|
return v.OrderNotifyRepo.Success(ctx, notifyId, bizStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Cmb) notifyFail(ctx context.Context, notifyId uint64, errMsg string) error {
|
||||||
|
|
||||||
|
if err := v.OrderNotifyRepo.Fail(ctx, notifyId, errMsg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err2.ErrorNeedRetryNotify(errMsg)
|
||||||
|
}
|
||||||
|
|
@ -3,109 +3,53 @@ package cmb
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/go-kratos/kratos/v2/log"
|
"github.com/go-kratos/kratos/v2/log"
|
||||||
"time"
|
|
||||||
v1 "voucher/api/v1"
|
v1 "voucher/api/v1"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *Cmb) NotifyConsume(ctx context.Context, order *bo.OrderBo, orderOutRequestNo string) error {
|
func (v *Cmb) NotifyConsume(ctx context.Context, order *bo.OrderBo, orderOutRequestNo string) (*bo.OrderNotifyBo, error) {
|
||||||
|
|
||||||
if !order.Channel.IsWeChat() {
|
event, err := order.Status.GetOrderNotifyEvent()
|
||||||
return fmt.Errorf("暂不支持订单%s渠道%s回调通知处理", order.OrderNo, order.Channel.GetText())
|
|
||||||
}
|
|
||||||
|
|
||||||
orderWechat, err := v.orderWechat(ctx, order, orderOutRequestNo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bizContent, err := v.bizContent(ctx, orderWechat)
|
req := &bo.OrderNotifyBo{
|
||||||
if err != nil {
|
OrderNo: order.OrderNo,
|
||||||
return err
|
OutRequestNo: orderOutRequestNo,
|
||||||
}
|
|
||||||
|
|
||||||
request, err := v.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{
|
|
||||||
FuncName: vo.CmbNotifyFuncName,
|
|
||||||
BizContent: bizContent,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
requestBytes, err := json.Marshal(request)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
orderNotify, err := v.OrderNotifyRepo.Create(ctx, &bo.OrderNotifyBo{
|
|
||||||
OrderNo: orderWechat.OrderNo,
|
|
||||||
OutRequestNo: orderWechat.OutRequestNo,
|
|
||||||
Request: string(requestBytes),
|
|
||||||
NotifyUrl: order.NotifyUrl,
|
NotifyUrl: order.NotifyUrl,
|
||||||
})
|
Channel: order.Channel,
|
||||||
if err != nil {
|
Event: event,
|
||||||
return err
|
Type: order.Type,
|
||||||
|
Request: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
x, err := v.CmbMixRepo.Request(ctx, request, v.bc.Cmb.NotifyUrl)
|
request, orderNotify, err := v.notifyCreate(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v.OrderNotifyRepo.Fail(ctx, orderNotify.ID, err.Error())
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
x, err := v.CmbMixRepo.Request(ctx, request, order.NotifyUrl)
|
||||||
|
if err != nil {
|
||||||
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
bizStr, err := v.CmbMixRepo.VerifyResponse(ctx, x)
|
bizStr, err := v.CmbMixRepo.VerifyResponse(ctx, x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("NotifyConsume CmbMixRepo.VerifyResponse error:%s", err.Error())
|
log.Errorf("NotifyConsume CmbMixRepo.VerifyResponse error:%s", err.Error())
|
||||||
return v.OrderNotifyRepo.Fail(ctx, orderNotify.ID, err.Error())
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var s *v1.CmbNotifyReply
|
var reply *v1.CmbNotifyReply
|
||||||
if err = json.Unmarshal([]byte(bizStr), &s); err != nil {
|
if err = json.Unmarshal([]byte(bizStr), &reply); err != nil {
|
||||||
return v.OrderNotifyRepo.Fail(ctx, orderNotify.ID, err.Error())
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.RespCode != vo.CmbResponseStatusSuccess.GetValue() {
|
if reply.RespCode != vo.CmbResponseStatusSuccess.GetValue() {
|
||||||
return v.OrderNotifyRepo.Fail(ctx, orderNotify.ID, s.RespMsg)
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, reply.RespMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.OrderNotifyRepo.Success(ctx, orderNotify.ID, bizStr)
|
return orderNotify, v.notifySuccess(ctx, orderNotify.ID, bizStr)
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Cmb) orderWechat(ctx context.Context, order *bo.OrderBo, orderOutRequestNo string) (*bo.OrderWechatBo, error) {
|
|
||||||
|
|
||||||
orderWechat, err := v.OrderWechatRepo.GetByOutRequestNo(ctx, orderOutRequestNo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("根据订单号%s获取微信订单失败:%s", orderWechat.OrderNo, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !orderWechat.Status.CanNotify() {
|
|
||||||
return nil, fmt.Errorf("微信订单状态错误,不能通知:%s", order.Status.GetText())
|
|
||||||
}
|
|
||||||
|
|
||||||
return orderWechat, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Cmb) bizContent(_ context.Context, orderWechat *bo.OrderWechatBo) (string, error) {
|
|
||||||
|
|
||||||
status, err := orderWechat.Status.GetCmbStatusText()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &v1.CmbNotifyRequest{
|
|
||||||
Ticket: orderWechat.OrderNo,
|
|
||||||
Status: status.GetValue(),
|
|
||||||
TransDate: time.Now().Format("20060102150405"),
|
|
||||||
OrgNo: v.bc.Cmb.OrgNo,
|
|
||||||
Ext: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
bizJsonBytes, err := json.Marshal(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bizJsonBytes), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package cmb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/go-kratos/kratos/v2/log"
|
||||||
|
v1 "voucher/api/v1"
|
||||||
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Cmb) NotifyRetryConsume(ctx context.Context, order *bo.OrderBo, orderNotify *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error) {
|
||||||
|
|
||||||
|
req := &bo.OrderNotifyBo{
|
||||||
|
OrderNo: orderNotify.OrderNo,
|
||||||
|
OutRequestNo: orderNotify.OutRequestNo,
|
||||||
|
NotifyUrl: order.NotifyUrl,
|
||||||
|
Channel: order.Channel,
|
||||||
|
Event: orderNotify.Event,
|
||||||
|
Type: order.Type,
|
||||||
|
Request: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
request, orderNotify, err := v.notifyCreate(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
x, err := v.CmbMixRepo.Request(ctx, request, order.NotifyUrl)
|
||||||
|
if err != nil {
|
||||||
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
bizStr, err := v.CmbMixRepo.VerifyResponse(ctx, x)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("NotifyRetryConsume CmbMixRepo.VerifyResponse error:%s", err.Error())
|
||||||
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply *v1.CmbNotifyReply
|
||||||
|
if err = json.Unmarshal([]byte(bizStr), &reply); err != nil {
|
||||||
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.RespCode != vo.CmbResponseStatusSuccess.GetValue() {
|
||||||
|
return orderNotify, v.notifyFail(ctx, orderNotify.ID, reply.RespMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderNotify, v.notifySuccess(ctx, orderNotify.ID, bizStr)
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ func (v *Cmb) OrderConsume(ctx context.Context, order *bo.OrderBo) (outRequestNo
|
||||||
|
|
||||||
func (v *Cmb) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
|
func (v *Cmb) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
|
||||||
|
|
||||||
c := vo.WechatNotifyRegisterTagCacheKey.BuildRegisterCache(v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID)
|
c := vo.WechatNotifyRegisterTagCacheKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
|
||||||
|
|
||||||
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
|
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ func (v *Cmb) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID
|
||||||
return fmt.Errorf(errMsg)
|
return fmt.Errorf(errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildRegisterCache(v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID)
|
cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
|
||||||
|
|
||||||
return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
||||||
// 二次获取,判定处理,以免获取锁后又执行了一次
|
// 二次获取,判定处理,以免获取锁后又执行了一次
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
errPb "voucher/api/err"
|
||||||
|
"voucher/internal/biz/bo"
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
"voucher/internal/pkg/lock"
|
"voucher/internal/pkg/lock"
|
||||||
"voucher/internal/pkg/mq"
|
"voucher/internal/pkg/mq"
|
||||||
|
|
@ -24,14 +26,18 @@ func (v *VoucherBiz) PushNotifyMQ(ctx context.Context, orderNo, outRequestNo str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VoucherBiz) NotifyConsume(ctx context.Context, orderNo, orderOutRequestNo string) (err error) {
|
func (v *VoucherBiz) NotifyConsume(ctx context.Context, orderNo, orderOutRequestNo string) error {
|
||||||
|
|
||||||
c := vo.NotifyConsume.BuildCache([]string{orderNo})
|
var (
|
||||||
|
err error
|
||||||
|
orderNotify *bo.OrderNotifyBo
|
||||||
|
cache = vo.NotifyConsume.BuildCache([]string{orderNo})
|
||||||
|
)
|
||||||
|
|
||||||
err = lock.NewMutex(v.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
err = lock.NewMutex(v.rdb.Rdb, cache.TTL).Lock(ctx, cache.Key, func(ctx context.Context) error {
|
||||||
|
|
||||||
order, err := v.OrderRepo.GetByOrderNo(ctx, orderNo)
|
order, err2 := v.OrderRepo.GetByOrderNo(ctx, orderNo)
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,12 +45,24 @@ func (v *VoucherBiz) NotifyConsume(ctx context.Context, orderNo, orderOutRequest
|
||||||
return fmt.Errorf("订单状态错误,不能通知:%s", order.Status.GetText())
|
return fmt.Errorf("订单状态错误,不能通知:%s", order.Status.GetText())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !order.Channel.IsWeChat() {
|
||||||
|
return fmt.Errorf("暂不支持订单%s渠道%s回调通知处理", order.OrderNo, order.Channel.GetText())
|
||||||
|
}
|
||||||
|
|
||||||
if order.Type.IsCmb() {
|
if order.Type.IsCmb() {
|
||||||
return v.Cmb.NotifyConsume(ctx, order, orderOutRequestNo)
|
if orderNotify, err2 = v.Cmb.NotifyConsume(ctx, order, orderOutRequestNo); err2 != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("订单类型错误:%s", order.Type.GetText())
|
return fmt.Errorf("订单类型错误:%s", order.Type.GetText())
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
if !errPb.IsNeedRetryNotify(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟
|
||||||
|
// 第一次通知失败重试入队
|
||||||
|
return v.PushNotifyRetryDelayMQ(ctx, 60, orderNotify.ID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
"strconv"
|
||||||
|
errPb "voucher/api/err"
|
||||||
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
|
"voucher/internal/pkg/lock"
|
||||||
|
"voucher/internal/pkg/mq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *VoucherBiz) PushNotifyRetryDelayMQ(ctx context.Context, level int, orderNotifyId uint64) error {
|
||||||
|
|
||||||
|
str := strconv.FormatUint(orderNotifyId, 10)
|
||||||
|
|
||||||
|
eventMap := v.bc.RocketMQ.EventMap["notifyRetry"]
|
||||||
|
sendOption := []mq.SendOption{
|
||||||
|
mq.WithSendShardingKeysOption(str),
|
||||||
|
mq.WithOpenTelemetryOption(trace.SpanFromContext(ctx).SpanContext().TraceID().String()),
|
||||||
|
mq.WithSendDelayLevelOption(level),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.MqSendMixRepo.SendSync(ctx, eventMap.Topic, []byte("{}"), sendOption...); err != nil {
|
||||||
|
return fmt.Errorf("回调通知延迟队列,投递消息出错:err=%s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VoucherBiz) NotifyRetryConsume(ctx context.Context, orderNotifyId uint64) error {
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
orderNotify *bo.OrderNotifyBo
|
||||||
|
cache = vo.NotifyRetryConsume.BuildCacheUint64([]uint64{orderNotifyId})
|
||||||
|
)
|
||||||
|
|
||||||
|
err = lock.NewMutex(v.rdb.Rdb, cache.TTL).Lock(ctx, cache.Key, func(ctx context.Context) error {
|
||||||
|
|
||||||
|
orderNotify, err = v.OrderNotifyRepo.GetByID(ctx, orderNotifyId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err2 := v.OrderRepo.GetByOrderNo(ctx, orderNotify.OrderNo)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
if order.Type.IsCmb() {
|
||||||
|
orderNotify, err2 = v.Cmb.NotifyRetryConsume(ctx, order, orderNotify)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("订单类型错误:%s", order.Type.GetText())
|
||||||
|
})
|
||||||
|
|
||||||
|
if !errPb.IsNeedRetryNotify(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
level, err2 := v.level(ctx, orderNotify)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.PushNotifyRetryDelayMQ(ctx, level, orderNotify.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VoucherBiz) level(ctx context.Context, orderNotify *bo.OrderNotifyBo) (int, error) {
|
||||||
|
// 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟
|
||||||
|
|
||||||
|
count, err := v.OrderNotifyRepo.GetCountByOrderNoAndEvent(ctx, orderNotify.OrderNo, orderNotify.Event)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 1:
|
||||||
|
return 60, nil
|
||||||
|
case 2:
|
||||||
|
return 120, nil
|
||||||
|
case 3:
|
||||||
|
return 720, nil
|
||||||
|
case 4:
|
||||||
|
return 3600, nil
|
||||||
|
case 5:
|
||||||
|
return 21600, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("回调通知失败次数超过5次,不再重试")
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,12 @@ package repo
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OrderNotifyRepo interface {
|
type OrderNotifyRepo interface {
|
||||||
|
GetByID(ctx context.Context, id uint64) (*bo.OrderNotifyBo, error)
|
||||||
|
GetCountByOrderNoAndEvent(ctx context.Context, orderNo string, event vo.OrderNotifyEvent) (int64, error)
|
||||||
Create(ctx context.Context, req *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error)
|
Create(ctx context.Context, req *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error)
|
||||||
Success(ctx context.Context, id uint64, responses string) error
|
Success(ctx context.Context, id uint64, responses string) error
|
||||||
Fail(ctx context.Context, id uint64, remark string) error
|
Fail(ctx context.Context, id uint64, remark string) error
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package vo
|
package vo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
"voucher/internal/pkg/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CacheKey string
|
type CacheKey string
|
||||||
|
|
@ -12,8 +12,9 @@ const (
|
||||||
CmbQueryLockKey CacheKey = "cmb_query"
|
CmbQueryLockKey CacheKey = "cmb_query"
|
||||||
CmbProductQueryLockKey CacheKey = "cmb_product_query"
|
CmbProductQueryLockKey CacheKey = "cmb_product_query"
|
||||||
|
|
||||||
OrderConsume CacheKey = "order_consume"
|
OrderConsume CacheKey = "order_consume"
|
||||||
NotifyConsume CacheKey = "notify_consume"
|
NotifyConsume CacheKey = "notify_consume"
|
||||||
|
NotifyRetryConsume CacheKey = "notify_retry_consume"
|
||||||
|
|
||||||
WechatNotifyRegisterTagCacheKey CacheKey = "wechat_notify_register_tag"
|
WechatNotifyRegisterTagCacheKey CacheKey = "wechat_notify_register_tag"
|
||||||
WechatNotifyRegisterTagCacheLockKey CacheKey = "wechat_notify_register_tag_lock"
|
WechatNotifyRegisterTagCacheLockKey CacheKey = "wechat_notify_register_tag_lock"
|
||||||
|
|
@ -25,8 +26,9 @@ var CacheKeyMap = map[CacheKey]time.Duration{
|
||||||
CmbOrderLockKey: 30 * time.Second,
|
CmbOrderLockKey: 30 * time.Second,
|
||||||
CmbQueryLockKey: 30 * time.Second,
|
CmbQueryLockKey: 30 * time.Second,
|
||||||
CmbProductQueryLockKey: 30 * time.Second,
|
CmbProductQueryLockKey: 30 * time.Second,
|
||||||
OrderConsume: 30 * time.Second,
|
OrderConsume: 60 * time.Second,
|
||||||
NotifyConsume: 30 * time.Second,
|
NotifyConsume: 60 * time.Second,
|
||||||
|
NotifyRetryConsume: 60 * time.Second,
|
||||||
WechatNotifyRegisterTagCacheKey: 86400 * time.Second,
|
WechatNotifyRegisterTagCacheKey: 86400 * time.Second,
|
||||||
WechatNotifyRegisterTagCacheLockKey: 30 * time.Second,
|
WechatNotifyRegisterTagCacheLockKey: 30 * time.Second,
|
||||||
WechatNotifyConsumeLockKey: 30 * time.Second,
|
WechatNotifyConsumeLockKey: 30 * time.Second,
|
||||||
|
|
@ -37,13 +39,13 @@ type Cache struct {
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s CacheKey) BuildCache(ids []string) *Cache {
|
func (s CacheKey) GetValue() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
k := fmt.Sprintf("%s", s)
|
func (s CacheKey) BuildCache(strArr []string) *Cache {
|
||||||
|
|
||||||
for _, id := range ids {
|
k := helper.BuildStr(s.GetValue(), strArr)
|
||||||
k = fmt.Sprintf("%s_%s", k, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &Cache{
|
c := &Cache{
|
||||||
Key: k,
|
Key: k,
|
||||||
|
|
@ -59,8 +61,9 @@ func (s CacheKey) BuildCache(ids []string) *Cache {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s CacheKey) BuildRegisterCache(tag, stockCreatorMchID, stockID string) *Cache {
|
func (s CacheKey) BuildCacheUint64(ids []uint64) *Cache {
|
||||||
k := fmt.Sprintf("%s_%s_%s_%s", s, tag, stockCreatorMchID, stockID)
|
|
||||||
|
k := helper.BuildStr(s.GetValue(), ids)
|
||||||
|
|
||||||
c := &Cache{
|
c := &Cache{
|
||||||
Key: k,
|
Key: k,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package vo
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type OrderNotifyEvent uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
OrderNotifyEventSendDEd OrderNotifyEvent = iota + 1
|
||||||
|
OrderNotifyEventUsed
|
||||||
|
OrderNotifyEventExpired
|
||||||
|
)
|
||||||
|
|
||||||
|
var OrderNotifyEventMap = map[OrderNotifyEvent]string{
|
||||||
|
OrderNotifyEventSendDEd: "可用",
|
||||||
|
OrderNotifyEventUsed: "已实扣",
|
||||||
|
OrderNotifyEventExpired: "已过期",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) GetText() string {
|
||||||
|
if t, ok := OrderNotifyEventMap[s]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return "未知通知事件"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) GetValue() uint8 {
|
||||||
|
return uint8(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) IsSendDEd() bool {
|
||||||
|
return s == OrderNotifyEventSendDEd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) IsUsed() bool {
|
||||||
|
return s == OrderNotifyEventUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) IsExpired() bool {
|
||||||
|
return s == OrderNotifyEventExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
var OrderNotifyEventMapCmbStatus = map[OrderNotifyEvent]CmbStatus{
|
||||||
|
OrderNotifyEventSendDEd: CmbStatusSuccess,
|
||||||
|
OrderNotifyEventUsed: CmbStatusUse,
|
||||||
|
OrderNotifyEventExpired: CmbStatusExpired,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderNotifyEvent) GetCmbStatusText() (CmbStatus, error) {
|
||||||
|
if t, ok := OrderNotifyEventMapCmbStatus[s]; ok {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cmbStatus[%s]未定义", s)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package vo
|
package vo
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type OrderStatus uint8
|
type OrderStatus uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -58,3 +60,29 @@ func (s OrderStatus) IsExpired() bool {
|
||||||
func (s OrderStatus) CanNotify() bool {
|
func (s OrderStatus) CanNotify() bool {
|
||||||
return s.IsSuccess() || s.IsUse() || s.IsExpired()
|
return s.IsSuccess() || s.IsUse() || s.IsExpired()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var OrderStatusMapOrderNotifyEvent = map[OrderStatus]OrderNotifyEvent{
|
||||||
|
OrderStatusSuccess: OrderNotifyEventSendDEd,
|
||||||
|
OrderStatusUse: OrderNotifyEventUsed,
|
||||||
|
OrderStatusExpired: OrderNotifyEventExpired,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderStatus) GetOrderNotifyEvent() (OrderNotifyEvent, error) {
|
||||||
|
if t, ok := OrderStatusMapOrderNotifyEvent[s]; ok {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CmbStatus[%s]未定义", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var OrderStatusMapCmbStatus = map[OrderStatus]CmbStatus{
|
||||||
|
OrderStatusSuccess: CmbStatusSuccess,
|
||||||
|
OrderStatusUse: CmbStatusUse,
|
||||||
|
OrderStatusExpired: CmbStatusExpired,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OrderStatus) GetCmbStatusText() (CmbStatus, error) {
|
||||||
|
if t, ok := OrderStatusMapCmbStatus[s]; ok {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cmbStatus[%s]未定义", s)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type OrderWechatStatus uint8
|
type OrderWechatStatus uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -48,19 +46,6 @@ var OrderWechatStatusMap = map[OrderWechatStatus]string{
|
||||||
OrderWechatStatusExpired: "已过期",
|
OrderWechatStatusExpired: "已过期",
|
||||||
}
|
}
|
||||||
|
|
||||||
var OrderStatusMapCmbStatus = map[OrderWechatStatus]CmbStatus{
|
|
||||||
OrderWechatStatusSuccess: CmbStatusSuccess,
|
|
||||||
OrderWechatStatusUse: CmbStatusUse,
|
|
||||||
OrderWechatStatusExpired: CmbStatusExpired,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OrderWechatStatus) GetCmbStatusText() (CmbStatus, error) {
|
|
||||||
if t, ok := OrderStatusMapCmbStatus[s]; ok {
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("CmbStatus[%s]未定义", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OrderWechatStatus) GetText() string {
|
func (s OrderWechatStatus) GetText() string {
|
||||||
if t, ok := OrderWechatStatusMap[s]; ok {
|
if t, ok := OrderWechatStatusMap[s]; ok {
|
||||||
return t
|
return t
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type VoucherBiz struct {
|
||||||
ProductRepo repo.ProductRepo
|
ProductRepo repo.ProductRepo
|
||||||
OrderRepo repo.OrderRepo
|
OrderRepo repo.OrderRepo
|
||||||
OrderWechatRepo repo.OrderWechatRepo
|
OrderWechatRepo repo.OrderWechatRepo
|
||||||
|
OrderNotifyRepo repo.OrderNotifyRepo
|
||||||
MqSendMixRepo mixrepos.MQSendMixRepo
|
MqSendMixRepo mixrepos.MQSendMixRepo
|
||||||
WechatCpnRepo wechatrepo.WechatCpnRepo
|
WechatCpnRepo wechatrepo.WechatCpnRepo
|
||||||
}
|
}
|
||||||
|
|
@ -27,6 +28,7 @@ func NewVoucherBiz(
|
||||||
ProductRepo repo.ProductRepo,
|
ProductRepo repo.ProductRepo,
|
||||||
OrderRepo repo.OrderRepo,
|
OrderRepo repo.OrderRepo,
|
||||||
OrderWechatRepo repo.OrderWechatRepo,
|
OrderWechatRepo repo.OrderWechatRepo,
|
||||||
|
OrderNotifyRepo repo.OrderNotifyRepo,
|
||||||
MqSendMixRepo mixrepos.MQSendMixRepo,
|
MqSendMixRepo mixrepos.MQSendMixRepo,
|
||||||
WechatCpnRepo wechatrepo.WechatCpnRepo,
|
WechatCpnRepo wechatrepo.WechatCpnRepo,
|
||||||
) *VoucherBiz {
|
) *VoucherBiz {
|
||||||
|
|
@ -37,6 +39,7 @@ func NewVoucherBiz(
|
||||||
ProductRepo: ProductRepo,
|
ProductRepo: ProductRepo,
|
||||||
OrderRepo: OrderRepo,
|
OrderRepo: OrderRepo,
|
||||||
OrderWechatRepo: OrderWechatRepo,
|
OrderWechatRepo: OrderWechatRepo,
|
||||||
|
OrderNotifyRepo: OrderNotifyRepo,
|
||||||
MqSendMixRepo: MqSendMixRepo,
|
MqSendMixRepo: MqSendMixRepo,
|
||||||
WechatCpnRepo: WechatCpnRepo,
|
WechatCpnRepo: WechatCpnRepo,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,7 @@ func (s *CmbMixRepoImpl) Request(ctx context.Context, req *v1.CmbRequest, uri st
|
||||||
|
|
||||||
var response *v1.CmbReply
|
var response *v1.CmbReply
|
||||||
if err = json.Unmarshal(bodyBytes, &response); err != nil {
|
if err = json.Unmarshal(bodyBytes, &response); err != nil {
|
||||||
|
log.Errorf("请求掌上生活返回数据解析报错,bodyBytes=%s,err=%s", string(bodyBytes), err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ type OrderNotify struct {
|
||||||
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
||||||
OutRequestNo string `gorm:"column:out_request_no;not null" json:"out_request_no"`
|
OutRequestNo string `gorm:"column:out_request_no;not null" json:"out_request_no"`
|
||||||
Status uint8 `gorm:"column:status;not null;comment:状态" json:"status"`
|
Status uint8 `gorm:"column:status;not null;comment:状态" json:"status"`
|
||||||
|
Event uint8 `gorm:"column:event;not null;comment:event" json:"event"`
|
||||||
|
Channel uint8 `gorm:"column:channel;not null;comment:channel" json:"channel"`
|
||||||
|
Type uint8 `gorm:"column:type;not null;comment:1:招行" json:"type"`
|
||||||
Request string `gorm:"column:request;not null" json:"request"`
|
Request string `gorm:"column:request;not null" json:"request"`
|
||||||
Responses string `gorm:"column:responses" json:"responses"`
|
Responses string `gorm:"column:responses" json:"responses"`
|
||||||
Remark string `gorm:"column:remark" json:"remark"`
|
Remark string `gorm:"column:remark" json:"remark"`
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,33 @@ func (p *OrderNotifyRepoImpl) DB(ctx context.Context) *gorm.DB {
|
||||||
return p.db.DB(ctx).Model(model.OrderNotify{})
|
return p.db.DB(ctx).Model(model.OrderNotify{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *OrderNotifyRepoImpl) GetByID(ctx context.Context, id uint64) (*bo.OrderNotifyBo, error) {
|
||||||
|
info := &model.OrderNotify{}
|
||||||
|
|
||||||
|
tx := p.DB(ctx).Where(model.OrderNotify{ID: id}).Find(&info)
|
||||||
|
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, tx.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.RowsAffected == 0 {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.ToBo(info), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderNotifyRepoImpl) GetCountByOrderNoAndEvent(ctx context.Context, orderNo string, event vo.OrderNotifyEvent) (int64, error) {
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
tx := p.DB(ctx).Where(model.OrderNotify{OrderNo: orderNo, Event: event.GetValue()}).Count(&total)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return 0, tx.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *OrderNotifyRepoImpl) Create(ctx context.Context, req *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error) {
|
func (p *OrderNotifyRepoImpl) Create(ctx context.Context, req *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
|
@ -37,6 +64,9 @@ func (p *OrderNotifyRepoImpl) Create(ctx context.Context, req *bo.OrderNotifyBo)
|
||||||
Request: req.Request,
|
Request: req.Request,
|
||||||
Responses: "{}",
|
Responses: "{}",
|
||||||
NotifyUrl: req.NotifyUrl,
|
NotifyUrl: req.NotifyUrl,
|
||||||
|
Channel: req.Channel.GetValue(),
|
||||||
|
Event: req.Event.GetValue(),
|
||||||
|
Type: req.Type.GetValue(),
|
||||||
CreateTime: &now,
|
CreateTime: &now,
|
||||||
UpdateTime: &now,
|
UpdateTime: &now,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
package helper
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
func ArrDeduplication[T int | int32 | uint32 | uint64 | int64 | int8 | string](arr []T) []T {
|
func ArrDeduplication[T int | int32 | uint32 | uint64 | int64 | int8 | string](arr []T) []T {
|
||||||
seen := map[T]bool{}
|
seen := map[T]bool{}
|
||||||
var result []T
|
var result []T
|
||||||
|
|
@ -11,3 +16,35 @@ func ArrDeduplication[T int | int32 | uint32 | uint64 | int64 | int8 | string](a
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildStr 函数用于将 uid 和 arr 中的元素拼接成一个字符串
|
||||||
|
func BuildStr[T int | int32 | uint32 | uint64 | int64 | int8 | string](uid string, arr []T) string {
|
||||||
|
// 创建一个 strings.Builder 实例,用于高效地构建字符串
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
// 写入初始的 uid
|
||||||
|
sb.WriteString(uid)
|
||||||
|
|
||||||
|
// 遍历 arr 切片中的每个元素
|
||||||
|
for i, id := range arr {
|
||||||
|
// 如果不是第一个元素,先写入下划线分隔符
|
||||||
|
if i > 0 {
|
||||||
|
sb.WriteByte('_')
|
||||||
|
} else if sb.Len() > 0 {
|
||||||
|
// 如果 uid 不为空且是第一个元素,也写入下划线分隔符
|
||||||
|
sb.WriteByte('_')
|
||||||
|
}
|
||||||
|
// 根据元素的类型进行不同的处理
|
||||||
|
switch v := any(id).(type) {
|
||||||
|
case string:
|
||||||
|
// 如果是字符串类型,直接写入
|
||||||
|
sb.WriteString(v)
|
||||||
|
default:
|
||||||
|
// 对于其他类型,使用 fmt.Sprint 转换为字符串后写入
|
||||||
|
sb.WriteString(fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 strings.Builder 中的内容转换为字符串并返回
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildStr(t *testing.T) {
|
||||||
|
uid := "example_uid"
|
||||||
|
arr := []int{1, 2, 3}
|
||||||
|
result := BuildStr(uid, arr)
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
arrStr := []string{"a", "b", "c"}
|
||||||
|
resultStr := BuildStr(uid, arrStr)
|
||||||
|
fmt.Println(resultStr)
|
||||||
|
}
|
||||||
|
|
@ -44,6 +44,12 @@ func NewConsumer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c := voucherService.GetNotifyRetryConfig(); c != nil {
|
||||||
|
if err := manager.Subscribe(context.Background(), cf, c, voucherService.NotifyRetryConsumer); err != nil {
|
||||||
|
panic(fmt.Sprintf("订阅失败: topicName:%s, groupName:%s, err:%v", c.TopicName, c.GroupName, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &Consumer{manager: manager, hLog: hLog, conf: conf}
|
return &Consumer{manager: manager, hLog: hLog, conf: conf}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/go-kratos/kratos/v2/log"
|
"github.com/go-kratos/kratos/v2/log"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"voucher/internal/pkg/mq"
|
"voucher/internal/pkg/mq"
|
||||||
)
|
)
|
||||||
|
|
@ -63,7 +64,7 @@ func (j *VoucherService) NotifyConsumer(ctx context.Context, msg *mq.ConsumerMes
|
||||||
|
|
||||||
shardingKey := msg.GetShardingKey()
|
shardingKey := msg.GetShardingKey()
|
||||||
if shardingKey == "" {
|
if shardingKey == "" {
|
||||||
log.Error("orderNotify 消费异常,获取 shardingKey 失败")
|
log.Error("notify 消费异常,获取 shardingKey 失败")
|
||||||
return errors.New("orderNotify 消费异常,获取 orderNo 失败")
|
return errors.New("orderNotify 消费异常,获取 orderNo 失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,3 +76,42 @@ func (j *VoucherService) NotifyConsumer(ctx context.Context, msg *mq.ConsumerMes
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *VoucherService) GetNotifyRetryConfig() *mq.ConsumerConfig {
|
||||||
|
elm, ok := j.bc.RocketMQ.EventMap["notifyRetry"]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !elm.IsOpenConsumer {
|
||||||
|
log.Warnf("notify MQ is not open")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mq.ConsumerConfig{
|
||||||
|
TopicName: elm.Topic,
|
||||||
|
GroupName: elm.Group,
|
||||||
|
PerCoroutineCnt: int(elm.PerCoroutineCnt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *VoucherService) NotifyRetryConsumer(ctx context.Context, msg *mq.ConsumerMessage) error {
|
||||||
|
|
||||||
|
shardingKey := msg.GetShardingKey()
|
||||||
|
if shardingKey == "" {
|
||||||
|
log.Error("notify retry 消费异常,获取 shardingKey 失败")
|
||||||
|
return errors.New("orderNotify 消费异常,获取 orderNo 失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
orderNotifyId, err := strconv.ParseUint(shardingKey, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("notify retry 消费异常,orderNotifyId转换失败,shardingKey=%s", shardingKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = j.VoucherBiz.NotifyRetryConsume(ctx, orderNotifyId); err != nil {
|
||||||
|
log.Errorf("notify retry 消费异常,shardingKey:%s,error: %s", shardingKey, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue