修复卖家商品价格展示
This commit is contained in:
@@ -378,7 +378,7 @@ func (r *Repository) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return rowsToDTO(rows), nil
|
return sellerListings(rowsToDTO(rows)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
||||||
@@ -412,7 +412,12 @@ func (r *Repository) FindPublicScreenshotKey(id uint64, index int) (string, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
func (r *Repository) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||||
return r.findDTO("l.id = ? AND l.owner_id = ?", id, ownerID)
|
dto, err := r.findDTO("l.id = ? AND l.owner_id = ?", id, ownerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
applySellerListingPrice(dto)
|
||||||
|
return dto, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) findOwnedForUpdate(tx *gorm.DB, ownerID uint64, listingID uint64) (*model.RentalListing, *model.GameAccount, error) {
|
func (r *Repository) findOwnedForUpdate(tx *gorm.DB, ownerID uint64, listingID uint64) (*model.RentalListing, *model.GameAccount, error) {
|
||||||
@@ -493,6 +498,13 @@ func publicListings(items []ListingDTO) []ListingDTO {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sellerListings(items []ListingDTO) []ListingDTO {
|
||||||
|
for index := range items {
|
||||||
|
applySellerListingPrice(&items[index])
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
func applyPublicListingURLs(item *ListingDTO) {
|
func applyPublicListingURLs(item *ListingDTO) {
|
||||||
item.ScreenshotURLS = publicScreenshotURLs(item.ID, item.ScreenshotURLS, item.Status, item.ReviewStatus)
|
item.ScreenshotURLS = publicScreenshotURLs(item.ID, item.ScreenshotURLS, item.Status, item.ReviewStatus)
|
||||||
if item.AssetSummary != nil {
|
if item.AssetSummary != nil {
|
||||||
@@ -500,6 +512,20 @@ func applyPublicListingURLs(item *ListingDTO) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applySellerListingPrice(item *ListingDTO) {
|
||||||
|
if item == nil || item.AssetSummary == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
breakdown, ok := item.AssetSummary["price_breakdown"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sellerPrice := readSummaryNumber(breakdown["seller_total_price"])
|
||||||
|
if sellerPrice > 0 {
|
||||||
|
item.Price = sellerPrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (row listingRow) toDTO() ListingDTO {
|
func (row listingRow) toDTO() ListingDTO {
|
||||||
assetSummary := decodeAssetSummary(row.AssetSummary)
|
assetSummary := decodeAssetSummary(row.AssetSummary)
|
||||||
screenshotURLS := cleanScreenshotURLs(decodeScreenshots(row.ScreenshotURLS))
|
screenshotURLS := cleanScreenshotURLs(decodeScreenshots(row.ScreenshotURLS))
|
||||||
|
|||||||
@@ -40,3 +40,37 @@ func TestValidateRequestRequiresDepositAboveConsumables(t *testing.T) {
|
|||||||
t.Fatalf("expected valid request, got %v", err)
|
t.Fatalf("expected valid request, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplySellerListingPriceUsesSellerTotalPrice(t *testing.T) {
|
||||||
|
item := &ListingDTO{
|
||||||
|
Price: 238,
|
||||||
|
AssetSummary: map[string]any{
|
||||||
|
"price_breakdown": map[string]any{
|
||||||
|
"seller_total_price": 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
applySellerListingPrice(item)
|
||||||
|
|
||||||
|
if item.Price != 200 {
|
||||||
|
t.Fatalf("expected seller price 200, got %.2f", item.Price)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplySellerListingPriceKeepsFallbackPrice(t *testing.T) {
|
||||||
|
item := &ListingDTO{
|
||||||
|
Price: 238,
|
||||||
|
AssetSummary: map[string]any{
|
||||||
|
"price_breakdown": map[string]any{
|
||||||
|
"seller_total_price": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
applySellerListingPrice(item)
|
||||||
|
|
||||||
|
if item.Price != 238 {
|
||||||
|
t.Fatalf("expected fallback price 238, got %.2f", item.Price)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ export function getListingDisplayPrice(item: Listing) {
|
|||||||
return Number(item.price || 0);
|
return Number(item.price || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getListingSellerPrice(item: Listing) {
|
||||||
|
const priceBreakdown = item.asset_summary?.price_breakdown;
|
||||||
|
if (typeof priceBreakdown === "object" && priceBreakdown !== null) {
|
||||||
|
const price = readUnknownNumber((priceBreakdown as Record<string, unknown>).seller_total_price);
|
||||||
|
if (price > 0) return price;
|
||||||
|
}
|
||||||
|
return getListingDisplayPrice(item);
|
||||||
|
}
|
||||||
|
|
||||||
export function getRatioValue(item: Listing) {
|
export function getRatioValue(item: Listing) {
|
||||||
const ratio = readAssetNumber(item, "publish_ratio");
|
const ratio = readAssetNumber(item, "publish_ratio");
|
||||||
if (ratio > 0) return ratio;
|
if (ratio > 0) return ratio;
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { useRouter } from 'vue-router'
|
|||||||
import { showToast, showDialog } from 'vant'
|
import { showToast, showDialog } from 'vant'
|
||||||
import { fetchSellerListings, offlineListing, submitListingReview, type Listing } from '@/api/listings'
|
import { fetchSellerListings, offlineListing, submitListingReview, type Listing } from '@/api/listings'
|
||||||
import { fetchFileBlobByURL } from '@/api/files'
|
import { fetchFileBlobByURL } from '@/api/files'
|
||||||
import { listingReviewStatusLabel, listingStatusLabel } from '@/utils/statusLabels'
|
import { listingStatusLabel } from '@/utils/statusLabels'
|
||||||
|
import { getListingSellerPrice } from '@/utils/listingDisplay'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const rawListings = ref<Listing[]>([])
|
|
||||||
const activeTab = ref('all')
|
const activeTab = ref('all')
|
||||||
|
|
||||||
interface DisplayListing extends Listing {
|
interface DisplayListing extends Listing {
|
||||||
@@ -128,12 +128,8 @@ function getStatusText(item: Listing) {
|
|||||||
return listingStatusLabel(item.status)
|
return listingStatusLabel(item.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
function goDetail(item: Listing) {
|
function listingPrice(item: Listing) {
|
||||||
if (item.status === 'published' && item.review_status === 'approved') {
|
return getListingSellerPrice(item).toFixed(2)
|
||||||
router.push(`/m/listings/${item.id}`)
|
|
||||||
} else {
|
|
||||||
showToast('该商品当前状态不支持预览')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
@@ -178,7 +174,6 @@ function goBack() {
|
|||||||
v-for="item in filteredListings"
|
v-for="item in filteredListings"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="listing-card"
|
class="listing-card"
|
||||||
@click="goDetail(item)"
|
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<!-- 封面图 -->
|
<!-- 封面图 -->
|
||||||
@@ -201,7 +196,7 @@ function goBack() {
|
|||||||
<p class="listing-meta">{{ item.server_region }} · {{ item.login_platform }}</p>
|
<p class="listing-meta">{{ item.server_region }} · {{ item.login_platform }}</p>
|
||||||
|
|
||||||
<div class="price-row">
|
<div class="price-row">
|
||||||
<span class="price-val">¥{{ item.price.toFixed(2) }}<small>/小时</small></span>
|
<span class="price-val">¥{{ listingPrice(item) }}</span>
|
||||||
<span class="deposit-val">押金: ¥{{ item.deposit_amount.toFixed(2) }}</span>
|
<span class="deposit-val">押金: ¥{{ item.deposit_amount.toFixed(2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -352,7 +347,6 @@ function goBack() {
|
|||||||
padding: 14px;
|
padding: 14px;
|
||||||
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.02), 0 1px 4px rgba(0, 0, 0, 0.02);
|
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.02), 0 1px 4px rgba(0, 0, 0, 0.02);
|
||||||
border: 1px solid rgba(243, 244, 246, 0.9);
|
border: 1px solid rgba(243, 244, 246, 0.9);
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { onMounted, ref } from 'vue'
|
|||||||
|
|
||||||
import { fetchSellerListings, offlineListing, submitListingReview, type Listing } from '@/api/listings'
|
import { fetchSellerListings, offlineListing, submitListingReview, type Listing } from '@/api/listings'
|
||||||
import { listingReviewStatusLabel, listingStatusLabel } from '@/utils/statusLabels'
|
import { listingReviewStatusLabel, listingStatusLabel } from '@/utils/statusLabels'
|
||||||
|
import { getListingSellerPrice } from '@/utils/listingDisplay'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const listings = ref<Listing[]>([])
|
const listings = ref<Listing[]>([])
|
||||||
@@ -36,16 +37,7 @@ async function offline(id: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function listingPrice(row: Listing) {
|
function listingPrice(row: Listing) {
|
||||||
return `¥${readSellerPrice(row).toFixed(2)}`
|
return `¥${getListingSellerPrice(row).toFixed(2)}`
|
||||||
}
|
|
||||||
|
|
||||||
function readSellerPrice(row: Listing) {
|
|
||||||
const breakdown = row.asset_summary?.price_breakdown
|
|
||||||
if (typeof breakdown === 'object' && breakdown !== null) {
|
|
||||||
const price = Number((breakdown as Record<string, unknown>).seller_total_price)
|
|
||||||
if (Number.isFinite(price) && price > 0) return price
|
|
||||||
}
|
|
||||||
return Number(row.price || 0)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user