feat: add order idempotency
This commit is contained in:
@@ -9,6 +9,8 @@ type OrderCreateForm struct {
|
|||||||
Quantity int `json:"quantity"`
|
Quantity int `json:"quantity"`
|
||||||
// UserCouponID 用户券ID(可选)。
|
// UserCouponID 用户券ID(可选)。
|
||||||
UserCouponID int64 `json:"user_coupon_id"`
|
UserCouponID int64 `json:"user_coupon_id"`
|
||||||
|
// IdempotencyKey 幂等键(同一业务请求需保持一致)。
|
||||||
|
IdempotencyKey *string `json:"idempotency_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderCreateResponse struct {
|
type OrderCreateResponse struct {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"quyun/v2/app/errorx"
|
"quyun/v2/app/errorx"
|
||||||
@@ -78,6 +79,22 @@ func (s *order) Create(
|
|||||||
uid := userID
|
uid := userID
|
||||||
cid := form.ContentID
|
cid := form.ContentID
|
||||||
|
|
||||||
|
// 幂等控制:相同幂等键直接返回已创建的订单。
|
||||||
|
idempotencyKey := ""
|
||||||
|
if form.IdempotencyKey != nil {
|
||||||
|
idempotencyKey = strings.TrimSpace(*form.IdempotencyKey)
|
||||||
|
}
|
||||||
|
if idempotencyKey != "" {
|
||||||
|
tbl, q := models.OrderQuery.QueryContext(ctx)
|
||||||
|
existing, err := q.Where(tbl.UserID.Eq(uid), tbl.IdempotencyKey.Eq(idempotencyKey)).First()
|
||||||
|
if err == nil {
|
||||||
|
return &transaction_dto.OrderCreateResponse{OrderID: existing.ID}, nil
|
||||||
|
}
|
||||||
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Fetch Content & Price
|
// 1. Fetch Content & Price
|
||||||
content, err := models.ContentQuery.WithContext(ctx).Where(models.ContentQuery.ID.Eq(cid)).First()
|
content, err := models.ContentQuery.WithContext(ctx).Where(models.ContentQuery.ID.Eq(cid)).First()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,9 +140,12 @@ func (s *order) Create(
|
|||||||
AmountDiscount: amountDiscount,
|
AmountDiscount: amountDiscount,
|
||||||
AmountPaid: amountPaid,
|
AmountPaid: amountPaid,
|
||||||
CouponID: couponID,
|
CouponID: couponID,
|
||||||
IdempotencyKey: uuid.NewString(),
|
IdempotencyKey: idempotencyKey,
|
||||||
Snapshot: types.NewJSONType(fields.OrdersSnapshot{}),
|
Snapshot: types.NewJSONType(fields.OrdersSnapshot{}),
|
||||||
}
|
}
|
||||||
|
if order.IdempotencyKey == "" {
|
||||||
|
order.IdempotencyKey = uuid.NewString()
|
||||||
|
}
|
||||||
|
|
||||||
err = models.Q.Transaction(func(tx *models.Query) error {
|
err = models.Q.Transaction(func(tx *models.Query) error {
|
||||||
if err := tx.Order.WithContext(ctx).Create(order); err != nil {
|
if err := tx.Order.WithContext(ctx).Create(order); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user