voucher/internal/biz/multi.go

318 lines
9.5 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"
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
}
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 {
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 {
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 err := biz.OrderRepo.OverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil {
return fmt.Errorf("订单使用完成修改发生错误 error: %v", err)
}
} else {
if err := biz.OrderRepo.LastUsed(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) {
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,
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: "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: biz.bc.Cmb.OrgNo, // cmb固定值
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 err2 := biz.notifyFail(ctx, nl, err.Error()); err2 != nil {
return err2
}
return err
}
if err = biz.CmbMixRepo.VerifyResponse(ctx, reply); err != nil {
errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,resp:%+v error:%s", reply, err.Error())
if err2 := biz.notifyFail(ctx, nl, errMsg); err2 != nil {
return err2
}
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
}