voucher/internal/biz/multi.go

399 lines
11 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"
"encoding/json"
"errors"
"fmt"
"github.com/go-kratos/kratos/v2/log"
"gorm.io/gorm"
"strconv"
"strings"
"sync"
err2 "voucher/api/err"
v1 "voucher/api/v1"
"voucher/internal/biz/bo"
"voucher/internal/biz/cmb"
"voucher/internal/biz/mixrepos"
"voucher/internal/biz/repo"
"voucher/internal/biz/vo"
"voucher/internal/conf"
"voucher/internal/data"
"voucher/internal/pkg/lock"
)
type MultiBiz struct {
bc *conf.Bootstrap
rdb *data.Rdb
Cmb *cmb.Cmb
ProductRepo repo.ProductRepo
OrderRepo repo.OrderRepo
MultiNotifyDataRepo repo.MultiNotifyDataRepo
MultiNotifyLogRepo repo.MultiNotifyLogRepo
CmbMixRepo mixrepos.CmbMixRepo
mu sync.RWMutex
NonExistentBatchData map[string]bool
}
func NewMultiBiz(
bc *conf.Bootstrap,
rdb *data.Rdb,
cmb *cmb.Cmb,
productRepo repo.ProductRepo,
orderRepo repo.OrderRepo,
multiNotifyDataRepo repo.MultiNotifyDataRepo,
multiNotifyLogRepo repo.MultiNotifyLogRepo,
cmbMixRepo mixrepos.CmbMixRepo,
) *MultiBiz {
return &MultiBiz{
bc: bc,
rdb: rdb,
Cmb: cmb,
ProductRepo: productRepo,
OrderRepo: orderRepo,
MultiNotifyDataRepo: multiNotifyDataRepo,
MultiNotifyLogRepo: multiNotifyLogRepo,
CmbMixRepo: cmbMixRepo,
NonExistentBatchData: make(map[string]bool),
}
}
func (this *MultiBiz) Get(uid string) bool {
if _, ok := this.NonExistentBatchData[uid]; ok {
return ok
}
return false
}
func (this *MultiBiz) Add(uid string) {
this.mu.Lock()
defer this.mu.Unlock()
this.NonExistentBatchData[uid] = true
}
func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error {
if biz.Get(req.PlainText.StockID) {
return nil
}
cl := vo.MultiNotifyLockKey.BuildCache([]string{
source,
req.PlainText.StockID,
req.PlainText.CouponID,
})
return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
product, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID)
if err != nil {
// 数据库不存在该活动批次,过滤掉
if errors.Is(err, gorm.ErrRecordNotFound) {
biz.Add(req.PlainText.StockID)
return nil
}
if err2.IsDbNotFound(err) {
biz.Add(req.PlainText.StockID)
return nil
}
return err
}
// 不是多笔立减金,过滤掉
if product.ActivityId == "" {
biz.Add(req.PlainText.StockID)
return nil
}
if req.PlainText.ConsumeInformation.ConsumeAmount == 0 {
return fmt.Errorf("消费金额不能为0")
}
order, err := biz.order(ctx, req)
if err != nil {
return err
}
if err = biz.Run(ctx, ip, source, req, order); err != nil {
return err
}
return nil
})
}
func (biz *MultiBiz) order(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) {
order, err := biz.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID)
if err != nil {
return nil, fmt.Errorf("订单查询错误 error: %v", err)
}
return order, nil
}
func (biz *MultiBiz) Run(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error {
if order.ActivityId == "" {
return fmt.Errorf("批次活动ID为空不是多笔立减金请检查")
}
mnd, err := biz.MultiNotifyDataRepo.GetByNotifyID(ctx, source, req.ID)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("查询通知数据错误 error: %v", err)
}
if mnd != nil {
if mnd.NoticeNum > 0 {
log.Warnf("[%s] multi notify log already exists,req:%+v", source, req)
return nil
}
} else {
mnd, err = biz.mndCreate(ctx, ip, source, req, order)
if err != nil {
return fmt.Errorf("创建通知数据错误 error: %v", err)
}
}
return biz.run(ctx, req, mnd, order)
}
func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotifyDataId int64) error {
mnd, err := biz.MultiNotifyDataRepo.GetByID(ctx, multiNotifyDataId)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("查询通知数据错误 error: %v", err)
}
order, err := biz.OrderRepo.GetByOrderNo(ctx, mnd.OrderNo)
if err != nil {
return fmt.Errorf("订单查询错误 error: %v", err)
}
var req *bo.WechatVoucherNotifyBo
if err = json.Unmarshal([]byte(mnd.OriginalData), &req); err != nil {
return fmt.Errorf("通知数据 json unmarshal 错误 error: %v", err)
}
return biz.run(ctx, req, mnd, order)
}
func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error {
if req.PlainText.Status.IsUsed() {
if order.Status.IsUse() {
parts := strings.Split(order.Remark, "_")
l := len(parts)
remark := "核销完成_"
if l == 1 {
remark = "核销完成_2"
}
if l == 2 {
i, err := strconv.Atoi(parts[1])
if err != nil {
return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err)
}
remark = fmt.Sprintf("核销完成_%d", i+1)
}
if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, remark); err != nil {
return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err)
}
} else {
if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成_1"); err != nil {
return fmt.Errorf("订单核销完成修改发生错误 error: %v", err)
}
}
} else {
if err := biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil {
return fmt.Errorf("订单核销修改发生错误 error: %v", err)
}
}
nl, err := biz.nlCreate(ctx, req, mnd, order)
if err != nil {
return fmt.Errorf("创建通知日志错误 error: %v", err)
}
return biz.Request(ctx, mnd, nl, order)
}
func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) {
originalData, err := req.Str()
if err != nil {
return nil, fmt.Errorf("通知数据 json str 错误 error: %v", err)
}
return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{
Source: source,
IP: ip,
NotifyID: req.ID,
OrderNo: order.OrderNo,
OutBizNo: order.OutBizNo,
CouponID: req.PlainText.CouponID,
StockID: req.PlainText.StockID,
ConsumeAmount: int32(req.PlainText.ConsumeInformation.ConsumeAmount),
ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime,
TransactionID: req.PlainText.ConsumeInformation.TransactionID,
EventType: req.EventType,
OriginalData: originalData,
})
}
func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, error) {
if biz.bc.Cmb.MultiNotifyUrl == "" {
return nil, fmt.Errorf("CMB多笔立减金通知地址为空")
}
nl := &bo.MultiNotifyLogBo{
MultiNotifyDataID: mnd.ID,
OrderNo: mnd.OrderNo,
OutBizNo: mnd.OutBizNo,
CouponID: mnd.CouponID,
ActivityNo: order.ProductNo,
StockID: mnd.StockID,
EventType: mnd.EventType,
Status: req.PlainText.Status,
ConsumeAmount: mnd.ConsumeAmount,
ConsumeTime: mnd.ConsumeTime,
TransactionID: req.PlainText.ConsumeInformation.TransactionID,
//RequestURL: order.NotifyUrl,
RequestURL: biz.bc.Cmb.MultiNotifyUrl,
RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(),
OrderCreateTime: order.CreateTime,
CouponCreateTime: &req.PlainText.CreateTime,
}
request, err := biz.GetRequest(ctx, nl, order)
if err != nil {
return nil, err
}
b, _ := json.Marshal(request)
nl.Request = string(b)
return biz.MultiNotifyLogRepo.Create(ctx, nl)
}
func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (string, error) {
req := &v1.CmbMultiNotifyRequest{
TransactionId: nl.OutBizNo, // cmb业务号
ActivityId: nl.ActivityNo, // 批次活动号
CouponId: nl.CouponID, // 微信券券号
AcquiredDate: order.ReceiveSuccessTime.Format("2006-01-02 15:04:05.000"), // 券领取时间
Status: "1", // 券状态 0可使用1已使用
TransDate: nl.ConsumeTime.Format("2006-01-02 15:04:05.000"), // 核销时间验券时间格式yyyy-mm-dd hh:mm:ss.sss
TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount),
OrderId: nl.TransactionID, // 券核销支付单号
Ticket: nl.OrderNo, // 券订单号lsxd订单号
OrgNo: "LANSEXIONGDIMULTI", // cmb固定值
Attach: order.Attach, // cmb拓展参数
Ext: "",
}
//if nl.Status.IsUsed() {
// req.Status = "1"
//}
bizJsonBytes, err := json.Marshal(req)
if err != nil {
return "", fmt.Errorf("json.Marshal CmbNotifyRequest error: %v", err)
}
return string(bizJsonBytes), nil
}
func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (*v1.CmbRequest, error) {
bizContent, err := biz.bizContent(nl, order)
if err != nil {
return nil, err
}
request, err := biz.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{
FuncName: vo.CmbNotifyFuncNameUpdateCodeStatusForMulti,
BizContent: bizContent,
})
if err != nil {
return nil, err
}
return request, nil
}
func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) error {
if nl.RequestURL == "" {
if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空,不做通知"); err != nil {
return err
}
// 回调通知地址为空,不返回错误,不做再次通知处理
return nil
}
request, err := biz.GetRequest(ctx, nl, order)
if err != nil {
return err
}
reply, err := biz.CmbMixRepo.Request(ctx, request, nl.RequestURL)
if err != nil {
if err3 := biz.notifyFail(ctx, nl, err.Error()); err3 != nil {
return err3
}
return err
}
if err = biz.CmbMixRepo.VerifyResponse(ctx, reply); err != nil {
errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,resp:%+v error:%s", reply, err.Error())
if err3 := biz.notifyFail(ctx, nl, errMsg); err3 != nil {
return err3
}
return err
}
return biz.notifySuccess(ctx, mmd, nl, reply)
}
func (biz *MultiBiz) notifyFail(ctx context.Context, nl *bo.MultiNotifyLogBo, remark string) error {
if err := biz.MultiNotifyLogRepo.Fail(ctx, nl.ID, remark); err != nil {
return fmt.Errorf("更新通知日志失败状态发生错误 error: %v", err)
}
return nil
}
func (biz *MultiBiz) notifySuccess(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, reply *v1.CmbReply) error {
response, err := json.Marshal(reply)
if err != nil {
return fmt.Errorf("json.Marshal CmbReply error: %v", err)
}
if err = biz.MultiNotifyLogRepo.Success(ctx, nl.ID, string(response)); err != nil {
return fmt.Errorf("更新通知日志成功状态发生错误 error: %v", err)
}
if err = biz.MultiNotifyDataRepo.AddNoticeNum(ctx, mmd.ID); err != nil {
return fmt.Errorf("更新通知数据通知次数发生错误 error: %v", err)
}
return nil
}