feat: 实现订单支付功能模块
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m36s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m36s
- 新增订单管理、支付回调、购买验证等核心服务 - 实现订单、订单项目的数据存储层和 API 接口 - 添加订单数据库迁移和 DTO 定义 - 更新 API 文档和路由配置 - 同步 3 个新规范到主规范库(订单管理、订单支付、套餐购买验证) - 完成 OpenSpec 变更归档 Ultraworked with Sisyphus
This commit is contained in:
47
internal/store/postgres/order_item_store.go
Normal file
47
internal/store/postgres/order_item_store.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrderItemStore struct {
|
||||
db *gorm.DB
|
||||
redis *redis.Client
|
||||
}
|
||||
|
||||
func NewOrderItemStore(db *gorm.DB, redis *redis.Client) *OrderItemStore {
|
||||
return &OrderItemStore{
|
||||
db: db,
|
||||
redis: redis,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrderItemStore) BatchCreate(ctx context.Context, items []*model.OrderItem) error {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.db.WithContext(ctx).Create(&items).Error
|
||||
}
|
||||
|
||||
func (s *OrderItemStore) ListByOrderID(ctx context.Context, orderID uint) ([]*model.OrderItem, error) {
|
||||
var items []*model.OrderItem
|
||||
if err := s.db.WithContext(ctx).Where("order_id = ?", orderID).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s *OrderItemStore) ListByOrderIDs(ctx context.Context, orderIDs []uint) ([]*model.OrderItem, error) {
|
||||
if len(orderIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var items []*model.OrderItem
|
||||
if err := s.db.WithContext(ctx).Where("order_id IN ?", orderIDs).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
142
internal/store/postgres/order_item_store_test.go
Normal file
142
internal/store/postgres/order_item_store_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOrderItemStore_BatchCreate(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
orderStore := NewOrderStore(tx, rdb)
|
||||
itemStore := NewOrderItemStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order := &model.Order{
|
||||
OrderNo: orderStore.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 100,
|
||||
TotalAmount: 15000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
require.NoError(t, orderStore.Create(ctx, order, nil))
|
||||
|
||||
items := []*model.OrderItem{
|
||||
{OrderID: order.ID, PackageID: 1, PackageName: "套餐A", Quantity: 1, UnitPrice: 5000, Amount: 5000},
|
||||
{OrderID: order.ID, PackageID: 2, PackageName: "套餐B", Quantity: 2, UnitPrice: 5000, Amount: 10000},
|
||||
}
|
||||
|
||||
err := itemStore.BatchCreate(ctx, items)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, item := range items {
|
||||
assert.NotZero(t, item.ID)
|
||||
assert.Equal(t, order.ID, item.OrderID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderItemStore_BatchCreate_Empty(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
itemStore := NewOrderItemStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
err := itemStore.BatchCreate(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = itemStore.BatchCreate(ctx, []*model.OrderItem{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestOrderItemStore_ListByOrderID(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
orderStore := NewOrderStore(tx, rdb)
|
||||
itemStore := NewOrderItemStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order := &model.Order{
|
||||
OrderNo: orderStore.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeDevice,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: 200,
|
||||
TotalAmount: 20000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
items := []*model.OrderItem{
|
||||
{PackageID: 10, PackageName: "设备套餐1", Quantity: 1, UnitPrice: 10000, Amount: 10000},
|
||||
{PackageID: 11, PackageName: "设备套餐2", Quantity: 1, UnitPrice: 10000, Amount: 10000},
|
||||
}
|
||||
require.NoError(t, orderStore.Create(ctx, order, items))
|
||||
|
||||
result, err := itemStore.ListByOrderID(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
for _, item := range result {
|
||||
assert.Equal(t, order.ID, item.OrderID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderItemStore_ListByOrderIDs(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
orderStore := NewOrderStore(tx, rdb)
|
||||
itemStore := NewOrderItemStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order1 := &model.Order{
|
||||
OrderNo: orderStore.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 300,
|
||||
TotalAmount: 5000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
items1 := []*model.OrderItem{
|
||||
{PackageID: 20, PackageName: "套餐X", Quantity: 1, UnitPrice: 5000, Amount: 5000},
|
||||
}
|
||||
require.NoError(t, orderStore.Create(ctx, order1, items1))
|
||||
|
||||
order2 := &model.Order{
|
||||
OrderNo: orderStore.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 300,
|
||||
TotalAmount: 8000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
items2 := []*model.OrderItem{
|
||||
{PackageID: 21, PackageName: "套餐Y", Quantity: 1, UnitPrice: 3000, Amount: 3000},
|
||||
{PackageID: 22, PackageName: "套餐Z", Quantity: 1, UnitPrice: 5000, Amount: 5000},
|
||||
}
|
||||
require.NoError(t, orderStore.Create(ctx, order2, items2))
|
||||
|
||||
t.Run("查询多个订单的明细", func(t *testing.T) {
|
||||
result, err := itemStore.ListByOrderIDs(ctx, []uint{order1.ID, order2.ID})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
})
|
||||
|
||||
t.Run("空订单ID列表", func(t *testing.T) {
|
||||
result, err := itemStore.ListByOrderIDs(ctx, []uint{})
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, result)
|
||||
})
|
||||
|
||||
t.Run("不存在的订单ID", func(t *testing.T) {
|
||||
result, err := itemStore.ListByOrderIDs(ctx, []uint{99999})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
})
|
||||
}
|
||||
148
internal/store/postgres/order_store.go
Normal file
148
internal/store/postgres/order_store.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrderStore struct {
|
||||
db *gorm.DB
|
||||
redis *redis.Client
|
||||
}
|
||||
|
||||
func NewOrderStore(db *gorm.DB, redis *redis.Client) *OrderStore {
|
||||
return &OrderStore{
|
||||
db: db,
|
||||
redis: redis,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrderStore) Create(ctx context.Context, order *model.Order, items []*model.OrderItem) error {
|
||||
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(order).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
item.OrderID = order.ID
|
||||
if err := tx.Create(item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *OrderStore) GetByID(ctx context.Context, id uint) (*model.Order, error) {
|
||||
var order model.Order
|
||||
if err := s.db.WithContext(ctx).First(&order, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order, nil
|
||||
}
|
||||
|
||||
func (s *OrderStore) GetByIDWithItems(ctx context.Context, id uint) (*model.Order, []*model.OrderItem, error) {
|
||||
var order model.Order
|
||||
if err := s.db.WithContext(ctx).First(&order, id).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var items []*model.OrderItem
|
||||
if err := s.db.WithContext(ctx).Where("order_id = ?", id).Find(&items).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &order, items, nil
|
||||
}
|
||||
|
||||
func (s *OrderStore) GetByOrderNo(ctx context.Context, orderNo string) (*model.Order, error) {
|
||||
var order model.Order
|
||||
if err := s.db.WithContext(ctx).Where("order_no = ?", orderNo).First(&order).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order, nil
|
||||
}
|
||||
|
||||
func (s *OrderStore) Update(ctx context.Context, order *model.Order) error {
|
||||
return s.db.WithContext(ctx).Save(order).Error
|
||||
}
|
||||
|
||||
func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]any) ([]*model.Order, int64, error) {
|
||||
var orders []*model.Order
|
||||
var total int64
|
||||
|
||||
query := s.db.WithContext(ctx).Model(&model.Order{})
|
||||
|
||||
if v, ok := filters["payment_status"]; ok {
|
||||
query = query.Where("payment_status = ?", v)
|
||||
}
|
||||
if v, ok := filters["order_type"]; ok {
|
||||
query = query.Where("order_type = ?", v)
|
||||
}
|
||||
if v, ok := filters["order_no"]; ok {
|
||||
query = query.Where("order_no = ?", v)
|
||||
}
|
||||
if v, ok := filters["buyer_type"]; ok {
|
||||
query = query.Where("buyer_type = ?", v)
|
||||
}
|
||||
if v, ok := filters["buyer_id"]; ok {
|
||||
query = query.Where("buyer_id = ?", v)
|
||||
}
|
||||
if v, ok := filters["iot_card_id"]; ok {
|
||||
query = query.Where("iot_card_id = ?", v)
|
||||
}
|
||||
if v, ok := filters["device_id"]; ok {
|
||||
query = query.Where("device_id = ?", v)
|
||||
}
|
||||
if v, ok := filters["start_time"]; ok {
|
||||
query = query.Where("created_at >= ?", v)
|
||||
}
|
||||
if v, ok := filters["end_time"]; ok {
|
||||
query = query.Where("created_at <= ?", v)
|
||||
}
|
||||
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = store.DefaultQueryOptions()
|
||||
}
|
||||
|
||||
offset := (opts.Page - 1) * opts.PageSize
|
||||
if opts.OrderBy != "" {
|
||||
query = query.Order(opts.OrderBy)
|
||||
} else {
|
||||
query = query.Order("id DESC")
|
||||
}
|
||||
|
||||
if err := query.Offset(offset).Limit(opts.PageSize).Find(&orders).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return orders, total, nil
|
||||
}
|
||||
|
||||
func (s *OrderStore) UpdatePaymentStatus(ctx context.Context, id uint, status int, paidAt *time.Time) error {
|
||||
updates := map[string]any{
|
||||
"payment_status": status,
|
||||
}
|
||||
if paidAt != nil {
|
||||
updates["paid_at"] = paidAt
|
||||
}
|
||||
return s.db.WithContext(ctx).Model(&model.Order{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (s *OrderStore) GenerateOrderNo() string {
|
||||
now := time.Now()
|
||||
randomNum := rand.Intn(1000000)
|
||||
return fmt.Sprintf("ORD%s%06d", now.Format("20060102150405"), randomNum)
|
||||
}
|
||||
287
internal/store/postgres/order_store_test.go
Normal file
287
internal/store/postgres/order_store_test.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOrderStore_Create(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
cardID := uint(1001)
|
||||
order := &model.Order{
|
||||
OrderNo: s.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 100,
|
||||
IotCardID: &cardID,
|
||||
TotalAmount: 9900,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
items := []*model.OrderItem{
|
||||
{
|
||||
PackageID: 1,
|
||||
PackageName: "测试套餐1",
|
||||
Quantity: 1,
|
||||
UnitPrice: 5000,
|
||||
Amount: 5000,
|
||||
},
|
||||
{
|
||||
PackageID: 2,
|
||||
PackageName: "测试套餐2",
|
||||
Quantity: 1,
|
||||
UnitPrice: 4900,
|
||||
Amount: 4900,
|
||||
},
|
||||
}
|
||||
|
||||
err := s.Create(ctx, order, items)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, order.ID)
|
||||
|
||||
for _, item := range items {
|
||||
assert.NotZero(t, item.ID)
|
||||
assert.Equal(t, order.ID, item.OrderID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderStore_GetByID(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order := &model.Order{
|
||||
OrderNo: s.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: 200,
|
||||
TotalAmount: 19900,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, order, nil))
|
||||
|
||||
t.Run("查询存在的订单", func(t *testing.T) {
|
||||
result, err := s.GetByID(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, order.OrderNo, result.OrderNo)
|
||||
assert.Equal(t, order.BuyerType, result.BuyerType)
|
||||
assert.Equal(t, order.TotalAmount, result.TotalAmount)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的订单", func(t *testing.T) {
|
||||
_, err := s.GetByID(ctx, 99999)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOrderStore_GetByIDWithItems(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
deviceID := uint(2001)
|
||||
order := &model.Order{
|
||||
OrderNo: s.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeDevice,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 300,
|
||||
DeviceID: &deviceID,
|
||||
TotalAmount: 29900,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
items := []*model.OrderItem{
|
||||
{PackageID: 10, PackageName: "设备套餐A", Quantity: 1, UnitPrice: 15000, Amount: 15000},
|
||||
{PackageID: 11, PackageName: "设备套餐B", Quantity: 1, UnitPrice: 14900, Amount: 14900},
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, order, items))
|
||||
|
||||
resultOrder, resultItems, err := s.GetByIDWithItems(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, order.OrderNo, resultOrder.OrderNo)
|
||||
assert.Len(t, resultItems, 2)
|
||||
}
|
||||
|
||||
func TestOrderStore_GetByOrderNo(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
orderNo := s.GenerateOrderNo()
|
||||
order := &model.Order{
|
||||
OrderNo: orderNo,
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: 400,
|
||||
TotalAmount: 5000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, order, nil))
|
||||
|
||||
t.Run("查询存在的订单号", func(t *testing.T) {
|
||||
result, err := s.GetByOrderNo(ctx, orderNo)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, order.ID, result.ID)
|
||||
})
|
||||
|
||||
t.Run("查询不存在的订单号", func(t *testing.T) {
|
||||
_, err := s.GetByOrderNo(ctx, "NOT_EXISTS_ORDER_NO")
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOrderStore_Update(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order := &model.Order{
|
||||
OrderNo: s.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: 500,
|
||||
TotalAmount: 10000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, order, nil))
|
||||
|
||||
order.PaymentMethod = model.PaymentMethodWallet
|
||||
order.PaymentStatus = model.PaymentStatusPaid
|
||||
now := time.Now()
|
||||
order.PaidAt = &now
|
||||
err := s.Update(ctx, order)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, model.PaymentMethodWallet, updated.PaymentMethod)
|
||||
assert.Equal(t, model.PaymentStatusPaid, updated.PaymentStatus)
|
||||
assert.NotNil(t, updated.PaidAt)
|
||||
}
|
||||
|
||||
func TestOrderStore_UpdatePaymentStatus(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
order := &model.Order{
|
||||
OrderNo: s.GenerateOrderNo(),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: 600,
|
||||
TotalAmount: 8000,
|
||||
PaymentStatus: model.PaymentStatusPending,
|
||||
}
|
||||
require.NoError(t, s.Create(ctx, order, nil))
|
||||
|
||||
now := time.Now()
|
||||
err := s.UpdatePaymentStatus(ctx, order.ID, model.PaymentStatusPaid, &now)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := s.GetByID(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, model.PaymentStatusPaid, updated.PaymentStatus)
|
||||
assert.NotNil(t, updated.PaidAt)
|
||||
}
|
||||
|
||||
func TestOrderStore_List(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
ctx := context.Background()
|
||||
|
||||
orders := []*model.Order{
|
||||
{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeSingleCard, BuyerType: model.BuyerTypePersonal, BuyerID: 700, TotalAmount: 1000, PaymentStatus: model.PaymentStatusPending},
|
||||
{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeDevice, BuyerType: model.BuyerTypeAgent, BuyerID: 701, TotalAmount: 2000, PaymentStatus: model.PaymentStatusPaid},
|
||||
{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeSingleCard, BuyerType: model.BuyerTypeAgent, BuyerID: 701, TotalAmount: 3000, PaymentStatus: model.PaymentStatusCancelled},
|
||||
}
|
||||
for _, o := range orders {
|
||||
require.NoError(t, s.Create(ctx, o, nil))
|
||||
}
|
||||
|
||||
t.Run("查询所有订单", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.GreaterOrEqual(t, len(result), 3)
|
||||
})
|
||||
|
||||
t.Run("按支付状态过滤", func(t *testing.T) {
|
||||
filters := map[string]any{"payment_status": model.PaymentStatusPending}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, o := range result {
|
||||
assert.Equal(t, model.PaymentStatusPending, o.PaymentStatus)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按订单类型过滤", func(t *testing.T) {
|
||||
filters := map[string]any{"order_type": model.OrderTypeDevice}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
for _, o := range result {
|
||||
assert.Equal(t, model.OrderTypeDevice, o.OrderType)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按买家过滤", func(t *testing.T) {
|
||||
filters := map[string]any{"buyer_type": model.BuyerTypeAgent, "buyer_id": uint(701)}
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(2))
|
||||
for _, o := range result {
|
||||
assert.Equal(t, model.BuyerTypeAgent, o.BuyerType)
|
||||
assert.Equal(t, uint(701), o.BuyerID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("分页查询", func(t *testing.T) {
|
||||
result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 2}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.LessOrEqual(t, len(result), 2)
|
||||
})
|
||||
|
||||
t.Run("默认分页选项", func(t *testing.T) {
|
||||
result, _, err := s.List(ctx, nil, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOrderStore_GenerateOrderNo(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
s := NewOrderStore(tx, rdb)
|
||||
|
||||
orderNo1 := s.GenerateOrderNo()
|
||||
orderNo2 := s.GenerateOrderNo()
|
||||
|
||||
assert.True(t, len(orderNo1) > 0)
|
||||
assert.True(t, len(orderNo1) <= 30)
|
||||
assert.Contains(t, orderNo1, "ORD")
|
||||
assert.NotEqual(t, orderNo1, orderNo2)
|
||||
}
|
||||
Reference in New Issue
Block a user