refactor: deprecate int32 (#7025)

* refactor: deprecate int32

* fix(db): reject legacy user quota schemas at startup

* fix(quota): enforce wallet bounds and saturating billing conversions

* fix(rate-limit): keep count*duration from wrapping int64

* fix: error message
This commit is contained in:
Seefs
2026-08-26 20:57:54 +08:00
committed by GitHub
parent 2d8e50bf36
commit a073f74b38
35 changed files with 445 additions and 116 deletions
+17 -16
View File
@@ -42,11 +42,12 @@ const (
)
var (
ErrPaymentMethodMismatch = errors.New("payment method mismatch")
ErrTopUpNotFound = errors.New("topup not found")
ErrTopUpStatusInvalid = errors.New("topup status invalid")
ErrInvalidTopUpQuota = errors.New("invalid top-up quota")
ErrTopUpQuotaLimitExceeded = errors.New("top-up quota limit exceeded")
ErrPaymentMethodMismatch = errors.New("payment method mismatch")
ErrTopUpNotFound = errors.New("topup not found")
ErrTopUpStatusInvalid = errors.New("topup status invalid")
ErrInvalidTopUpQuota = errors.New("invalid top-up quota")
ErrTopUpQuotaLimitExceeded = errors.New("top-up quota limit exceeded")
ErrWalletQuotaLimitExceeded = errors.New("wallet quota limit exceeded")
)
func (topUp *TopUp) Insert() error {
@@ -56,10 +57,10 @@ func (topUp *TopUp) Insert() error {
}
func topUpQuotaMaxCurrent(creditedQuota int) (int, error) {
if creditedQuota <= 0 || creditedQuota >= common.MaxQuota {
if creditedQuota <= 0 || creditedQuota > common.MaxWalletQuota {
return 0, ErrInvalidTopUpQuota
}
return common.MaxQuota - 1 - creditedQuota, nil
return common.MaxWalletQuota - creditedQuota, nil
}
// ValidateTopUpQuotaCapacity performs the user-facing pre-payment check. The
@@ -81,8 +82,8 @@ func ValidateTopUpQuotaCapacity(userId int, creditedQuota int) error {
return nil
}
// creditTopUpQuota atomically enforces the int32 wallet ceiling while adding
// quota. Keeping the predicate and increment in one UPDATE prevents two
// creditTopUpQuota atomically enforces the wallet ceiling while adding quota.
// Keeping the predicate and increment in one UPDATE prevents two
// concurrent callbacks from both passing a separate read/check.
func creditTopUpQuota(tx *gorm.DB, userId int, creditedQuota int, updates map[string]interface{}) error {
maxCurrentQuota, err := topUpQuotaMaxCurrent(creditedQuota)
@@ -203,7 +204,7 @@ func RechargeEpay(tradeNo string, actualPaymentMethod string, callerIp string) (
topUp.PaymentMethod = actualPaymentMethod
}
var quotaErr error
quotaToAdd, quotaErr = common.QuotaFromDecimalStrict(
quotaToAdd, quotaErr = common.WalletQuotaFromDecimalStrict(
decimal.NewFromInt(topUp.Amount).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
if quotaErr != nil || quotaToAdd <= 0 {
@@ -266,7 +267,7 @@ func Recharge(referenceId string, customerId string, callerIp string) (err error
return err
}
quota, err = common.QuotaFromDecimalStrict(
quota, err = common.WalletQuotaFromDecimalStrict(
decimal.NewFromFloat(topUp.Money).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
if err != nil || quota <= 0 {
@@ -482,11 +483,11 @@ func ManualCompleteTopUp(tradeNo string, callerIp string) error {
// - 其他订单(如易支付):Amount 为美元数量,* QuotaPerUnit
var quotaErr error
if topUp.PaymentProvider == PaymentProviderStripe {
quotaToAdd, quotaErr = common.QuotaFromDecimalStrict(
quotaToAdd, quotaErr = common.WalletQuotaFromDecimalStrict(
decimal.NewFromFloat(topUp.Money).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
} else {
quotaToAdd, quotaErr = common.QuotaFromDecimalStrict(
quotaToAdd, quotaErr = common.WalletQuotaFromDecimalStrict(
decimal.NewFromInt(topUp.Amount).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
}
@@ -556,7 +557,7 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string
}
// Creem 直接使用 Amount 作为充值额度(整数)
quota, err = common.QuotaFromDecimalStrict(decimal.NewFromInt(topUp.Amount))
quota, err = common.WalletQuotaFromDecimalStrict(decimal.NewFromInt(topUp.Amount))
if err != nil || quota <= 0 {
return ErrInvalidTopUpQuota
}
@@ -624,7 +625,7 @@ func RechargeWaffo(tradeNo string, callerIp string) (err error) {
return errors.New("充值订单状态错误")
}
quotaToAdd, err = common.QuotaFromDecimalStrict(
quotaToAdd, err = common.WalletQuotaFromDecimalStrict(
decimal.NewFromInt(topUp.Amount).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
if err != nil || quotaToAdd <= 0 {
@@ -684,7 +685,7 @@ func RechargeWaffoPancake(tradeNo string) (err error) {
return errors.New("充值订单状态错误")
}
quotaToAdd, err = common.QuotaFromDecimalStrict(
quotaToAdd, err = common.WalletQuotaFromDecimalStrict(
decimal.NewFromInt(topUp.Amount).Mul(decimal.NewFromFloat(common.QuotaPerUnit)),
)
if err != nil || quotaToAdd <= 0 {