voucher/internal/biz/order.go

283 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package biz
import (
"context"
"fmt"
"github.com/go-kratos/kratos/v2/log"
"github.com/redis/go-redis/v9"
err2 "voucher/api/err"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
"voucher/internal/pkg/lock"
)
func (v *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
order, err := v.create(ctx, req, product)
if err != nil {
return nil, err
}
// 注册通知标签 order.MerchantNo 批次创建商户, order.BatchNo 商品批次号
if err = v.registerNotifyTag(ctx, order.MerchantNo, order.BatchNo); err != nil {
return nil, err
}
// 真实发放
voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
if err != nil {
if err3 := v.fail(ctx, order, err.Error()); err3 != nil {
return nil, err3
}
return nil, err
}
return order, v.success(ctx, order, voucherNo)
}
func (v *VoucherBiz) orderRetry(ctx context.Context, order *bo.OrderBo) error {
voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
if err != nil {
if err3 := v.fail(ctx, order, err.Error()); err3 != nil {
return err3
}
return err
}
return v.success(ctx, order, voucherNo)
}
func (v *VoucherBiz) create(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
o := &bo.OrderBo{
OrderNo: v.GenerateMixRepo.GeneratorString(ctx, fmt.Sprintf("%d%s", req.Type, req.OutBizNo)),
OutBizNo: req.OutBizNo,
ProductNo: req.ProductNo,
Account: req.Account,
AppID: req.AppID,
MerchantNo: product.MchId,
Channel: product.Channel,
BatchNo: product.BatchNo,
NotifyUrl: req.NotifyUrl,
AccountType: vo.OrderAccountTypeOpenId,
Type: req.Type,
Status: vo.OrderStatusIng, // 同步发放,状态至为发放中
Attach: req.Attach,
}
return v.OrderRepo.Create(ctx, o)
}
func (v *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
c := vo.WechatNotifyRegisterTagCacheKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err == nil {
// 缓存存在,直接返回
return nil
}
if err != redis.Nil {
return fmt.Errorf(fmt.Sprintf("获取redis缓存%s异常:%v", c.Key, err))
}
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 {
// 二次获取,判定处理,以免获取锁后又执行了一次
cacheValue, err3 := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err3 != nil && err3 != redis.Nil {
return fmt.Errorf(fmt.Sprintf("二次获取redis缓存%s异常:%v", c.Key, err))
}
if cacheValue != "" {
return nil // 有直接返回
}
wechatNotifyTag, err3 := v.WechatNotifyRegisterTagRepo.GetByStockIdAndMchId(ctx, stockCreatorMchID, stockID)
if err3 != nil && !err2.IsDbNotFound(err3) {
return err3
}
if wechatNotifyTag != nil {
if wechatNotifyTag.Tag != v.bc.WechatNotifyMQ.Tag {
return fmt.Errorf("tag不一致请检查tag配置:%s", wechatNotifyTag.Tag)
}
return v.setCache(ctx, c, wechatNotifyTag)
}
wechatNotifyTag, err3 = v.createWechatNotifyRegisterTag(ctx, stockCreatorMchID, stockID)
if err3 != nil {
return err3
}
if err = v.WechatCpnRepo.RegisterNotifyTag(ctx, stockID); err != nil {
return v.WechatNotifyRegisterTagRepo.Fail(ctx, wechatNotifyTag.ID, err.Error())
}
if err = v.WechatNotifyRegisterTagRepo.Success(ctx, wechatNotifyTag.ID); err != nil {
return err
}
return v.setCache(ctx, c, wechatNotifyTag)
})
}
func (v *VoucherBiz) createWechatNotifyRegisterTag(ctx context.Context, stockCreatorMchID, stockID string) (*bo.WechatNotifyRegisterTagBo, error) {
return v.WechatNotifyRegisterTagRepo.Create(ctx, &bo.WechatNotifyRegisterTagBo{
StockID: stockID,
StockCreatorMchID: stockCreatorMchID,
Tag: v.bc.WechatNotifyMQ.Tag,
})
}
func (v *VoucherBiz) setCache(ctx context.Context, c *vo.Cache, wechatNotifyTag *bo.WechatNotifyRegisterTagBo) error {
if err := v.rdb.Rdb.Set(ctx, c.Key, wechatNotifyTag.Tag, c.TTL).Err(); err != nil {
return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err))
}
return nil
}
func (v *VoucherBiz) ing(ctx context.Context, id uint64) error {
return v.OrderRepo.Ing(ctx, id)
}
func (v *VoucherBiz) success(ctx context.Context, order *bo.OrderBo, voucherNo string) error {
return v.OrderRepo.Success(ctx, order.ID, voucherNo)
}
func (v *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, remark string) error {
if err := v.OrderRepo.Fail(ctx, order.ID, remark); err != nil {
return err
}
return v.alarm(ctx, order, remark)
}
func (v *VoucherBiz) alarm(ctx context.Context, order *bo.OrderBo, errMsg string) error {
// 1小时 内 指定的批次号 发放 发生错误 预警
c := vo.OrderConsumeFailAlarmKey.BuildCache([]string{order.ProductNo})
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err == nil {
// 缓存存在,直接返回
return nil
}
if err != redis.Nil {
return fmt.Errorf(fmt.Sprintf("alarm 获取redis缓存%s异常:%v", c.Key, err))
}
cl := vo.OrderConsumeFailAlarmLockKey.BuildCache([]string{order.ProductNo})
return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
// 二次获取,判定处理,以免获取锁后又执行了一次
cacheValue, err3 := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err3 != nil && err3 != redis.Nil {
return fmt.Errorf(fmt.Sprintf("alarm 二次获取redis缓存%s异常:%v", c.Key, err))
}
if len(cacheValue) > 0 {
return nil // 有直接返回
}
// 通知
if err = v.DingMixRepo.SendMarkdownMessage(ctx, "异常通知", v.alarmText(ctx, order, errMsg)); err != nil {
return err
}
if err = v.rdb.Rdb.Set(ctx, c.Key, order.ProductNo, c.TTL).Err(); err != nil {
return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err))
}
return nil
})
}
func (v *VoucherBiz) alarmText(_ context.Context, order *bo.OrderBo, errMsg string) string {
remarks := fmt.Sprintf("订单号:%s商品编号:%s,原因:%s", order.OrderNo, order.ProductNo, errMsg)
msg := "# <font color='green'>" +
"<h1>立减金发放平台报警通知</h1>" +
"</font> \n" +
"<font color='black'>" +
"不好了,订单发放发生异常了" +
"[<font color='red'>%s</font>]请尽快处理@相关人员。" +
"</font>"
return fmt.Sprintf(msg, remarks)
}
func (v *VoucherBiz) Query(ctx context.Context, order *bo.OrderBo) error {
status, err := v.WechatCpnRepo.Query(ctx, order)
if err != nil {
return err
}
if order.Status == status {
log.Warnf("券状态未改变:%s忽略不处理,orderNo:%s", order.Status.GetText(), order.OrderNo)
return nil
}
if err = v.UpdateOrderStatus(ctx, order.ID, status); err != nil {
return err
}
order.Status = status
return nil
}
func (v *VoucherBiz) UpdateOrderStatus(ctx context.Context, orderId uint64, status vo.OrderStatus) error {
if status.IsSuccess() {
return v.OrderRepo.Available(ctx, orderId)
} else if status.IsUse() {
return v.OrderRepo.Used(ctx, orderId)
} else if status.IsExpired() {
return v.OrderRepo.Expired(ctx, orderId)
}
return fmt.Errorf("notice 未知券状态,orderId:%d,statuText:%s", orderId, status.GetText())
}
func (v *VoucherBiz) QueryOrder(ctx context.Context, orderNo string) (string, error) {
order, err3 := v.OrderRepo.GetByOrderNo(ctx, orderNo)
if err3 != nil {
return "", err3
}
status, err := v.WechatCpnRepo.Query(ctx, order)
if err != nil {
return "", err
}
return fmt.Sprintf("orderNo:%s,订单状态:%s,微信查询返回状态:%s", orderNo, order.Status.GetText(), status.GetText()), nil
}