feat: 添加订单退款功能的测试用例及相关逻辑
This commit is contained in:
@@ -4,13 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
jobs_args "quyun/v2/app/jobs/args"
|
||||||
"quyun/v2/database"
|
"quyun/v2/database"
|
||||||
"quyun/v2/providers/job"
|
"quyun/v2/providers/job"
|
||||||
"quyun/v2/providers/jwt"
|
"quyun/v2/providers/jwt"
|
||||||
"quyun/v2/providers/postgres"
|
"quyun/v2/providers/postgres"
|
||||||
|
|
||||||
|
"github.com/riverqueue/river"
|
||||||
"go.ipao.vip/atom"
|
"go.ipao.vip/atom"
|
||||||
"go.ipao.vip/atom/container"
|
"go.ipao.vip/atom/container"
|
||||||
|
"go.ipao.vip/atom/contracts"
|
||||||
|
"go.ipao.vip/atom/opt"
|
||||||
"go.uber.org/dig"
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/rogeecn/fabfile"
|
"github.com/rogeecn/fabfile"
|
||||||
@@ -22,10 +26,33 @@ func Default(providers ...container.ProviderContainer) container.Providers {
|
|||||||
postgres.DefaultProvider(),
|
postgres.DefaultProvider(),
|
||||||
jwt.DefaultProvider(),
|
jwt.DefaultProvider(),
|
||||||
job.DefaultProvider(),
|
job.DefaultProvider(),
|
||||||
|
testJobWorkersProvider(),
|
||||||
database.DefaultProvider(),
|
database.DefaultProvider(),
|
||||||
}, providers...)
|
}, providers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type orderRefundTestWorker struct {
|
||||||
|
river.WorkerDefaults[jobs_args.OrderRefundJob]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *orderRefundTestWorker) Work(ctx context.Context, job *river.Job[jobs_args.OrderRefundJob]) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testJobWorkersProvider() container.ProviderContainer {
|
||||||
|
return container.ProviderContainer{
|
||||||
|
Provider: func(opts ...opt.Option) error {
|
||||||
|
return container.Container.Provide(func(__job *job.Job) (contracts.Initial, error) {
|
||||||
|
obj := &orderRefundTestWorker{}
|
||||||
|
if err := river.AddWorkerSafely(__job.Workers, obj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
}, atom.GroupInitial)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Serve(providers container.Providers, t *testing.T, invoke any) {
|
func Serve(providers container.Providers, t *testing.T, invoke any) {
|
||||||
Convey("tests boot up", t, func() {
|
Convey("tests boot up", t, func() {
|
||||||
// 关键语义:测试用例可能会在同一进程内多次调用 Serve。
|
// 关键语义:测试用例可能会在同一进程内多次调用 Serve。
|
||||||
|
|||||||
@@ -135,23 +135,25 @@ func (*orderAdmin) adminOrderDetail(
|
|||||||
return &dto.AdminOrderDetail{Order: m}, nil
|
return &dto.AdminOrderDetail{Order: m}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// adminRefund
|
// adminRefund
|
||||||
//
|
//
|
||||||
// @Summary 订单退款(租户管理)
|
// @Summary 订单退款(租户管理)
|
||||||
// @Tags Tenant
|
// @Description 该接口只负责将订单从 paid 推进到 refunding,并提交异步退款任务;退款入账与权益回收由 worker 异步完成。
|
||||||
// @Accept json
|
// @Description 重复请求幂等:订单处于 refunding/refunded 时会返回当前订单状态,不会重复入账/重复回收权益。
|
||||||
// @Produce json
|
// @Tags Tenant
|
||||||
// @Param tenantCode path string true "Tenant Code"
|
// @Accept json
|
||||||
// @Param orderID path int64 true "OrderID"
|
// @Produce json
|
||||||
// @Param form body dto.AdminOrderRefundForm true "Form"
|
// @Param tenantCode path string true "Tenant Code"
|
||||||
// @Success 200 {object} models.Order
|
// @Param orderID path int64 true "OrderID"
|
||||||
//
|
// @Param form body dto.AdminOrderRefundForm true "Form"
|
||||||
// @Router /t/:tenantCode/v1/admin/orders/:orderID/refund [post]
|
// @Success 200 {object} models.Order
|
||||||
// @Bind tenant local key(tenant)
|
//
|
||||||
// @Bind tenantUser local key(tenant_user)
|
// @Router /t/:tenantCode/v1/admin/orders/:orderID/refund [post]
|
||||||
// @Bind orderID path
|
// @Bind tenant local key(tenant)
|
||||||
// @Bind form body
|
// @Bind tenantUser local key(tenant_user)
|
||||||
func (*orderAdmin) adminRefund(
|
// @Bind orderID path
|
||||||
|
// @Bind form body
|
||||||
|
func (*orderAdmin) adminRefund(
|
||||||
ctx fiber.Ctx,
|
ctx fiber.Ctx,
|
||||||
tenant *models.Tenant,
|
tenant *models.Tenant,
|
||||||
tenantUser *models.TenantUser,
|
tenantUser *models.TenantUser,
|
||||||
|
|||||||
@@ -1127,6 +1127,12 @@ func (s *OrderTestSuite) Test_AdminRefundOrder() {
|
|||||||
So(refunding, ShouldNotBeNil)
|
So(refunding, ShouldNotBeNil)
|
||||||
So(refunding.Status, ShouldEqual, consts.OrderStatusRefunding)
|
So(refunding.Status, ShouldEqual, consts.OrderStatusRefunding)
|
||||||
|
|
||||||
|
// refunding 期间重复请求应幂等返回 refunding(并允许重复触发入队,不影响最终结果)。
|
||||||
|
refunding2, err := Order.AdminRefundOrder(ctx, tenantID, operatorUserID, orderModel.ID, false, "原因2", "", now.Add(90*time.Second))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(refunding2, ShouldNotBeNil)
|
||||||
|
So(refunding2.Status, ShouldEqual, consts.OrderStatusRefunding)
|
||||||
|
|
||||||
refunded, err := Order.ProcessRefundingOrder(ctx, &ProcessRefundingOrderParams{
|
refunded, err := Order.ProcessRefundingOrder(ctx, &ProcessRefundingOrderParams{
|
||||||
TenantID: tenantID,
|
TenantID: tenantID,
|
||||||
OrderID: orderModel.ID,
|
OrderID: orderModel.ID,
|
||||||
@@ -1139,6 +1145,19 @@ func (s *OrderTestSuite) Test_AdminRefundOrder() {
|
|||||||
So(refunded, ShouldNotBeNil)
|
So(refunded, ShouldNotBeNil)
|
||||||
So(refunded.Status, ShouldEqual, consts.OrderStatusRefunded)
|
So(refunded.Status, ShouldEqual, consts.OrderStatusRefunded)
|
||||||
|
|
||||||
|
// worker 重试/重复执行应幂等:不重复入账、不重复回收权益。
|
||||||
|
refundedRetry, err := Order.ProcessRefundingOrder(ctx, &ProcessRefundingOrderParams{
|
||||||
|
TenantID: tenantID,
|
||||||
|
OrderID: orderModel.ID,
|
||||||
|
OperatorUserID: operatorUserID,
|
||||||
|
Force: false,
|
||||||
|
Reason: "原因",
|
||||||
|
Now: now.Add(5 * time.Minute),
|
||||||
|
})
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(refundedRetry, ShouldNotBeNil)
|
||||||
|
So(refundedRetry.Status, ShouldEqual, consts.OrderStatusRefunded)
|
||||||
|
|
||||||
var tu models.TenantUser
|
var tu models.TenantUser
|
||||||
So(_db.WithContext(ctx).Where("tenant_id = ? AND user_id = ?", tenantID, buyerUserID).First(&tu).Error, ShouldBeNil)
|
So(_db.WithContext(ctx).Where("tenant_id = ? AND user_id = ?", tenantID, buyerUserID).First(&tu).Error, ShouldBeNil)
|
||||||
So(tu.Balance, ShouldEqual, 300)
|
So(tu.Balance, ShouldEqual, 300)
|
||||||
@@ -1162,8 +1181,21 @@ func (s *OrderTestSuite) Test_AdminRefundOrder() {
|
|||||||
Find(&ledgers).Error, ShouldBeNil)
|
Find(&ledgers).Error, ShouldBeNil)
|
||||||
So(len(ledgers), ShouldEqual, 1)
|
So(len(ledgers), ShouldEqual, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("不可重试错误分类应稳定", func() {
|
||||||
|
So(IsRefundJobNonRetryableError(nil), ShouldBeFalse)
|
||||||
|
So(IsRefundJobNonRetryableError(errors.New("x")), ShouldBeFalse)
|
||||||
|
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrInvalidParameter), ShouldBeTrue)
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrRecordNotFound), ShouldBeTrue)
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrStatusConflict), ShouldBeTrue)
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrPreconditionFailed), ShouldBeTrue)
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrPermissionDenied), ShouldBeTrue)
|
||||||
|
|
||||||
|
So(IsRefundJobNonRetryableError(errorx.ErrInternalError), ShouldBeFalse)
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *OrderTestSuite) Test_PurchaseContent() {
|
func (s *OrderTestSuite) Test_PurchaseContent() {
|
||||||
Convey("Order.PurchaseContent", s.T(), func() {
|
Convey("Order.PurchaseContent", s.T(), func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user