feat(user): 支持后台调整等级并限制极品专区可见性

This commit is contained in:
yml2213
2026-11-05 12:02:48 +08:00
parent 09f244a93d
commit 0d558bdce0
35 changed files with 934 additions and 131 deletions
@@ -100,8 +100,18 @@ func applyUserFilter(tx *gorm.DB, query ListQuery, growthConfig rentergrowth.Con
tx = tx.Where("u.status = ?", query.Status)
}
if query.HasDepositFreeQuota {
quotaLevelCodes := growthQuotaLevelCodes(growthConfig)
if minPoints, ok := minimumGrowthQuotaPoints(growthConfig); ok {
tx = tx.Where("u.deposit_free_quota_cent > 0 OR u.renter_growth_points >= ?", minPoints)
tx = tx.Where(
"u.deposit_free_quota_cent > 0 OR u.renter_growth_points >= ? OR u.renter_growth_level_override IN ?",
minPoints,
quotaLevelCodes,
)
} else if len(quotaLevelCodes) > 0 {
tx = tx.Where(
"u.deposit_free_quota_cent > 0 OR u.renter_growth_level_override IN ?",
quotaLevelCodes,
)
} else {
tx = tx.Where("u.deposit_free_quota_cent > 0")
}
@@ -125,6 +135,16 @@ func applyUserFilter(tx *gorm.DB, query ListQuery, growthConfig rentergrowth.Con
return tx
}
func growthQuotaLevelCodes(cfg rentergrowth.Config) []string {
codes := make([]string, 0, len(cfg.Levels))
for _, level := range cfg.Levels {
if level.DepositFreeQuotaCent > 0 {
codes = append(codes, level.Code)
}
}
return codes
}
func minimumGrowthQuotaPoints(cfg rentergrowth.Config) (int64, bool) {
if !cfg.Enabled {
return 0, false
@@ -198,9 +218,9 @@ func (r *Repository) AdjustGrowthPoints(ctx context.Context, adminID uint64, use
if afterAvailable < 0 {
afterAvailable = 0
}
beforeLevel := rentergrowth.LevelForPoints(cfg, beforePoints)
afterLevel := rentergrowth.LevelForPoints(cfg, req.TargetPoints)
if !cfg.Enabled {
beforeLevel := rentergrowth.EffectiveLevelForUser(cfg, beforePoints, user.RenterGrowthLevelOverride)
afterLevel := rentergrowth.LevelForUserAfterPoints(cfg, req.TargetPoints, user.RenterGrowthLevelOverride)
if !cfg.Enabled && user.RenterGrowthLevelOverride == "" {
beforeLevel = rentergrowth.LevelForPoints(cfg, 0)
afterLevel = beforeLevel
}
@@ -245,6 +265,56 @@ func (r *Repository) AdjustGrowthPoints(ctx context.Context, adminID uint64, use
return r.Find(ctx, userID)
}
func (r *Repository) AdjustGrowthLevel(ctx context.Context, adminID uint64, userID uint64, req GrowthLevelAdjustRequest, meta AuditMeta) (*UserDTO, error) {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var user model.User
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&user, userID).Error; err != nil {
return err
}
cfg, err := rentergrowth.ConfigForTx(tx)
if err != nil {
return err
}
targetLevel := strings.TrimSpace(req.TargetLevel)
beforeOverride := user.RenterGrowthLevelOverride
beforeLevel := rentergrowth.EffectiveLevelForUser(cfg, user.RenterGrowthPoints, beforeOverride)
if beforeOverride == "" && !cfg.Enabled {
beforeLevel = rentergrowth.LevelForPoints(cfg, 0)
}
afterLevel := rentergrowth.LevelForPoints(cfg, user.RenterGrowthPoints)
if targetLevel != "" {
level, ok := rentergrowth.LevelForCode(cfg, targetLevel)
if !ok {
return ErrInvalidGrowthLevel
}
afterLevel = level
} else if !cfg.Enabled {
afterLevel = rentergrowth.LevelForPoints(cfg, 0)
}
if targetLevel == beforeOverride && user.RenterGrowthLevel == afterLevel.Code {
return ErrGrowthLevelUnchanged
}
user.RenterGrowthLevelOverride = targetLevel
user.RenterGrowthLevel = afterLevel.Code
if err := tx.Save(&user).Error; err != nil {
return err
}
return appendAuditLog(tx, adminID, "admin_user.adjust_growth_level", user.ID, meta, map[string]any{
"user_id": user.ID,
"before_level": beforeLevel.Code,
"after_level": afterLevel.Code,
"before_override": beforeOverride,
"after_override": targetLevel,
"reason": req.Reason,
})
})
if err != nil {
return nil, err
}
return r.Find(ctx, userID)
}
func (r *Repository) updateStatus(ctx context.Context, adminID uint64, userID uint64, status string, riskStatus string, action string, reason string, meta AuditMeta) (*UserDTO, error) {
statusChanged := false
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
@@ -548,9 +618,13 @@ type userRow struct {
}
func (row userRow) toDTO(growthConfig rentergrowth.Config) UserDTO {
level := rentergrowth.LevelForPoints(growthConfig, row.RenterGrowthPoints)
level := rentergrowth.EffectiveLevelForUser(
growthConfig,
row.RenterGrowthPoints,
row.RenterGrowthLevelOverride,
)
levelQuotaCent := level.DepositFreeQuotaCent
if !growthConfig.Enabled {
if !growthConfig.Enabled && row.RenterGrowthLevelOverride == "" {
level = rentergrowth.LevelForPoints(growthConfig, 0)
levelQuotaCent = 0
}
@@ -576,6 +650,8 @@ func (row userRow) toDTO(growthConfig rentergrowth.Config) UserDTO {
PointsAvailable: row.PointsAvailable,
RenterGrowthLevel: level.Code,
RenterGrowthLevelName: level.Name,
RenterGrowthLevelOverride: row.RenterGrowthLevelOverride,
RenterGrowthLevelManual: row.RenterGrowthLevelOverride != "",
AvailableBalanceCent: row.AvailableBalanceCent,
FrozenBalanceCent: row.FrozenBalanceCent,
QualityOwnerStatus: row.QualityOwnerStatus,