feat(listings): 排序交互升级为双向翻转并对齐双端体验

- PC端交互:HomeView 排序按钮支持点击切换升降序翻转(比例、哈夫币、AWM数量、合并价格为单个价格项),并带有高低方向箭头(▲/▼)高亮指示
- 移动端交互:MobileHomeView 菜单提供清晰的双向文案(如「比例从高到低 / 比例从低到高」),且点击当前选中项时支持自动翻转方向,顶部工具栏标题紧凑对齐
- 后端排序:public_filter 与 public_query 完整支持 ratioDesc/ratioAsc、priceAsc/priceDesc、coinDesc/coinAsc、awmDesc/awmAsc
- 验证方式:新增并运行 TestSortPublicListingsByRatio 包含升序/降序多断言,全量运行 ./scripts/check.sh 门禁全绿通过
This commit is contained in:
yml2213
2026-10-10 17:44:27 +08:00
parent 180a21c048
commit 5b2f36ac36
5 changed files with 183 additions and 24 deletions
@@ -103,6 +103,8 @@ func sortPublicListings(items []ListingDTO, sortKey string) {
return a.PriceCent > b.PriceCent
case "coinDesc":
return a.HafCoinAmount > b.HafCoinAmount
case "coinAsc":
return a.HafCoinAmount < b.HafCoinAmount
case "ratioDesc", "ratio":
aRatio := ratioFromListing(a)
bRatio := ratioFromListing(b)
@@ -124,6 +126,13 @@ func sortPublicListings(items []ListingDTO, sortKey string) {
return aAmmo > bAmmo
}
return a.HafCoinAmount > b.HafCoinAmount
case "awmAsc":
aAmmo := resourceQuantity(a.AssetSummary, "awmAmmo")
bAmmo := resourceQuantity(b.AssetSummary, "awmAmmo")
if aAmmo != bAmmo {
return aAmmo < bAmmo
}
return a.HafCoinAmount < b.HafCoinAmount
case "published":
return publicRecentLess(a, b)
case "recommended", "comprehensive", "":
@@ -208,13 +208,25 @@ func TestSortPublicListingsByRatio(t *testing.T) {
sortPublicListings(items, "ratioDesc")
// 预期顺序:ID=2 (45.0) > ID=4 (35.0, 100M 币更多) > ID=3 (35.0, 80M) > ID=1 (20.0)
// 预期降序:ID=2 (45.0) > ID=4 (35.0, 100M 币更多) > ID=3 (35.0, 80M) > ID=1 (20.0)
expectedIDs := []uint64{2, 4, 3, 1}
for i, expected := range expectedIDs {
if items[i].ID != expected {
t.Fatalf("at index %d, expected listing %d, got %d (ratio=%.1f)", i, expected, items[i].ID, ratioFromListing(items[i]))
}
}
sortPublicListings(items, "ratioAsc")
// 预期升序:ID=1 (20.0) < ID=3 (35.0) < ID=4 (35.0) < ID=2 (45.0)
if items[0].ID != 1 || items[len(items)-1].ID != 2 {
t.Fatalf("expected ratioAsc items[0].ID=1 and items[last].ID=2, got order: %v", []uint64{items[0].ID, items[1].ID, items[2].ID, items[3].ID})
}
sortPublicListings(items, "coinAsc")
if items[0].HafCoinAmount > items[len(items)-1].HafCoinAmount {
t.Fatalf("expected coinAsc first item coin <= last item coin")
}
}
func TestIsNightAvailableSummaryUsesEarlyMorningResponse(t *testing.T) {
@@ -146,7 +146,7 @@ func canListPublicWithSQL(query PublicListQuery) bool {
return false
}
switch query.Sort {
case "", "published", "recommended", "comprehensive", "priceAsc", "priceDesc", "coinDesc":
case "", "published", "recommended", "comprehensive", "priceAsc", "priceDesc", "coinDesc", "coinAsc":
return true
default:
return false
@@ -161,6 +161,8 @@ func applyPublicSQLSort(db *gorm.DB, sortKey string) *gorm.DB {
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 "coinAsc":
return db.Order("a.haf_coin_amount ASC, l.published_at DESC, l.id DESC")
case "published":
return db.Order("l.published_at DESC, l.id DESC")
default: