feat: add order idempotency

This commit is contained in:
2026-01-08 16:31:51 +08:00
parent b6c661fb3d
commit 8ac82aaeb0
2 changed files with 23 additions and 1 deletions

View File

@@ -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 {

View File

@@ -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 {