b5a1a51403
新增 duplicate-distribution.ts:按国家/站点聚合撞款分布与撞款 ASIN top 店铺。 TDD: task-116.test.ts 8 用例先 RED 后 GREEN。
47 lines
1.8 KiB
TypeScript
47 lines
1.8 KiB
TypeScript
/** 重复检查分布视图(任务 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<string, { asinCount: number; shopCount: number }>()
|
||
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))
|
||
}
|