This commit is contained in:
ziming 2025-06-03 11:05:05 +08:00
parent 4b9c9b5652
commit c92828fc41
10 changed files with 155 additions and 348 deletions

View File

@ -62,12 +62,6 @@ service Cmb {
};
}
rpc Test (Empty) returns (Empty) {
option (google.api.http) = {
post: "/voucher/cmb/v1/test",
body: "*"
};
}
}
message OrderRetryRequest {

View File

@ -1,101 +0,0 @@
package biz
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
err2 "voucher/api/err"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
"voucher/internal/pkg/lock"
)
func (v *VoucherBiz) RegisterTag(ctx context.Context, productNo string) error {
product, err := v.ProductRepo.GetByProductNo(ctx, productNo)
if err != nil {
return err
}
return v.registerNotifyTag(ctx, product.MchId, product.BatchNo)
}
func (v *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
c := vo.WechatNotifyRegisterTagCacheKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err == nil {
// 缓存存在,直接返回
return nil
}
if err != redis.Nil {
return fmt.Errorf(fmt.Sprintf("获取redis缓存%s异常:%v", c.Key, err))
}
cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
// 二次获取,判定处理,以免获取锁后又执行了一次
cacheValue, err3 := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err3 != nil && err3 != redis.Nil {
return fmt.Errorf(fmt.Sprintf("二次获取redis缓存%s异常:%v", c.Key, err))
}
if cacheValue != "" {
return nil // 有直接返回
}
wechatNotifyTag, err3 := v.WechatNotifyRegisterTagRepo.GetByStockIdAndMchId(ctx, stockCreatorMchID, stockID)
if err3 != nil && !err2.IsDbNotFound(err3) {
return err3
}
if wechatNotifyTag != nil {
if wechatNotifyTag.Tag != v.bc.WechatNotifyMQ.Tag {
return fmt.Errorf("tag不一致请检查tag配置:%s", wechatNotifyTag.Tag)
}
if wechatNotifyTag.Status.IsSuccess() {
return v.setCache(ctx, c, wechatNotifyTag)
}
} else {
wechatNotifyTag, err3 = v.createWechatNotifyRegisterTag(ctx, stockCreatorMchID, stockID)
if err3 != nil {
return err3
}
}
if err = v.WechatCpnRepo.RegisterNotifyTag(ctx, stockID); err != nil {
return v.WechatNotifyRegisterTagRepo.Fail(ctx, wechatNotifyTag.ID, err.Error())
}
if err = v.WechatNotifyRegisterTagRepo.Success(ctx, wechatNotifyTag.ID); err != nil {
return err
}
return v.setCache(ctx, c, wechatNotifyTag)
})
}
func (v *VoucherBiz) createWechatNotifyRegisterTag(ctx context.Context, stockCreatorMchID, stockID string) (*bo.WechatNotifyRegisterTagBo, error) {
return v.WechatNotifyRegisterTagRepo.Create(ctx, &bo.WechatNotifyRegisterTagBo{
StockID: stockID,
StockCreatorMchID: stockCreatorMchID,
Tag: v.bc.WechatNotifyMQ.Tag,
})
}
func (v *VoucherBiz) setCache(ctx context.Context, c *vo.Cache, wechatNotifyTag *bo.WechatNotifyRegisterTagBo) error {
if err := v.rdb.Rdb.Set(ctx, c.Key, wechatNotifyTag.Tag, c.TTL).Err(); err != nil {
return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err))
}
return nil
}

View File

@ -2,17 +2,62 @@ package biz
import (
"context"
"encoding/json"
"fmt"
"github.com/go-kratos/kratos/v2/errors"
"time"
err2 "voucher/api/err"
v1 "voucher/api/v1"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
)
func (v *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (orderNo string, err error) {
func (v *VoucherBiz) CmbOrder(ctx context.Context, request *v1.CmbRequest) (*v1.CmbReply, error) {
order, err3 := v.GetByOutBizNo(ctx, req)
order, err := v.cmbOrder(ctx, request)
if err != nil {
return v.OrderFail(ctx, err)
}
reply, err := v.OrderSuccess(ctx, order.OrderNo)
if err != nil {
return nil, err
}
_ = v.bbToWx(ctx, order, reply)
return reply, nil
}
func (v *VoucherBiz) cmbOrder(ctx context.Context, request *v1.CmbRequest) (*bo.OrderBo, error) {
bizContent, err := v.CmbMixRepo.OrderVerify(ctx, request)
if err != nil {
return nil, err
}
product, err3 := v.ProductRepo.GetByProductNo(ctx, bizContent.ActivityId)
if err3 != nil {
return "", err3
return nil, err
}
order, err := v.Order(ctx, product, bizContent)
if err != nil {
return nil, err
}
order.MiniMum = product.MiniMum
order.CouponValue = product.Amount
return order, nil
}
func (v *VoucherBiz) Order(ctx context.Context, product *bo.ProductBo, bizContent *v1.CmbOrderRequest) (order *bo.OrderBo, err error) {
order, err = v.OrderRepo.GetByOutBizNo(ctx, vo.OrderTypeCmb, bizContent.TransactionId)
if err != nil && !err2.IsDbNotFound(err) {
return order, err
}
if order != nil {
@ -20,60 +65,74 @@ func (v *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (or
if order.Status.IsFail() || order.Status.IsIng() {
if err4 := v.orderRetry(ctx, order); err4 != nil {
return "", err4
return order, err4
}
}
return order.OrderNo, err
return order, err
}
product, err3 := v.ProductRepo.GetByProductNo(ctx, req.ProductNo)
if err3 != nil {
return "", err3
order, err = v.order(ctx, product, bizContent)
if err != nil {
return nil, err
}
order, err3 = v.order(ctx, req, product)
if err3 != nil {
return "", err3
}
return order.OrderNo, nil
return order, nil
}
func (v *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
func (v *VoucherBiz) order(ctx context.Context, product *bo.ProductBo, bizContent *v1.CmbOrderRequest) (*bo.OrderBo, error) {
req := &bo.OrderCreateReqBo{
OutBizNo: bizContent.TransactionId,
ProductNo: bizContent.ActivityId,
Account: bizContent.CmbUid,
AppID: bizContent.AppId,
Attach: bizContent.Attach,
AccountType: vo.OrderAccountTypeOpenId,
Type: vo.OrderTypeCmb,
NotifyUrl: v.bc.Cmb.NotifyUrl,
}
order, err := v.create(ctx, req, product)
if err != nil {
return nil, err
}
voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
if err != nil {
if err3 := v.fail(ctx, order, err); err3 != nil {
return nil, err3
}
return nil, err
}
_ = v.cmbToBB(ctx, bizContent)
//voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
//if err != nil {
// if err3 := v.fail(ctx, order, err); err3 != nil {
// return nil, err3
// }
// return nil, err
//}
//休眠100微妙
time.Sleep(time.Millisecond * 100)
//mock发券成功,测试使用
voucherNo := order.OrderNo
if err = v.success(ctx, order, voucherNo); err != nil {
return nil, err
return order, err
}
//_ = v.bbToWx(ctx, order, reply)
return order, nil
}
func (v *VoucherBiz) orderRetry(ctx context.Context, order *bo.OrderBo) error {
voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
// mock发券成功,测试使用
voucherNo := order.OrderNo
if err != nil {
if err3 := v.fail(ctx, order, err); err3 != nil {
return err3
}
return err
}
//voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
//if err != nil {
// if err3 := v.fail(ctx, order, err); err3 != nil {
// return err3
// }
// return err
//}
return v.success(ctx, order, voucherNo)
}
@ -122,17 +181,6 @@ func (v *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, errReq error)
return v.alarm(ctx, order, errReq.Error())
}
func (v *VoucherBiz) GetByOutBizNo(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, error) {
order, err := v.OrderRepo.GetByOutBizNo(ctx, vo.OrderTypeCmb, req.OutBizNo)
if err != nil && !err2.IsDbNotFound(err) {
return nil, err
}
return order, nil
}
func (v *VoucherBiz) UpdateOrderStatus(ctx context.Context, orderId uint64, status vo.OrderStatus) error {
if status.IsSuccess() {
@ -150,3 +198,36 @@ func (v *VoucherBiz) UpdateOrderStatus(ctx context.Context, orderId uint64, stat
return fmt.Errorf("notice 未知券状态,orderId:%d,statuText:%s", orderId, status.GetText())
}
func (c *VoucherBiz) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbReply, error) {
bizReply := &v1.CmbOrderReply{
RespCode: vo.CmbResponseStatusSuccess.GetValue(),
RespMsg: "成功",
CodeNo: orderNo,
}
replyBizContent, _ := json.Marshal(bizReply)
return c.GetResponse(ctx, replyBizContent)
}
func (c *VoucherBiz) OrderFail(ctx context.Context, err error) (*v1.CmbReply, error) {
se := errors.FromError(err)
if len(se.Reason) == 0 {
se.Reason = err2.CmbErr_CMB_UNKNOWN.String()
}
bizReply := &v1.CmbOrderReply{
RespCode: vo.CmbResponseStatusFail.GetValue(),
RespMsg: se.Message,
CodeNo: "",
ThirdErrCode: se.Reason,
}
replyBizContent, _ := json.Marshal(bizReply)
return c.GetResponse(ctx, replyBizContent)
}

View File

@ -3,7 +3,6 @@ package biz
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
err2 "voucher/api/err"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
@ -23,33 +22,9 @@ func (this *VoucherBiz) RegisterTag(ctx context.Context, productNo string) error
func (v *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
c := vo.WechatNotifyRegisterTagCacheKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
_, err := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err == nil {
// 缓存存在,直接返回
return nil
}
if err != redis.Nil {
return fmt.Errorf(fmt.Sprintf("获取redis缓存%s异常:%v", c.Key, err))
}
cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildCache([]string{v.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
// 二次获取,判定处理,以免获取锁后又执行了一次
cacheValue, err3 := v.rdb.Rdb.Get(ctx, c.Key).Result()
if err3 != nil && err3 != redis.Nil {
return fmt.Errorf(fmt.Sprintf("二次获取redis缓存%s异常:%v", c.Key, err))
}
if cacheValue != "" {
return nil // 有直接返回
}
wechatNotifyTag, err3 := v.WechatNotifyRegisterTagRepo.GetByStockIdAndMchId(ctx, stockCreatorMchID, stockID)
if err3 != nil && !err2.IsDbNotFound(err3) {
@ -57,33 +32,36 @@ func (v *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, s
}
if wechatNotifyTag != nil {
if wechatNotifyTag.Tag != v.bc.WechatNotifyMQ.Tag {
return fmt.Errorf("tag不一致请检查tag配置:%s", wechatNotifyTag.Tag)
}
if wechatNotifyTag.Status.IsSuccess() {
return v.setCache(ctx, c, wechatNotifyTag)
return nil
}
} else {
wechatNotifyTag, err3 = v.createWechatNotifyRegisterTag(ctx, stockCreatorMchID, stockID)
if err3 != nil {
return err3
}
}
if err = v.WechatCpnRepo.RegisterNotifyTag(ctx, stockID); err != nil {
return v.WechatNotifyRegisterTagRepo.Fail(ctx, wechatNotifyTag.ID, err.Error())
}
if err = v.WechatNotifyRegisterTagRepo.Success(ctx, wechatNotifyTag.ID); err != nil {
return err
}
return v.setCache(ctx, c, wechatNotifyTag)
return v.registerTag(ctx, wechatNotifyTag)
})
}
func (v *VoucherBiz) registerTag(ctx context.Context, wechatNotifyTa *bo.WechatNotifyRegisterTagBo) error {
if err := v.WechatCpnRepo.RegisterNotifyTag(ctx, wechatNotifyTa.Tag); err != nil {
return v.WechatNotifyRegisterTagRepo.Fail(ctx, wechatNotifyTa.ID, err.Error())
}
return v.WechatNotifyRegisterTagRepo.Success(ctx, wechatNotifyTa.ID)
}
func (v *VoucherBiz) createWechatNotifyRegisterTag(ctx context.Context, stockCreatorMchID, stockID string) (*bo.WechatNotifyRegisterTagBo, error) {
return v.WechatNotifyRegisterTagRepo.Create(ctx, &bo.WechatNotifyRegisterTagBo{
StockID: stockID,
@ -91,12 +69,3 @@ func (v *VoucherBiz) createWechatNotifyRegisterTag(ctx context.Context, stockCre
Tag: v.bc.WechatNotifyMQ.Tag,
})
}
func (v *VoucherBiz) setCache(ctx context.Context, c *vo.Cache, wechatNotifyTag *bo.WechatNotifyRegisterTagBo) error {
if err := v.rdb.Rdb.Set(ctx, c.Key, wechatNotifyTag.Tag, c.TTL).Err(); err != nil {
return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err))
}
return nil
}

View File

@ -2,7 +2,6 @@ package repo
import (
"context"
"time"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
)
@ -18,7 +17,7 @@ type OrderRepo interface {
Ing(ctx context.Context, id uint64) error
Success(ctx context.Context, id uint64, voucherNo string) error
Fail(ctx context.Context, id uint64, remark string) error
Used(ctx context.Context, id uint64, usedTime time.Time) error
Used(ctx context.Context, id uint64) error
Available(ctx context.Context, id uint64) error
Expired(ctx context.Context, id uint64) error
}

View File

@ -86,19 +86,24 @@ func (v *VoucherBiz) createUseLog(ctx context.Context, order *bo.OrderBo, req *b
func (v *VoucherBiz) used(ctx context.Context, order *bo.OrderBo, req *bo.WechatVoucherNotifyBo) error {
var usedTime time.Time
if req.PlainText.ConsumeInformation.ConsumeTime != "" {
usedTime, _ = time.Parse(time.RFC3339, req.PlainText.ConsumeInformation.ConsumeTime)
} else {
usedTime = time.Now()
}
//var usedTime time.Time
//if req.PlainText.ConsumeInformation.ConsumeTime != "" {
// usedTime, _ = time.Parse(time.RFC3339, req.PlainText.ConsumeInformation.ConsumeTime)
//} else {
// usedTime = time.Now()
//}
if order.Status.IsUse() && usedTime == *order.LastUseTime {
//if order.Status.IsUse() && usedTime == *order.LastUseTime {
// log.Warnf("券状态已是可使用,忽略不处理,orderNo:%s", order.OrderNo)
// return nil
//}
if order.Status.IsUse() {
log.Warnf("券状态已是可使用,忽略不处理,orderNo:%s", order.OrderNo)
return nil
}
if err := v.OrderRepo.Used(ctx, order.ID, usedTime); err != nil {
if err := v.OrderRepo.Used(ctx, order.ID); err != nil {
return err
}

View File

@ -271,7 +271,7 @@ func (p *OrderRepoImpl) Fail(ctx context.Context, id uint64, remark string) erro
return nil
}
func (p *OrderRepoImpl) Used(ctx context.Context, id uint64, usedTime time.Time) error {
func (p *OrderRepoImpl) Used(ctx context.Context, id uint64) error {
now := time.Now()
tx := p.DB(ctx).
@ -280,7 +280,7 @@ func (p *OrderRepoImpl) Used(ctx context.Context, id uint64, usedTime time.Time)
}).
Updates(model.Order{
Status: vo.OrderStatusUse.GetValue(),
LastUseTime: &usedTime,
LastUseTime: &now,
UpdateTime: &now,
})

View File

@ -1,41 +0,0 @@
package service
import (
"context"
"fmt"
v1 "voucher/api/v1"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
"voucher/internal/pkg/helper"
)
func (s *CmbService) BatchQuery(ctx context.Context, request *v1.BatchQueryRequest) (*v1.Empty, error) {
req := &bo.FindInBatchesUseBo{
Type: vo.OrderTypeCmb,
}
if len(request.BeginTime) > 0 {
beginTime, err := helper.Parse(request.BeginTime)
if err != nil {
return nil, err
}
req.StartTime = &beginTime
}
if len(request.EndTime) > 0 {
endTime, err := helper.Parse(request.EndTime)
if err != nil {
return nil, err
}
req.EndTime = &endTime
}
if req.StartTime == nil || req.EndTime == nil {
return nil, fmt.Errorf("时间范围错误")
}
ctx = context.Background()
return nil, s.VoucherBiz.ExecuteNotice(ctx, req)
}

View File

@ -2,80 +2,10 @@ package service
import (
"context"
"encoding/json"
"github.com/go-kratos/kratos/v2/errors"
err2 "voucher/api/err"
v1 "voucher/api/v1"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
)
func (c *CmbService) Order(ctx context.Context, request *v1.CmbRequest) (*v1.CmbReply, error) {
orderNo, err := c.order(ctx, request)
if err != nil {
return c.OrderFail(ctx, err)
}
return c.OrderSuccess(ctx, orderNo)
}
func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (string, error) {
bizContent, err := c.CmbMixRepo.OrderVerify(ctx, request)
if err != nil {
return "", err
}
boReq := &bo.OrderCreateReqBo{
OutBizNo: bizContent.TransactionId,
ProductNo: bizContent.ActivityId,
Account: bizContent.CmbUid,
AppID: bizContent.AppId,
Attach: bizContent.Attach,
AccountType: vo.OrderAccountTypeOpenId,
Type: vo.OrderTypeCmb,
NotifyUrl: c.bc.Cmb.NotifyUrl,
}
orderNo, err := c.VoucherBiz.CmbOrder(ctx, boReq)
if err != nil {
return "", err
}
return orderNo, nil
}
func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbReply, error) {
bizReply := &v1.CmbOrderReply{
RespCode: vo.CmbResponseStatusSuccess.GetValue(),
RespMsg: "成功",
CodeNo: orderNo,
}
replyBizContent, _ := json.Marshal(bizReply)
return c.GetResponse(ctx, replyBizContent)
}
func (c *CmbService) OrderFail(ctx context.Context, err error) (*v1.CmbReply, error) {
se := errors.FromError(err)
if len(se.Reason) == 0 {
se.Reason = err2.CmbErr_CMB_UNKNOWN.String()
}
bizReply := &v1.CmbOrderReply{
RespCode: vo.CmbResponseStatusFail.GetValue(),
RespMsg: se.Message,
CodeNo: "",
ThirdErrCode: se.Reason,
}
replyBizContent, _ := json.Marshal(bizReply)
return c.GetResponse(ctx, replyBizContent)
return c.VoucherBiz.CmbOrder(ctx, request)
}

View File

@ -150,37 +150,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/api.v1.CmbRequest'
/voucher/cmb/v1/test:
post:
tags:
- Cmb
operationId: Cmb_BatchQuery
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/api.v1.BatchQueryRequest'
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/api.v1.Empty'
components:
schemas:
api.v1.BatchQueryRequest:
type: object
properties:
order_ids:
type: array
items:
type: string
begin_time:
type: string
end_time:
type: string
api.v1.CmbOrderRequest:
type: object
properties: