voucher/internal/biz/wechat_notify.go

143 lines
3.7 KiB
Go

package biz
import (
"context"
"errors"
"fmt"
"gorm.io/gorm"
errPb "voucher/api/err"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
"voucher/internal/pkg/lock"
)
func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req *bo.WechatVoucherNotifyBo) error {
if req.PlainText.StockCreatorMchid == "" || req.PlainText.StockID == "" || req.PlainText.CouponID == "" {
return fmt.Errorf("回调必要信息不能为空")
}
// 商品数据量较少,先查询商品是否存在,过滤多余的通知信息
_, err := this.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return fmt.Errorf("商品查询错误 error: %w", err)
}
c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID})
return lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
order, err := this.getOrder(ctx, req)
if err != nil {
return err
}
if order.ActivityId != "" {
if err = req.Validate(); err != nil {
return fmt.Errorf("multi validate req error: %v", err)
}
return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order)
}
if req.PlainText.Status.IsSended() {
return this.available(ctx, order)
} else if req.PlainText.Status.IsUsed() {
return this.notifyUsed(ctx, order, req)
} else if req.PlainText.Status.IsExpired() {
return this.expired(ctx, order)
}
return fmt.Errorf("未知通知类型:%s", req.PlainText.Status.GetText())
})
}
func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) {
order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID)
if err != nil {
return nil, fmt.Errorf("订单查询错误 error: %w", err)
}
return order, nil
}
func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req *bo.WechatVoucherNotifyBo) error {
if order.Status.IsUse() {
// 券状态已是已使用,忽略不处理
return nil
}
if err := this.OrderRepo.NotifyUsed(ctx, order.ID, req.PlainText.ConsumeInformation.TransactionID); err != nil {
return err
}
return this.notify(ctx, order)
}
func (this *VoucherBiz) available(ctx context.Context, order *bo.OrderBo) error {
if order.Status.IsSuccess() {
// 券状态已是可使用,忽略不处理
return nil
}
if err := this.OrderRepo.Available(ctx, order.ID); err != nil {
return err
}
return this.notify(ctx, order)
}
func (this *VoucherBiz) expired(ctx context.Context, order *bo.OrderBo) error {
if order.Status.IsExpired() {
// 券状态已是已过期,忽略不处理
return nil
}
if err := this.OrderRepo.Expired(ctx, order.ID); err != nil {
return err
}
return this.notify(ctx, order)
}
func (this *VoucherBiz) notify(ctx context.Context, order *bo.OrderBo) error {
if order.ActivityId != "" {
return nil // 多笔立减活动,不做通知(?不知道有没有核销通知,多笔核销情况未知)
}
return this.cmbNotify(ctx, order.ID)
}
func (this *VoucherBiz) cmbNotify(ctx context.Context, orderId uint64) error {
order, err := this.OrderRepo.GetByID(ctx, orderId)
if err != nil {
return err
}
if orderNotify, err2 := this.Cmb.Notify(ctx, order); err != nil {
if !errPb.IsNeedRetryNotify(err2) {
return err2
}
// 第一次通知失败重试入队
// 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟
return this.PushNotifyRetryDelayMQ(ctx, 60, orderNotify.ID)
}
return nil
}