Merge pull request 'feat(multi-notify): 增加合单支付回调' (#2) from feature/fzy/multi-notify into pro
Reviewed-on: #2
This commit is contained in:
commit
08355a0fa8
|
|
@ -22,6 +22,8 @@ Thumbs.db
|
||||||
*.swp
|
*.swp
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
.trae/
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
cert/
|
cert/
|
||||||
log
|
log
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,19 @@ type ConsumeInformation struct {
|
||||||
ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) // 多笔立减金必须 validate:"required"
|
ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) // 多笔立减金必须 validate:"required"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CombineSubOrder 定义合单子单消费信息结构体
|
||||||
|
type CombineSubOrder struct {
|
||||||
|
TransactionID string `json:"transaction_id" validate:"required"` // 合单子单微信支付订单号
|
||||||
|
ConsumeAmount int `json:"comsume_amount" validate:"required"` // 子单核销金额,微信文档字段名如此定义
|
||||||
|
ConsumeTime time.Time `json:"consume_time" validate:"required"` // 子单核销时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// CombineOrderInfo 定义合单订单信息结构体
|
||||||
|
type CombineOrderInfo struct {
|
||||||
|
CombineConsumeAmount int `json:"combine_consume_amount"` // 合单总核销金额
|
||||||
|
SubOrders []CombineSubOrder `json:"sub_orders,omitempty"` // 合单子单列表
|
||||||
|
}
|
||||||
|
|
||||||
// PlainText 定义明文数据结构体
|
// PlainText 定义明文数据结构体
|
||||||
type PlainText struct {
|
type PlainText struct {
|
||||||
StockCreatorMchid string `json:"stock_creator_mchid" validate:"required"`
|
StockCreatorMchid string `json:"stock_creator_mchid" validate:"required"`
|
||||||
|
|
@ -29,7 +42,9 @@ type PlainText struct {
|
||||||
NoCash bool `json:"no_cash"`
|
NoCash bool `json:"no_cash"`
|
||||||
Singleitem bool `json:"singleitem"`
|
Singleitem bool `json:"singleitem"`
|
||||||
BusinessType string `json:"business_type"` // 业务类型
|
BusinessType string `json:"business_type"` // 业务类型
|
||||||
|
IsCombineOrder bool `json:"is_combine_order,omitempty"`
|
||||||
ConsumeInformation *ConsumeInformation `json:"consume_information,omitempty"`
|
ConsumeInformation *ConsumeInformation `json:"consume_information,omitempty"`
|
||||||
|
CombineOrderInfo *CombineOrderInfo `json:"combine_order_info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WechatVoucherNotifyBo struct {
|
type WechatVoucherNotifyBo struct {
|
||||||
|
|
@ -63,3 +78,39 @@ func (c *WechatVoucherNotifyBo) Validate() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *WechatVoucherNotifyBo) ValidateMultiNotify() error {
|
||||||
|
if err := c.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.PlainText.IsCombineOrder {
|
||||||
|
if c.PlainText.CombineOrderInfo == nil {
|
||||||
|
return fmt.Errorf("合单订单信息不能为空")
|
||||||
|
}
|
||||||
|
if len(c.PlainText.CombineOrderInfo.SubOrders) == 0 {
|
||||||
|
return fmt.Errorf("合单子单不能为空")
|
||||||
|
}
|
||||||
|
for _, subOrder := range c.PlainText.CombineOrderInfo.SubOrders {
|
||||||
|
if subOrder.TransactionID == "" {
|
||||||
|
return fmt.Errorf("合单子单微信支付订单号不能为空")
|
||||||
|
}
|
||||||
|
if subOrder.ConsumeAmount <= 0 {
|
||||||
|
return fmt.Errorf("合单子单核销金额必须大于0")
|
||||||
|
}
|
||||||
|
if subOrder.ConsumeTime.IsZero() {
|
||||||
|
return fmt.Errorf("合单子单核销时间不能为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.PlainText.ConsumeInformation == nil {
|
||||||
|
return fmt.Errorf("消费信息不能为空")
|
||||||
|
}
|
||||||
|
if c.PlainText.ConsumeInformation.ConsumeAmount <= 0 {
|
||||||
|
return fmt.Errorf("消费金额必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,8 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech
|
||||||
|
|
||||||
return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
||||||
|
|
||||||
if req.PlainText.ConsumeInformation.ConsumeAmount == 0 {
|
if err = req.ValidateMultiNotify(); err != nil {
|
||||||
return fmt.Errorf("消费金额不能为0")
|
return fmt.Errorf("multi validate req error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
order, err := biz.order(ctx, req)
|
order, err := biz.order(ctx, req)
|
||||||
|
|
@ -115,7 +115,7 @@ func (biz *MultiBiz) Run(ctx context.Context, ip, source string, req *bo.WechatV
|
||||||
}
|
}
|
||||||
|
|
||||||
if mnd != nil {
|
if mnd != nil {
|
||||||
if mnd.NoticeNum > 0 {
|
if !req.PlainText.IsCombineOrder && mnd.NoticeNum > 0 {
|
||||||
log.Warnf("[%s] multi notify log already exists,req:%+v", source, req)
|
log.Warnf("[%s] multi notify log already exists,req:%+v", source, req)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -150,6 +150,14 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif
|
||||||
}
|
}
|
||||||
|
|
||||||
func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error {
|
func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error {
|
||||||
|
if req.PlainText.IsCombineOrder {
|
||||||
|
return biz.runCombine(ctx, req, mnd, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
return biz.runSingle(ctx, req, mnd, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (biz *MultiBiz) runSingle(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error {
|
||||||
// 如果核销金额为空,不再推送下游
|
// 如果核销金额为空,不再推送下游
|
||||||
if mnd.ConsumeAmount == 0 {
|
if mnd.ConsumeAmount == 0 {
|
||||||
log.Warnf("[%s] multi notify log consume amount is 0,req:%+v", mnd.NotifyID, req)
|
log.Warnf("[%s] multi notify log consume amount is 0,req:%+v", mnd.NotifyID, req)
|
||||||
|
|
@ -165,20 +173,50 @@ func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd
|
||||||
return fmt.Errorf("请求错误 error: %v", err)
|
return fmt.Errorf("请求错误 error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return biz.updateOrderStatus(ctx, req, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (biz *MultiBiz) runCombine(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error {
|
||||||
|
for _, subOrder := range req.PlainText.CombineOrderInfo.SubOrders {
|
||||||
|
exists, err := biz.MultiNotifyLogRepo.ExistsSuccessByDataIDAndTransactionID(ctx, mnd.ID, subOrder.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("查询合单子单通知记录错误 error: %v", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
log.Warnf("[%s] combine sub order already notified,transaction_id:%s", mnd.NotifyID, subOrder.TransactionID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nl, request, err := biz.nlCreateBySubOrder(ctx, req, mnd, order, subOrder)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return biz.updateOrderStatus(ctx, req, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (biz *MultiBiz) updateOrderStatus(ctx context.Context, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error {
|
||||||
|
consumeTime := req.PlainText.ConsumeInformation.ConsumeTime
|
||||||
|
|
||||||
if req.PlainText.Status.IsUsed() {
|
if req.PlainText.Status.IsUsed() {
|
||||||
|
|
||||||
if order.Status.IsUse() {
|
if order.Status.IsUse() {
|
||||||
if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil {
|
if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, consumeTime, "再次核销完成"); err != nil {
|
||||||
return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err)
|
return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); err != nil {
|
if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, consumeTime, "核销完成"); err != nil {
|
||||||
return fmt.Errorf("订单核销完成修改发生错误 error: %v", err)
|
return fmt.Errorf("订单核销完成修改发生错误 error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err = biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil {
|
if err := biz.OrderRepo.MultiLastUsed(ctx, order.ID, consumeTime); err != nil {
|
||||||
return fmt.Errorf("订单核销修改发生错误 error: %v", err)
|
return fmt.Errorf("订单核销修改发生错误 error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,6 +231,13 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W
|
||||||
return nil, fmt.Errorf("通知数据 json str 错误 error: %v", err)
|
return nil, fmt.Errorf("通知数据 json str 错误 error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consumeAmount := int32(req.PlainText.ConsumeInformation.ConsumeAmount)
|
||||||
|
consumeTime := &req.PlainText.ConsumeInformation.ConsumeTime
|
||||||
|
transactionID := req.PlainText.ConsumeInformation.TransactionID
|
||||||
|
if req.PlainText.IsCombineOrder {
|
||||||
|
consumeAmount = int32(req.PlainText.CombineOrderInfo.CombineConsumeAmount)
|
||||||
|
}
|
||||||
|
|
||||||
return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{
|
return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{
|
||||||
Source: source,
|
Source: source,
|
||||||
IP: ip,
|
IP: ip,
|
||||||
|
|
@ -201,15 +246,57 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W
|
||||||
OutBizNo: order.OutBizNo,
|
OutBizNo: order.OutBizNo,
|
||||||
CouponID: req.PlainText.CouponID,
|
CouponID: req.PlainText.CouponID,
|
||||||
StockID: req.PlainText.StockID,
|
StockID: req.PlainText.StockID,
|
||||||
ConsumeAmount: int32(req.PlainText.ConsumeInformation.ConsumeAmount),
|
ConsumeAmount: consumeAmount,
|
||||||
ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime,
|
ConsumeTime: consumeTime,
|
||||||
TransactionID: req.PlainText.ConsumeInformation.TransactionID,
|
TransactionID: transactionID,
|
||||||
EventType: req.EventType,
|
EventType: req.EventType,
|
||||||
Status: req.PlainText.Status,
|
Status: req.PlainText.Status,
|
||||||
OriginalData: originalData,
|
OriginalData: originalData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (biz *MultiBiz) nlCreateBySubOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo, subOrder bo.CombineSubOrder) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) {
|
||||||
|
if biz.bc.Cmb.MultiNotifyUrl == "" {
|
||||||
|
return nil, nil, fmt.Errorf("CMB多笔立减金通知地址为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeTime := subOrder.ConsumeTime
|
||||||
|
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: int32(subOrder.ConsumeAmount),
|
||||||
|
ConsumeTime: &consumeTime,
|
||||||
|
TransactionID: subOrder.TransactionID,
|
||||||
|
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 = nl.ConsumeTime
|
||||||
|
|
||||||
|
return res, request, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) {
|
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 == "" {
|
if biz.bc.Cmb.MultiNotifyUrl == "" {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
type MultiNotifyLogRepo interface {
|
type MultiNotifyLogRepo interface {
|
||||||
Create(ctx context.Context, req *bo.MultiNotifyLogBo) (*bo.MultiNotifyLogBo, error)
|
Create(ctx context.Context, req *bo.MultiNotifyLogBo) (*bo.MultiNotifyLogBo, error)
|
||||||
GetByID(ctx context.Context, id int64) (*bo.MultiNotifyLogBo, error)
|
GetByID(ctx context.Context, id int64) (*bo.MultiNotifyLogBo, error)
|
||||||
|
ExistsSuccessByDataIDAndTransactionID(ctx context.Context, multiNotifyDataID int64, transactionID string) (bool, error)
|
||||||
Success(ctx context.Context, id int64, response string) error
|
Success(ctx context.Context, id int64, response string) error
|
||||||
Fail(ctx context.Context, id int64, remark string) error
|
Fail(ctx context.Context, id int64, remark string) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req
|
||||||
}
|
}
|
||||||
|
|
||||||
if order.ActivityId != "" {
|
if order.ActivityId != "" {
|
||||||
if err = req.Validate(); err != nil {
|
if err = req.ValidateMultiNotify(); err != nil {
|
||||||
return fmt.Errorf("multi validate req error: %v", err)
|
return fmt.Errorf("multi validate req error: %v", err)
|
||||||
}
|
}
|
||||||
return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order)
|
return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package repoimpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gorm.io/gorm"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
err2 "voucher/api/err"
|
err2 "voucher/api/err"
|
||||||
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
"voucher/internal/data"
|
"voucher/internal/data"
|
||||||
"voucher/internal/data/model"
|
"voucher/internal/data/model"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MultiNotifyLogRepoImpl .
|
// MultiNotifyLogRepoImpl .
|
||||||
|
|
@ -77,6 +79,24 @@ func (p *MultiNotifyLogRepoImpl) GetByID(ctx context.Context, id int64) (*bo.Mul
|
||||||
return p.ToBo(&item), nil
|
return p.ToBo(&item), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *MultiNotifyLogRepoImpl) ExistsSuccessByDataIDAndTransactionID(ctx context.Context, multiNotifyDataID int64, transactionID string) (bool, error) {
|
||||||
|
var item model.MultiNotifyLog
|
||||||
|
|
||||||
|
err := p.DB(ctx).
|
||||||
|
Select("id").
|
||||||
|
Where("multi_notify_data_id = ? AND transaction_id = ? AND request_status = ?", multiNotifyDataID, transactionID, vo.MultiNotifyLogStatusSuccess.GetValue()).
|
||||||
|
Limit(1).
|
||||||
|
Take(&item).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *MultiNotifyLogRepoImpl) Success(ctx context.Context, id int64, response string) error {
|
func (p *MultiNotifyLogRepoImpl) Success(ctx context.Context, id int64, response string) error {
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue