voucher/internal/biz/multi.go

363 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"
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"
"github.com/go-kratos/kratos/v2/log"
"gorm.io/gorm"
)
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
}
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,
}
}
func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error {
// 商品数据量较少,先查询商品是否存在,过滤多余的通知信息
_, err := biz.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)
}
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 {
if req.PlainText.ConsumeInformation.ConsumeAmount == 0 {
return fmt.Errorf("消费金额不能为0")
}
order, err := biz.order(ctx, req)
if err != nil {
return err
}
if order.ActivityId == "" {
return fmt.Errorf("批次活动ID为空不是多笔立减金请检查")
}
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: %w", 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 mnd.ConsumeAmount == 0 {
log.Warnf("[%s] multi notify log consume amount is 0,req:%+v", mnd.NotifyID, req)
return nil
}
nl, request, err := biz.nlCreate(ctx, req, mnd, order)
if err != nil {
return fmt.Errorf("创建通知日志错误 error: %v", err)
}
if err = biz.Request(ctx, mnd, nl, request); err != nil {
return fmt.Errorf("请求错误 error: %v", err)
}
if req.PlainText.Status.IsUsed() {
if order.Status.IsUse() {
if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil {
return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err)
}
} else {
if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); 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)
}
}
return nil
}
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,
Status: req.PlainText.Status,
OriginalData: originalData,
})
}
func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) {
if biz.bc.Cmb.MultiNotifyUrl == "" {
return nil, 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, cmbRequestBo, err := biz.GetRequest(ctx, nl, order)
if err != nil {
return nil, nil, err
}
b, _ := json.Marshal(request)
nl.OriginReq = cmbRequestBo.BizContent
nl.Request = string(b)
res, err := biz.MultiNotifyLogRepo.Create(ctx, nl)
if err != nil {
return nil, nil, fmt.Errorf("创建通知日志错误 error: %v", err)
}
// 创建出来的核销时间精度有差距,重新赋值返回出去
res.ConsumeTime = mnd.ConsumeTime
return res, request, nil
}
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已使用
CouponStatus: "0", // 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.CouponStatus = "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, *bo.CmbRequestBo, error) {
bizContent, err := biz.bizContent(nl, order)
if err != nil {
return nil, nil, err
}
r := &bo.CmbRequestBo{
FuncName: vo.CmbNotifyFuncNameUpdateCodeStatusForMulti,
BizContent: bizContent,
}
request, err := biz.CmbMixRepo.GetRequest(ctx, r)
if err != nil {
return nil, nil, err
}
return request, r, nil
}
func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, request *v1.CmbRequest) error {
if nl.RequestURL == "" {
if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空,不做通知"); err != nil {
return err
}
// 回调通知地址为空,不返回错误,不做再次通知处理
return nil
}
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
}