package biz import ( "context" "errors" "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" "time" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" "voucher/internal/pkg/lock" ) func (v *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, req *bo.WechatVoucherNotifyBo) error { c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{tag, req.PlainText.StockID, req.PlainText.CouponID}) return lock.NewMutex(v.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error { order, err := v.OrderRepo.GetByVoucherNo(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { return err } if req.PlainText.Status.IsSended() { return v.available(ctx, order) } else if req.PlainText.Status.IsUsed() { if err = v.used(ctx, order); err != nil { return err } return v.useLog(ctx, order, req) } else if req.PlainText.Status.IsExpired() { return v.expired(ctx, order) } return fmt.Errorf("未知通知类型:%s", req.PlainText.Status.GetText()) }) } func (v *VoucherBiz) useLog(ctx context.Context, order *bo.OrderBo, req *bo.WechatVoucherNotifyBo) error { if req.PlainText.ConsumeInformation.ConsumeTime == "" { return nil } if req.PlainText.ConsumeInformation.ConsumeAmount > 0 { if err := v.createUseLog(ctx, order, req); err != nil { return err } } _ = v.wxToBBUse(ctx, order, req) return nil } func (v *VoucherBiz) createUseLog(ctx context.Context, order *bo.OrderBo, req *bo.WechatVoucherNotifyBo) error { useTime, err2 := time.Parse(time.RFC3339, req.PlainText.ConsumeInformation.ConsumeTime) if err2 != nil { return err2 } useLog, err2 := v.UseLogRepo.GetByUseTimeOrder(ctx, order.OrderNo, &useTime) if err2 != nil && !errors.Is(err2, gorm.ErrRecordNotFound) { return err2 } if useLog == nil { _, err := v.UseLogRepo.Create(ctx, &bo.UseLogBo{ OrderNo: order.OrderNo, Amount: req.PlainText.ConsumeInformation.ConsumeAmount, Type: vo.UseLogTypeUsed, OperateTime: &useTime, }) if err != nil { return err } } return nil } func (v *VoucherBiz) used(ctx context.Context, order *bo.OrderBo) error { if order.Status.IsUse() { log.Warnf("券状态已是已使用,忽略不处理,orderNo:%s", order.OrderNo) return nil } if err := v.OrderRepo.Used(ctx, order.ID); err != nil { return err } return v.notify(ctx, order) } func (v *VoucherBiz) available(ctx context.Context, order *bo.OrderBo) error { if order.Status.IsSuccess() { log.Warnf("券状态已是可使用,忽略不处理,orderNo:%s", order.OrderNo) return nil } if err := v.OrderRepo.Available(ctx, order.ID); err != nil { return err } return v.notify(ctx, order) } func (v *VoucherBiz) expired(ctx context.Context, order *bo.OrderBo) error { if order.Status.IsExpired() { log.Warnf("券状态已是已过期,忽略不处理,orderNo:%s", order.OrderNo) return nil } if err := v.OrderRepo.Expired(ctx, order.ID); err != nil { return err } return v.notify(ctx, order) } func (v *VoucherBiz) notify(ctx context.Context, order *bo.OrderBo) error { if order.Type.IsCmb() { return v.cmbNotify(ctx, order.ID) } return fmt.Errorf("未知渠道订单类型:%s", order.Type.GetText()) } func (v *VoucherBiz) cmbNotify(ctx context.Context, orderId uint64) error { order, err := v.OrderRepo.GetByID(ctx, orderId) if err != nil { return err } if orderNotify, err2 := v.Cmb.Notify(ctx, order); err2 != nil { if !errPb.IsNeedRetryNotify(err2) { return err2 } // 第一次通知失败重试入队 // 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟 return v.PushNotifyRetryDelayMQ(ctx, 60, orderNotify.ID) } return nil }