/** 重复检查分布视图(任务 116):按国家/站点聚合撞款与 top 店铺分布;纯逻辑。 */ import type { DuplicateShop } from './duplicate-model.ts' export interface CountryDistributionRow { country: string asinCount: number shopCount: number } /** 按国家聚合分布(每店铺 country_codes 全量计入)。 */ export function countryDistribution(shops: readonly DuplicateShop[]): CountryDistributionRow[] { const byCountry = new Map() for (const item of shops) { for (const code of item.countryCodes) { const country = code.trim().toUpperCase() if (!country) continue const entry = byCountry.get(country) ?? { asinCount: 0, shopCount: 0 } entry.asinCount += item.asinCount entry.shopCount += 1 byCountry.set(country, entry) } } return [...byCountry.entries()] .map(([country, entry]) => ({ country, asinCount: entry.asinCount, shopCount: entry.shopCount })) .sort((a, b) => b.asinCount - a.asinCount || a.country.localeCompare(b.country)) } export interface SiteDistributionRow { site: string asinCount: number shopCount: number } /** 按站点(site)聚合:以单国家维度汇总撞款,站点取国家码。 */ export function duplicateSiteDistribution(shops: readonly DuplicateShop[]): SiteDistributionRow[] { const rows = countryDistribution(shops) return rows.map((row) => ({ site: row.country, asinCount: row.asinCount, shopCount: row.shopCount })) } /** 按撞款 ASIN 数取 top 店铺(降序),limit<=0 返回空。 */ export function topShopsByAsin(shops: readonly DuplicateShop[], limit: number): DuplicateShop[] { if (!(limit > 0)) return [] return [...shops] .sort((a, b) => b.asinCount - a.asinCount || a.shopName.localeCompare(b.shopName)) .slice(0, Math.floor(limit)) }