feat(user): 支持后台调整等级并限制极品专区可见性
This commit is contained in:
@@ -6,11 +6,19 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (r *Repository) ListPublic(ctx context.Context, query PublicListQuery) (*PublicListResult, error) {
|
||||
canViewPremium, err := r.canViewPremium(ctx, query.ViewerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.CanViewPremium = canViewPremium
|
||||
page, pageSize := normalizedPublicPage(query)
|
||||
if canListPublicWithSQL(query) {
|
||||
return r.listPublicPage(ctx, query, page, pageSize)
|
||||
@@ -25,7 +33,8 @@ func (r *Repository) ListPublic(ctx context.Context, query PublicListQuery) (*Pu
|
||||
}
|
||||
|
||||
var rows []listingRow
|
||||
err := db.Order("l.published_at DESC, l.id DESC").
|
||||
db = applyPremiumVisibility(db, query)
|
||||
err = db.Order("l.published_at DESC, l.id DESC").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,7 +76,7 @@ func (r *Repository) ListPublic(ctx context.Context, query PublicListQuery) (*Pu
|
||||
|
||||
func (r *Repository) listPublicPage(ctx context.Context, query PublicListQuery, page int, pageSize int) (*PublicListResult, error) {
|
||||
var total int64
|
||||
if err := r.db.WithContext(ctx).Table("rental_listings AS l").
|
||||
if err := applyPremiumVisibility(r.db.WithContext(ctx).Table("rental_listings AS l"), query).
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -75,7 +84,7 @@ func (r *Repository) listPublicPage(ctx context.Context, query PublicListQuery,
|
||||
|
||||
var rows []listingRow
|
||||
offset := (page - 1) * pageSize
|
||||
err := applyPublicSQLSort(r.baseQuery(ctx), query.Sort).
|
||||
err := applyPublicSQLSort(applyPremiumVisibility(r.baseQuery(ctx), query), query.Sort).
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
@@ -83,10 +92,14 @@ func (r *Repository) listPublicPage(ctx context.Context, query PublicListQuery,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zoneCounts, err := r.publicZoneCountsCached(ctx)
|
||||
zoneCounts, nonPremiumTotal, err := r.publicZoneCountsCached(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !query.CanViewPremium {
|
||||
zoneCounts["all"] = nonPremiumTotal
|
||||
zoneCounts["premium"] = 0
|
||||
}
|
||||
return &PublicListResult{
|
||||
Items: publicListings(rowsToDTO(rows)),
|
||||
Total: total,
|
||||
@@ -96,6 +109,13 @@ func (r *Repository) listPublicPage(ctx context.Context, query PublicListQuery,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func applyPremiumVisibility(tx *gorm.DB, query PublicListQuery) *gorm.DB {
|
||||
if query.CanViewPremium {
|
||||
return tx
|
||||
}
|
||||
return tx.Where("l.is_premium = ?", false)
|
||||
}
|
||||
|
||||
func normalizedPublicPage(query PublicListQuery) (int, int) {
|
||||
page := query.Page
|
||||
if page <= 0 {
|
||||
@@ -175,12 +195,12 @@ func publicShuffleSeed() int64 {
|
||||
return int64(now.Year()*366 + now.YearDay())
|
||||
}
|
||||
|
||||
func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int64, error) {
|
||||
func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int64, int64, error) {
|
||||
now := time.Now()
|
||||
r.publicZoneCountsMu.Lock()
|
||||
defer r.publicZoneCountsMu.Unlock()
|
||||
if r.publicZoneCounts.Counts != nil && now.Before(r.publicZoneCounts.ExpiresAt) {
|
||||
return copyPublicZoneCounts(r.publicZoneCounts.Counts), nil
|
||||
return copyPublicZoneCounts(r.publicZoneCounts.Counts), r.publicZoneCounts.NonPremiumTotal, nil
|
||||
}
|
||||
|
||||
var rows []publicZoneRow
|
||||
@@ -190,7 +210,7 @@ func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
counts := map[string]int64{
|
||||
"all": int64(len(rows)),
|
||||
@@ -201,6 +221,7 @@ func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int
|
||||
"highCoin": 0,
|
||||
"premium": 0,
|
||||
}
|
||||
var nonPremiumTotal int64
|
||||
for _, row := range rows {
|
||||
summary := decodeAssetSummary(row.AssetSummary)
|
||||
if isAcceleratedSale(summary) {
|
||||
@@ -220,13 +241,16 @@ func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int
|
||||
}
|
||||
if row.IsPremium {
|
||||
counts["premium"]++
|
||||
} else {
|
||||
nonPremiumTotal++
|
||||
}
|
||||
}
|
||||
r.publicZoneCounts = publicZoneCountCache{
|
||||
Counts: counts,
|
||||
ExpiresAt: now.Add(publicZoneCountCacheTTL),
|
||||
Counts: counts,
|
||||
NonPremiumTotal: nonPremiumTotal,
|
||||
ExpiresAt: now.Add(publicZoneCountCacheTTL),
|
||||
}
|
||||
return copyPublicZoneCounts(counts), nil
|
||||
return copyPublicZoneCounts(counts), nonPremiumTotal, nil
|
||||
}
|
||||
|
||||
func copyPublicZoneCounts(counts map[string]int64) map[string]int64 {
|
||||
@@ -237,11 +261,20 @@ func copyPublicZoneCounts(counts map[string]int64) map[string]int64 {
|
||||
return copied
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublic(ctx context.Context, id uint64, siteCode string) (*ListingDTO, error) {
|
||||
func (r *Repository) FindPublic(ctx context.Context, id uint64, siteCode string, viewerUserID uint64) (*ListingDTO, error) {
|
||||
dto, err := r.findDTO(ctx, "l.id = ? AND l.status = ? AND l.review_status = ? AND l.in_transaction = ?", id, "published", "approved", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dto.IsPremium {
|
||||
canView, err := r.canViewPremium(ctx, viewerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !canView {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
}
|
||||
applyPublicListingURLs(dto)
|
||||
items := []ListingDTO{*dto}
|
||||
if err := r.applyShelfMarkup(ctx, siteCode, items); err != nil {
|
||||
@@ -251,6 +284,30 @@ func (r *Repository) FindPublic(ctx context.Context, id uint64, siteCode string)
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) canViewPremium(ctx context.Context, userID uint64) (bool, error) {
|
||||
if userID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
var user model.User
|
||||
if err := r.db.WithContext(ctx).
|
||||
Select("id, renter_growth_points, renter_growth_level_override").
|
||||
First(&user, userID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
cfg, err := rentergrowth.ConfigForTx(r.db.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
level := rentergrowth.EffectiveLevelForUser(cfg, user.RenterGrowthPoints, user.RenterGrowthLevelOverride)
|
||||
if !cfg.Enabled && user.RenterGrowthLevelOverride == "" {
|
||||
level = rentergrowth.LevelForPoints(cfg, 0)
|
||||
}
|
||||
return rentergrowth.CanViewPremiumLevel(level.Code), nil
|
||||
}
|
||||
|
||||
func (r *Repository) applyShelfMarkup(ctx context.Context, siteCode string, items []ListingDTO) error {
|
||||
if siteCode == "" || len(items) == 0 {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user