package listing import ( "context" "fmt" "strings" "time" "gorm.io/datatypes" "gorm.io/gorm" ) func (r *Repository) ListPublic(ctx context.Context, query PublicListQuery) (*PublicListResult, error) { page, pageSize := normalizedPublicPage(query) if canListPublicWithSQL(query) { return r.listPublicPage(ctx, query, page, pageSize) } db := r.baseQuery(ctx). Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false) if query.DistributionSiteCode != "" { db = db.Joins("INNER JOIN distribution_site_accounts AS dsa ON dsa.listing_id = l.id AND dsa.display_status = 'enabled'"). Joins("INNER JOIN distribution_sites AS ds ON ds.id = dsa.site_id AND ds.site_code = ? AND ds.status = 'active' AND ds.display_account_enabled = ?", query.DistributionSiteCode, true) } var rows []listingRow err := db.Order("l.published_at DESC, l.id DESC"). Scan(&rows).Error if err != nil { return nil, err } items := publicListings(rowsToDTO(rows)) if err := r.applyShelfMarkup(ctx, query.DistributionSiteCode, items); err != nil { return nil, err } baseQuery := query baseQuery.Zone = "" items = filterPublicListings(items, baseQuery) zoneCounts := publicZoneCounts(items) if query.Zone != "" && query.Zone != "all" { items = filterPublicListings(items, query) } sortPublicListings(items, query.Sort) total := int64(len(items)) start := (page - 1) * pageSize if start < 0 { start = 0 } if start >= len(items) { items = []ListingDTO{} } else { end := start + pageSize if end > len(items) { end = len(items) } items = items[start:end] } return &PublicListResult{ Items: items, Total: total, Page: page, PageSize: pageSize, ZoneCounts: zoneCounts, }, nil } 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"). Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false). Count(&total).Error; err != nil { return nil, err } var rows []listingRow offset := (page - 1) * pageSize err := applyPublicSQLSort(r.baseQuery(ctx), query.Sort). Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false). Limit(pageSize). Offset(offset). Scan(&rows).Error if err != nil { return nil, err } zoneCounts, err := r.publicZoneCountsCached(ctx) if err != nil { return nil, err } return &PublicListResult{ Items: publicListings(rowsToDTO(rows)), Total: total, Page: page, PageSize: pageSize, ZoneCounts: zoneCounts, }, nil } func normalizedPublicPage(query PublicListQuery) (int, int) { page := query.Page if page <= 0 { page = 1 } pageSize := query.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 50 { pageSize = 50 } return page, pageSize } func canListPublicWithSQL(query PublicListQuery) bool { if query.DistributionSiteCode != "" { return false } if query.Keyword != "" { return false } if query.Zone != "" && query.Zone != "all" { return false } if len(query.Server) > 0 || len(query.Region) > 0 || len(query.LoginMethod) > 0 || len(query.Rank) > 0 { return false } if len(query.Insurance) > 0 || len(query.Stamina) > 0 || len(query.Load) > 0 { return false } if len(query.SkinGroup) > 0 || len(query.SkinName) > 0 || len(query.ResourceRanges) > 0 { return false } if query.MinCoin != nil || query.MaxCoin != nil || query.MinRatio != nil || query.MaxRatio != nil { return false } if query.MinPrice != nil || query.MaxPrice != nil { return false } if query.MinDeposit != nil || query.MaxDeposit != nil || query.MinTotal != nil || query.MaxTotal != nil { return false } if query.MinFireLevel != nil || query.MaxFireLevel != nil || query.MinSecretKD != nil || query.MaxSecretKD != nil { return false } if query.MinDailyLoss != nil || query.MaxDailyLoss != nil { return false } switch query.Sort { case "", "published", "recommended", "comprehensive", "priceAsc", "priceDesc", "coinDesc": return true default: return false } } func applyPublicSQLSort(db *gorm.DB, sortKey string) *gorm.DB { switch sortKey { case "priceAsc": return db.Order("l.price_cent ASC, l.published_at DESC, l.id DESC") case "priceDesc": return db.Order("l.price_cent DESC, l.published_at DESC, l.id DESC") case "coinDesc": return db.Order("a.haf_coin_amount DESC, l.published_at DESC, l.id DESC") case "published": return db.Order("l.published_at DESC, l.id DESC") default: return db.Order(fmt.Sprintf("((l.id * 1103515245 + %d) %% 2147483647) ASC, l.id ASC", publicShuffleSeed())) } } func publicShuffleSeed() int64 { now := time.Now().UTC() return int64(now.Year()*366 + now.YearDay()) } func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]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 } var rows []publicZoneRow err := r.db.WithContext(ctx).Table("rental_listings AS l"). Select("a.login_platform, a.haf_coin_amount, a.asset_summary"). Joins("JOIN game_accounts AS a ON a.id = l.account_id"). Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false). Scan(&rows).Error if err != nil { return nil, err } counts := map[string]int64{ "all": int64(len(rows)), "sale": 0, "gift": 0, "night": 0, "password": 0, "highCoin": 0, } for _, row := range rows { summary := decodeAssetSummary(row.AssetSummary) if isAcceleratedSale(summary) { counts["sale"]++ } if hasGiftResourcesSummary(summary) { counts["gift"]++ } if isNightAvailableSummary(summary) { counts["night"]++ } if strings.Contains(row.LoginPlatform, "账密") || strings.Contains(row.LoginPlatform, "账号密码") { counts["password"]++ } if float64(row.HafCoinAmount)/1000000 >= 100 { counts["highCoin"]++ } } r.publicZoneCounts = publicZoneCountCache{ Counts: counts, ExpiresAt: now.Add(publicZoneCountCacheTTL), } return copyPublicZoneCounts(counts), nil } func copyPublicZoneCounts(counts map[string]int64) map[string]int64 { copied := make(map[string]int64, len(counts)) for key, value := range counts { copied[key] = value } return copied } func (r *Repository) FindPublic(ctx context.Context, id uint64, siteCode string) (*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 } applyPublicListingURLs(dto) markups, err := r.shelfMarkupByListingID(ctx, siteCode, []uint64{dto.ID}) if err != nil { return nil, err } dto.PriceCent += markups[dto.ID] return dto, nil } func (r *Repository) applyShelfMarkup(ctx context.Context, siteCode string, items []ListingDTO) error { if siteCode == "" || len(items) == 0 { return nil } ids := make([]uint64, 0, len(items)) for _, item := range items { ids = append(ids, item.ID) } markups, err := r.shelfMarkupByListingID(ctx, siteCode, ids) if err != nil { return err } for i := range items { items[i].PriceCent += markups[items[i].ID] } return nil } func (r *Repository) shelfMarkupByListingID(ctx context.Context, siteCode string, listingIDs []uint64) (map[uint64]int64, error) { result := make(map[uint64]int64, len(listingIDs)) if siteCode == "" || len(listingIDs) == 0 { return result, nil } type markupRow struct { ListingID uint64 `gorm:"column:listing_id"` MarkupAmountCent int64 `gorm:"column:markup_amount_cent"` } var rows []markupRow if err := r.db.WithContext(ctx).Table("distribution_site_accounts AS dsa"). Select("dsa.listing_id, COALESCE(dsa.markup_amount_cent, 0) AS markup_amount_cent"). Joins("INNER JOIN distribution_sites AS ds ON ds.id = dsa.site_id AND ds.site_code = ? AND ds.status = 'active'", siteCode). Where("dsa.display_status = 'enabled' AND dsa.listing_id IN ?", listingIDs). Scan(&rows).Error; err != nil { return nil, err } for _, row := range rows { if row.MarkupAmountCent > 0 { result[row.ListingID] = row.MarkupAmountCent } } return result, nil } func (r *Repository) FindPublicCoverKey(ctx context.Context, id uint64) (string, error) { return r.FindPublicScreenshotKey(ctx, id, 0) } func (r *Repository) FindPublicScreenshotKey(ctx context.Context, id uint64, index int) (string, error) { if index < 0 { return "", gorm.ErrRecordNotFound } 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 "", err } if index >= len(dto.ScreenshotURLS) { return "", gorm.ErrRecordNotFound } if key := extractListingObjectKey(dto.ScreenshotURLS[index]); key != "" { return key, nil } return "", gorm.ErrRecordNotFound } type publicZoneRow struct { LoginPlatform string HafCoinAmount int64 AssetSummary datatypes.JSON `gorm:"column:asset_summary"` }