351 lines
11 KiB
Go
351 lines
11 KiB
Go
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 {
|
||
|
||
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: %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 {
|
||
|
||
nl, err := biz.nlCreate(ctx, req, mnd, order)
|
||
if err != nil {
|
||
return fmt.Errorf("创建通知日志错误 error: %v", err)
|
||
}
|
||
|
||
if err = biz.Request(ctx, mnd, nl, order); 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, 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)
|
||
|
||
res, err := biz.MultiNotifyLogRepo.Create(ctx, nl)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建通知日志错误 error: %v", err)
|
||
}
|
||
// 创建出来的核销时间精度有差距,重新赋值返回出去
|
||
res.ConsumeTime = mnd.ConsumeTime
|
||
|
||
return res, 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, 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
|
||
}
|