/** * 从 zhibo 导出的 world-cup-group-stage-matches.json 生成 seed 用裁剪 JSON 与球队映射 TS。 * 用法: node apps/api/scripts/build-wc2026-seed-json.mjs <源JSON路径> */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const apiRoot = path.resolve(__dirname, '..'); const seedDataDir = path.join(apiRoot, 'src/infrastructure/database/seed-data'); const outrightTeamsPath = path.join(apiRoot, 'src/domains/catalog/wc2026-outright-teams.ts'); const sourcePath = process.argv[2]; if (!sourcePath) { console.error('用法: node build-wc2026-seed-json.mjs <源JSON路径>'); process.exit(1); } const raw = JSON.parse(fs.readFileSync(path.resolve(sourcePath), 'utf8')); const outrightSrc = fs.readFileSync(outrightTeamsPath, 'utf8'); const outrightTeams = [...outrightSrc.matchAll(/code: '([^']+)', names: \{ 'zh-CN': '([^']+)', 'en-US': '([^']+)'/g)].map((m) => ({ code: m[1], zh: m[2], en: m[3], })); function pickNames(names) { return { zh: names?.zh ?? null, en: names?.en ?? null, zhTw: names?.zhTw ?? null, vi: names?.vi ?? null, km: names?.km ?? null, ms: names?.ms ?? null, }; } function pickTeam(team) { return { id: team.id, name: team.name, names: pickNames(team.names), image: team.image ?? '', }; } function slimMatch(m) { return { officialMatchNo: m.officialMatchNo, stage: m.stage, groupName: m.groupName, liveMatchId: m.liveMatchId, additionMatchId: m.additionMatchId, channelId: m.channelId, matchName: m.matchName, league: { type: m.league.type, en: m.league.en, zh: m.league.zh }, kickoff: { utcTimeStart: m.kickoff.utcTimeStart, utcTimeStop: m.kickoff.utcTimeStop, utcIso: m.kickoff.utcIso, chinaTime: m.kickoff.chinaTime, venueTime: m.kickoff.venueTime, venueTimezone: m.kickoff.venueTimezone, }, homeTeam: pickTeam(m.homeTeam), awayTeam: pickTeam(m.awayTeam), status: { state: m.status.state, isHot: m.status.isHot ?? 0 }, venue: { names: pickNames(m.venue?.names), city: pickNames(m.venue?.city), }, sortOrder: m.sortOrder, isPublished: m.isPublished, }; } function resolveCanonicalCode(team) { if (team.id == null) return null; const en = (team.name || team.names?.en || '').toLowerCase(); const zh = team.names?.zh || ''; const hit = outrightTeams.find( (o) => o.en.toLowerCase() === en || o.zh === zh || (o.en === 'Turkey' && en.includes('türkiye')) || (o.en === 'Czech' && en === 'czechia') || (o.en === 'Bosnia' && en.includes('bosnia')) || (o.en === 'Ivory Coast' && en.includes('côte')) || (o.en === 'DR Congo' && en.includes('congo')) || (o.en === 'Curacao' && en.includes('cura')), ); return hit?.code ?? null; } const matches = (raw.matches || []).map(slimMatch); const bundle = { count: matches.length, matches }; const teamById = new Map(); for (const m of raw.matches || []) { for (const t of [m.homeTeam, m.awayTeam]) { if (t?.id != null) teamById.set(t.id, t); } } const zhiboToCode = {}; const logoByCode = {}; const unmatched = []; for (const [id, team] of teamById) { const code = resolveCanonicalCode(team); if (code) { zhiboToCode[id] = code; if (team.image) logoByCode[code] = team.image; } else { unmatched.push({ id, name: team.name }); } } fs.mkdirSync(seedDataDir, { recursive: true }); const jsonOut = path.join(seedDataDir, 'wc2026-group-stage.json'); fs.writeFileSync(jsonOut, JSON.stringify(bundle, null, 2) + '\n', 'utf8'); const mapLines = Object.entries(zhiboToCode) .sort(([a], [b]) => Number(a) - Number(b)) .map(([id, code]) => ` ${id}: '${code}',`) .join('\n'); const logoLines = Object.entries(logoByCode) .sort(([a], [b]) => a.localeCompare(b)) .map(([code, url]) => ` ${code}: '${url.replace(/'/g, "\\'")}',`) .join('\n'); const mapTs = `/** 由 build-wc2026-seed-json.mjs 生成 — zhibo externalId → WC2026 canonical code */ export const WC2026_ZIBO_ID_TO_CODE: Record = { ${mapLines} }; /** zhibo 球队 logo(seed 时写入 teams.logo_url) */ export const WC2026_TEAM_LOGO_BY_CODE: Record = { ${logoLines} }; `; const mapOut = path.join(seedDataDir, 'wc2026-zhibo-team-map.ts'); fs.writeFileSync(mapOut, mapTs, 'utf8'); console.log(`Wrote ${jsonOut} (${matches.length} matches)`); console.log(`Wrote ${mapOut} (${Object.keys(zhiboToCode).length} team mappings)`); if (unmatched.length) { console.warn('Unmatched teams:', unmatched); process.exit(1); }