补齐相关日志日志
This commit is contained in:
@@ -103,6 +103,10 @@ type EffectiveQuota struct {
|
||||
func getUserVIPExpire(ctx context.Context, userID int64) int64 {
|
||||
user, err := mysqldao.NewUserDao().UserGetByID(ctx, userID)
|
||||
if err != nil {
|
||||
// 查询失败一律视为非会员(跟随 VIP 的凭证将按失效处理),必须留痕便于排查额度差异。
|
||||
if !isNotFound(err) {
|
||||
slog.WarnContext(ctx, "get user vip expire failed, treat as non-vip", "user_id", userID, "error", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return user.VIPExpire
|
||||
@@ -140,6 +144,8 @@ func getEffectiveQuota(ctx context.Context, userID int64, quotaType int8) (*Effe
|
||||
vipExpire := getUserVIPExpire(ctx, userID)
|
||||
grants, err := listValidGrants(ctx, userID, quotaType, vipExpire, now)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "get effective quota: list valid grants failed",
|
||||
"user_id", userID, "quota_type", quotaType, "error", err)
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
|
||||
@@ -154,6 +160,9 @@ func getEffectiveQuota(ctx context.Context, userID int64, quotaType int8) (*Effe
|
||||
freeUsed := int64(0)
|
||||
if q, qerr := mysqldao.NewQuotaDao().QuotaGetByUserIDAndType(ctx, userID, quotaType); qerr == nil {
|
||||
freeUsed = q.Used
|
||||
} else if !isNotFound(qerr) {
|
||||
slog.WarnContext(ctx, "get effective quota: query free quota failed, treat as 0",
|
||||
"user_id", userID, "quota_type", quotaType, "error", qerr)
|
||||
}
|
||||
total := freeTotal + grantTotal
|
||||
used := freeUsed + grantUsed
|
||||
@@ -329,7 +338,7 @@ func consumeCosQuota(ctx context.Context, userID int64, n int64, relatedID int64
|
||||
}
|
||||
|
||||
// 3. 写消费流水(old/new 记录 Cos 有效剩余)。
|
||||
before := cosRemainTx(tx, userID, vipExpire, now)
|
||||
before := cosRemainTx(ctx, tx, userID, vipExpire, now)
|
||||
flow := &mysqlmodel.QuotaFlowLog{
|
||||
QuotaFlowUUID: genUUID(),
|
||||
UserID: userID,
|
||||
@@ -351,13 +360,15 @@ func consumeCosQuota(ctx context.Context, userID int64, n int64, relatedID int64
|
||||
if errors.Is(err, errcode.QuotaInsufficient) {
|
||||
return errcode.QuotaInsufficient
|
||||
}
|
||||
slog.ErrorContext(ctx, "consume cos quota: transaction failed",
|
||||
"user_id", userID, "amount", n, "related_id", relatedID, "error", err)
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cosRemainTx 在事务内计算 Cos 有效剩余(免费剩余 + 有效凭证剩余)。
|
||||
func cosRemainTx(tx *gorm.DB, userID int64, vipExpire, now int64) int64 {
|
||||
func cosRemainTx(ctx context.Context, tx *gorm.DB, userID int64, vipExpire, now int64) int64 {
|
||||
freeRemain := int64(0)
|
||||
var freeQ mysqlmodel.Quota
|
||||
if err := tx.Where("user_id = ? AND quota_type = ?", userID, QuotaTypeCos).First(&freeQ).Error; err == nil {
|
||||
@@ -372,7 +383,7 @@ func cosRemainTx(tx *gorm.DB, userID int64, vipExpire, now int64) int64 {
|
||||
q = q.Where("expire_time > ?", now)
|
||||
}
|
||||
if err := q.Select("COALESCE(SUM(amount - used), 0)").Scan(&grantRemain).Error; err != nil {
|
||||
slog.WarnContext(context.Background(), "calc cos grant remain failed", "user_id", userID, "error", err)
|
||||
slog.WarnContext(ctx, "calc cos grant remain failed", "user_id", userID, "error", err)
|
||||
}
|
||||
if freeRemain < 0 {
|
||||
freeRemain = 0
|
||||
@@ -382,7 +393,7 @@ func cosRemainTx(tx *gorm.DB, userID int64, vipExpire, now int64) int64 {
|
||||
|
||||
// refundCosQuotaTx 在事务内回退 Cos 额度并写流水。
|
||||
// 回退策略(后进先出):优先回退免费额度(最后扣减的),不足再回退凭证(最后到期优先)。
|
||||
func refundCosQuotaTx(tx *gorm.DB, userID int64, vipExpire int64, n int64, relatedID int64, reason string) error {
|
||||
func refundCosQuotaTx(ctx context.Context, tx *gorm.DB, userID int64, vipExpire int64, n int64, relatedID int64, reason string) error {
|
||||
now := nowUnix()
|
||||
|
||||
remain := n
|
||||
@@ -431,10 +442,10 @@ func refundCosQuotaTx(tx *gorm.DB, userID int64, vipExpire int64, n int64, relat
|
||||
flowReason := reason
|
||||
if refunded < n {
|
||||
flowReason = fmt.Sprintf("%s(实际回退 %d/%d,可用已退额度不足)", reason, refunded, n)
|
||||
slog.WarnContext(context.Background(), "refund cos quota partially applied",
|
||||
slog.WarnContext(ctx, "refund cos quota partially applied",
|
||||
"user_id", userID, "want", n, "refunded", refunded, "related_id", relatedID)
|
||||
}
|
||||
after := cosRemainTx(tx, userID, vipExpire, now)
|
||||
after := cosRemainTx(ctx, tx, userID, vipExpire, now)
|
||||
flow := &mysqlmodel.QuotaFlowLog{
|
||||
QuotaFlowUUID: genUUID(),
|
||||
UserID: userID,
|
||||
@@ -456,8 +467,9 @@ func refundCosQuotaTx(tx *gorm.DB, userID int64, vipExpire int64, n int64, relat
|
||||
func refundCosQuota(ctx context.Context, userID int64, n int64, relatedID int64, reason string) {
|
||||
vipExpire := getUserVIPExpire(ctx, userID)
|
||||
if err := global.MysqlEngine.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return refundCosQuotaTx(tx, userID, vipExpire, n, relatedID, reason)
|
||||
return refundCosQuotaTx(ctx, tx, userID, vipExpire, n, relatedID, reason)
|
||||
}); err != nil {
|
||||
slog.WarnContext(ctx, "refund cos quota failed", "user_id", userID, "error", err)
|
||||
slog.WarnContext(ctx, "refund cos quota failed",
|
||||
"user_id", userID, "amount", n, "related_id", relatedID, "reason", reason, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user