import type { AdminListing } from '@/features/admin/types' import { formatCoinM, formatMoney } from '@/shared/utils/format' type JsonRecord = Record function recordOf(value: unknown): JsonRecord { return typeof value === 'object' && value !== null ? (value as JsonRecord) : {} } function textOf(value: unknown) { return value == null || value === '' ? '' : String(value) } function numberOf(value: unknown) { const parsed = Number(value) return Number.isFinite(parsed) ? parsed : 0 } function compactNumber(value: number) { const rounded = Math.round(value * 10) / 10 return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1) } export function listingCodeText(row: AdminListing) { return row.listing_no || `SP${String(row.id).padStart(6, '0')}` } export function listingCoinText(row: AdminListing) { return formatCoinM(row.haf_coin_amount) } export function listingRegionText(row: AdminListing) { const regions = row.asset_summary?.common_regions if (Array.isArray(regions)) { const names = regions.filter( (region): region is string => typeof region === 'string' && Boolean(region.trim()) ) if (names.length > 0) return names.join('、') } return row.server_region || '—' } export function listingAssetText(row: AdminListing, key: string) { const value = row.asset_summary?.[key] if (value == null || value === '') return '—' if (Array.isArray(value)) { const names = value.filter( (item): item is string => typeof item === 'string' && Boolean(item.trim()) ) return names.length > 0 ? names.join('、') : '—' } if (typeof value === 'object') return '—' return String(value) } export function listingLevelText(row: AdminListing, key: string) { const value = listingAssetText(row, key) return value === '—' ? value : value.replace(/级$/, '') } export function listingQuantity(row: AdminListing, key: string) { const resources = row.asset_summary?.resources if (!Array.isArray(resources)) return 0 const match = resources.find(item => { const resource = recordOf(item) return textOf(resource.key) === key }) return numberOf(recordOf(match).quantity) } export function listingResourceText(row: AdminListing, key: string) { const quantity = listingQuantity(row, key) return quantity > 0 ? compactNumber(quantity) : '—' } export function listingSkinGroupText(row: AdminListing, groupKey: string) { const groups = recordOf(row.asset_summary?.skin_groups) const names = groups[groupKey] if (!Array.isArray(names)) return '—' const values = names.filter( (name): name is string => typeof name === 'string' && Boolean(name.trim()) ) return values.length > 0 ? values.join('、') : '—' } export function listingCharacterSkinText(row: AdminListing) { const groups = [ { label: '红皮', key: 'operatorRed' }, { label: '金皮', key: 'operatorGold' }, { label: '武器', key: 'weapon' }, ] const parts = groups .map(group => { const names = listingSkinGroupText(row, group.key) return names === '—' ? '' : `${group.label}:${names}` }) .filter(Boolean) return parts.length > 0 ? parts.join(' / ') : '—' } export function listingAllHeroText(row: AdminListing) { const value = listingAssetText(row, 'all_hero') !== '—' ? listingAssetText(row, 'all_hero') : listingAssetText(row, 'all_heroes') return value === '是' || value === '否' ? value : '—' } export function listingOwnerRemark(row: AdminListing) { return row.description?.trim() || '—' } export function listingPureCoinPrice(row: AdminListing) { const summary = row.asset_summary ?? {} const breakdown = recordOf(summary.price_breakdown) const buyerCent = numberOf(breakdown.buyer_coin_base_price_cent) if (buyerCent > 0) return Math.round(buyerCent) const buyerYuan = numberOf(breakdown.buyer_coin_base_price) if (buyerYuan > 0) return Math.round(buyerYuan * 100) const consumableCent = numberOf(breakdown.consumable_price_cent) || numberOf(breakdown.consumable_price) * 100 return Math.max(0, Math.round(row.price_cent - consumableCent)) } export function listingPureCoinPriceText(row: AdminListing) { const cents = listingPureCoinPrice(row) return cents > 0 ? formatMoney(cents) : '—' } export function listingRentalDuration(row: AdminListing) { const coinM = numberOf(row.haf_coin_amount) / 1_000_000 const dailyLoss = numberOf(row.asset_summary?.daily_loss_m) if (coinM <= 0 || dailyLoss <= 0) { return { days: '—', dailyLoss: '' } } return { days: `${Math.max(1, Math.ceil(coinM / dailyLoss))}天`, dailyLoss: `日耗 ${compactNumber(dailyLoss)}M`, } } export function listingRentDepositText(row: AdminListing) { return `${formatMoney(row.price_cent)} / ${formatMoney(row.deposit_amount_cent)}` } export function listingRatioText(row: AdminListing) { const ratio = numberOf(row.asset_summary?.publish_ratio) if (ratio <= 0) return '—' return `1:${Math.round(ratio)}` } export function listingAccountLevel(row: AdminListing) { const fireLevel = listingAssetText(row, 'fire_level') if (fireLevel !== '—') return fireLevel return row.rank_level || '—' } function breakdownCent(row: AdminListing, key: string) { const breakdown = recordOf(row.asset_summary?.price_breakdown) const cent = numberOf(breakdown[`${key}_cent`]) if (cent > 0) return Math.round(cent) const yuan = numberOf(breakdown[key]) return yuan > 0 ? Math.round(yuan * 100) : 0 } export function listingConsumablePriceCent(row: AdminListing) { return breakdownCent(row, 'consumable_price') } export function listingSellerCoinBasePriceCent(row: AdminListing) { const stored = breakdownCent(row, 'seller_coin_base_price') if (stored > 0) return stored const fallback = row.price_cent - listingConsumablePriceCent(row) return Math.max(0, Math.round(fallback)) } export function listingSellerTotalPriceCent(row: AdminListing) { const stored = breakdownCent(row, 'seller_total_price') if (stored > 0) return stored return listingSellerCoinBasePriceCent(row) + listingConsumablePriceCent(row) } export function listingBuyerCoinBasePriceCent(row: AdminListing) { return listingPureCoinPrice(row) } export function listingBuyerTotalPriceCent(row: AdminListing) { const stored = breakdownCent(row, 'buyer_total_price') if (stored > 0) return stored return listingBuyerCoinBasePriceCent(row) + listingConsumablePriceCent(row) } export function listingPlatformMarkupCent(row: AdminListing) { const stored = breakdownCent(row, 'platform_markup_amount') if (stored > 0) return stored return Math.max(0, listingBuyerCoinBasePriceCent(row) - listingSellerCoinBasePriceCent(row)) } export function listingSellerRatio(row: AdminListing) { const breakdown = recordOf(row.asset_summary?.price_breakdown) const stored = numberOf(breakdown.seller_ratio) || numberOf(breakdown.seller_reference_ratio) if (stored > 0) return stored const baseYuan = listingSellerCoinBasePriceCent(row) / 100 const coinWan = numberOf(row.haf_coin_amount) / 10000 return baseYuan > 0 ? coinWan / baseYuan : 0 } export function listingBuyerRatio(row: AdminListing) { const breakdown = recordOf(row.asset_summary?.price_breakdown) const stored = numberOf(breakdown.buyer_ratio) if (stored > 0) return stored const baseYuan = listingBuyerCoinBasePriceCent(row) / 100 const coinWan = numberOf(row.haf_coin_amount) / 10000 return baseYuan > 0 ? coinWan / baseYuan : 0 } export function listingSkinNames(row: AdminListing): string[] { const groups = recordOf(row.asset_summary?.skin_groups) const names: string[] = [] Object.values(groups).forEach(value => { if (!Array.isArray(value)) return value.forEach(name => { if (typeof name === 'string' && name.trim()) names.push(name.trim()) }) }) return Array.from(new Set(names)) }