fix: harden concurrent quota and status updates
This commit is contained in:
+13
-3
@@ -600,6 +600,12 @@ func CompleteSubscriptionOrder(tradeNo string, providerPayload string, expectedP
|
||||
if !plan.Enabled {
|
||||
// still allow completion for already purchased orders
|
||||
}
|
||||
// 锁定用户行:并发完成同一用户的不同订单(包括多实例部署下)时,
|
||||
// 使 CreateUserSubscriptionFromPlanTx 的 MaxPurchasePerUser 检查按用户串行。
|
||||
var userRow User
|
||||
if err := lockForUpdate(tx).Select("id").Where("id = ?", order.UserId).First(&userRow).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
subscription, err := CreateUserSubscriptionFromPlanTx(tx, order.UserId, plan, "order")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -712,6 +718,11 @@ func AdminBindSubscription(userId int, planId int, sourceNote string) (string, e
|
||||
}
|
||||
groupChanged := false
|
||||
err = DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 与 CompleteSubscriptionOrder 一致:先锁用户行,再做购买次数检查。
|
||||
var userRow User
|
||||
if err := lockForUpdate(tx).Select("id").Where("id = ?", userId).First(&userRow).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
subscription, err := CreateUserSubscriptionFromPlanTx(tx, userId, plan, "admin")
|
||||
if err == nil {
|
||||
groupChanged = subscription.PrevUserGroup != ""
|
||||
@@ -737,9 +748,8 @@ func calcSubscriptionBalanceQuota(priceAmount float64) (int, error) {
|
||||
}
|
||||
quota := decimal.NewFromFloat(priceAmount).
|
||||
Mul(decimal.NewFromFloat(common.QuotaPerUnit)).
|
||||
Ceil().
|
||||
IntPart()
|
||||
return int(quota), nil
|
||||
Ceil()
|
||||
return common.QuotaFromDecimalStrict(quota)
|
||||
}
|
||||
|
||||
// PurchaseSubscriptionWithBalance creates a subscription by deducting the user's wallet quota.
|
||||
|
||||
Reference in New Issue
Block a user