From 536f39e347a0ab56a282f4d7a49f5bc4c114c509 Mon Sep 17 00:00:00 2001 From: zhaoxing <2558691827@qq.com> Date: Thu, 12 Nov 2026 14:58:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(listings):=20=E6=8A=98=E7=AE=97=E6=AF=94?= =?UTF-8?q?=E6=8C=89=E6=95=B4=E6=95=B0=E5=B1=95=E7=A4=BA=E5=B9=B6=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E7=AD=9B=E9=80=89=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/modules/listing/public_filter.go | 5 ++- .../modules/listing/public_filter_test.go | 25 +++++++++++++++ .../src/features/admin/listingDisplay.test.ts | 6 ++++ frontend/src/features/admin/listingDisplay.ts | 2 +- .../features/listings/ListingDetailPage.tsx | 15 +++------ .../src/features/listings/ListingListPage.tsx | 15 +++------ frontend/src/features/listings/labels.test.ts | 32 +++++++++++++++++++ frontend/src/features/listings/labels.ts | 19 +++++++++++ .../mobile/MobileListingDetailPage.tsx | 11 ++++++- 9 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 frontend/src/features/listings/labels.test.ts diff --git a/backend/internal/modules/listing/public_filter.go b/backend/internal/modules/listing/public_filter.go index 38c62ef..a8606b8 100644 --- a/backend/internal/modules/listing/public_filter.go +++ b/backend/internal/modules/listing/public_filter.go @@ -1,6 +1,7 @@ package listing import ( + "math" "sort" "strconv" "strings" @@ -63,7 +64,9 @@ func matchesPublicQuery(item ListingDTO, query PublicListQuery) bool { if !numberInRange(coinM, NumberRange{Min: query.MinCoin, Max: query.MaxCoin}) { return false } - if !numberInRange(ratioFromListing(item), NumberRange{Min: query.MinRatio, Max: query.MaxRatio}) { + // 折算比在前端按四舍五入取整展示,筛选必须用同一口径, + // 否则会出现「显示 35w 却不在 35-40 区间」的错位。 + if !numberInRange(math.Round(ratioFromListing(item)), NumberRange{Min: query.MinRatio, Max: query.MaxRatio}) { return false } if !numberInRange(price, NumberRange{Min: query.MinPrice, Max: query.MaxPrice}) { diff --git a/backend/internal/modules/listing/public_filter_test.go b/backend/internal/modules/listing/public_filter_test.go index a422210..ad71116 100644 --- a/backend/internal/modules/listing/public_filter_test.go +++ b/backend/internal/modules/listing/public_filter_test.go @@ -21,6 +21,31 @@ func TestFilterPublicListingsByRatio(t *testing.T) { } } +func TestFilterPublicListingsByRoundedRatio(t *testing.T) { + minRatio := 30.0 + maxRatio := 35.0 + items := []ListingDTO{ + {ID: 1, AssetSummary: map[string]any{"publish_ratio": 29.4}}, // 29w,区间外 + {ID: 2, AssetSummary: map[string]any{"publish_ratio": 34.4}}, // 34w,区间内 + {ID: 3, AssetSummary: map[string]any{"publish_ratio": 34.6}}, // 35w,区间内 + {ID: 4, AssetSummary: map[string]any{"publish_ratio": 35.4}}, // 35w,区间内(与展示一致) + {ID: 5, AssetSummary: map[string]any{"publish_ratio": 35.6}}, // 36w,区间外 + // 无 publish_ratio 时按「万币 / 元」兜底计算:34.6w → 35w + {ID: 6, HafCoinAmount: 34_600_000, PriceCent: 10_000}, + } + + filtered := filterPublicListings(items, PublicListQuery{MinRatio: &minRatio, MaxRatio: &maxRatio}) + + if len(filtered) != 4 { + t.Fatalf("expected 4 listings after rounding, got %#v", filtered) + } + for i, wantID := range []uint64{2, 3, 4, 6} { + if filtered[i].ID != wantID { + t.Fatalf("at index %d, expected listing %d, got %d", i, wantID, filtered[i].ID) + } + } +} + func TestFilterPublicListingsRegionMultiSelectRequiresAll(t *testing.T) { items := []ListingDTO{ { diff --git a/frontend/src/features/admin/listingDisplay.test.ts b/frontend/src/features/admin/listingDisplay.test.ts index ee822b3..2f67e4c 100644 --- a/frontend/src/features/admin/listingDisplay.test.ts +++ b/frontend/src/features/admin/listingDisplay.test.ts @@ -118,6 +118,12 @@ describe('admin listing display helpers', () => { expect(listingRatioText(item)).toBe('1:43') }) + it('rounds ratio to an integer', () => { + expect(listingRatioText(listing({ asset_summary: { publish_ratio: 33.8256 } }))).toBe('1:34') + expect(listingRatioText(listing({ asset_summary: { publish_ratio: 33.4 } }))).toBe('1:33') + expect(listingRatioText(listing({ asset_summary: {} }))).toBe('—') + }) + it('falls back safely when fields are absent', () => { const item = listing({ listing_no: '', description: '', asset_summary: null }) expect(listingCodeText(item)).toBe('SP000001') diff --git a/frontend/src/features/admin/listingDisplay.ts b/frontend/src/features/admin/listingDisplay.ts index 46397ad..5369dda 100644 --- a/frontend/src/features/admin/listingDisplay.ts +++ b/frontend/src/features/admin/listingDisplay.ts @@ -148,7 +148,7 @@ export function listingRentDepositText(row: AdminListing) { export function listingRatioText(row: AdminListing) { const ratio = numberOf(row.asset_summary?.publish_ratio) if (ratio <= 0) return '—' - return `1:${compactNumber(ratio)}` + return `1:${Math.round(ratio)}` } export function listingAccountLevel(row: AdminListing) { diff --git a/frontend/src/features/listings/ListingDetailPage.tsx b/frontend/src/features/listings/ListingDetailPage.tsx index e3f65d0..b598b56 100644 --- a/frontend/src/features/listings/ListingDetailPage.tsx +++ b/frontend/src/features/listings/ListingDetailPage.tsx @@ -29,6 +29,7 @@ import { faceOwnerText, gameNameChangeText, listingPriceDetail, + listingRatioText, listingRentalDays, } from '@/features/listings/labels' import { recordListingView, useListingCollections } from '@/features/listings/collections' @@ -126,15 +127,6 @@ function resourcesOf(item: Listing) { .filter(row => row.label && row.quantity > 0) } -function ratioOf(item: Listing) { - const published = Number(item.asset_summary?.publish_ratio ?? 0) - if (published > 0) return `${published}w` - const priceYuan = item.price_cent / 100 - const coinM = item.haf_coin_amount / 1_000_000 - if (priceYuan <= 0 || coinM <= 0) return '' - return `${((coinM / priceYuan) * 100).toFixed(0)}w` -} - function coreStats(item: Listing) { const summary = item.asset_summary ?? {} const dailyLoss = Number(summary.daily_loss_m ?? 0) @@ -143,10 +135,11 @@ function coreStats(item: Listing) { const faceOwner = faceOwnerText(summary) const banRecord = banRecordText(summary) const gameNameChange = gameNameChangeText(summary) + const ratio = listingRatioText(item) return [ { label: '哈夫币', value: formatCoinM(item.haf_coin_amount) }, { label: '保险格数', value: textOf(summary.season_insurance) }, - { label: '折算比', value: ratioOf(item) ? `1 元 ≈ ${ratioOf(item)}` : '' }, + { label: '折算比', value: ratio ? `1 元 ≈ ${ratio}` : '' }, { label: '火力等级', value: textOf(summary.fire_level) }, { label: '体力', value: textOf(summary.stamina_level) }, { label: '负重', value: textOf(summary.load_level) }, @@ -414,7 +407,7 @@ export function ListingDetailPage() {