import { PrismaClient, AdType, ImageSource, Prisma, } from '@prisma/mongo/client'; const prisma = new PrismaClient(); type SeedArgs = { clean: boolean; perType: number; }; function parseArgs(argv: string[]): SeedArgs { const clean = argv.includes('--clean') || argv.includes('-c') || process.env.SEED_CLEAN === '1' || process.env.SEED_CLEAN === 'true'; const perTypeFromArg = (() => { const idx = argv.findIndex((a) => a === '--perType'); if (idx >= 0) { const v = Number(argv[idx + 1]); if (Number.isFinite(v) && v > 0) return Math.floor(v); } const envValue = Number(process.env.SEED_PER_TYPE); if (Number.isFinite(envValue) && envValue > 0) return Math.floor(envValue); return 100; })(); return { clean, perType: perTypeFromArg }; } function nowEpochSec(): bigint { return BigInt(Math.floor(Date.now() / 1000)); } function randInt(min: number, max: number): number { // inclusive range return Math.floor(Math.random() * (max - min + 1)) + min; } function pick(arr: readonly T[]): T { return arr[randInt(0, arr.length - 1)]; } function clampLen(s: string, maxLen: number): string { return s.length <= maxLen ? s : s.slice(0, maxLen); } function makeShortLabel(prefix: string, maxLen = 20): string { const tail = randInt(1, 9999).toString().padStart(4, '0'); return clampLen(`${prefix}${tail}`, maxLen); } function makeCoverKey(adType: AdType, idx: number): string { const n = String(idx + 1).padStart(3, '0'); return `ads/${adType.toLowerCase()}/img_${n}.png`; } function makeAdsUrl(adType: AdType): string { const id = randInt(100000, 999999); return `https://example.com/ads/${adType.toLowerCase()}?c=${id}`; } function makeContent(adType: AdType): string { const phrases = [ '限时优惠', '新人专享', '立即领取', '官方推荐', '热卖爆款', '今日必看', '立刻参与', '福利放送', '超值精选', '品质保证', ] as const; const base = `${pick(phrases)} · ${adType}`; const extra = `|${pick(phrases)}|编号${randInt(1000, 9999)}`; return clampLen(base + extra, 500); } function makeTimeWindow(): { startDt: bigint; expiryDt: bigint } { const now = Number(nowEpochSec()); const startOffsetDays = randInt(-30, 30); const start = now + startOffsetDays * 86400; const durationDays = randInt(7, 90); const expiry = start + durationDays * 86400; return { startDt: BigInt(start), expiryDt: BigInt(expiry) }; } async function main() { const args = parseArgs(process.argv.slice(2)); const tsNow = nowEpochSec(); const adTypes = Object.values(AdType) as AdType[]; if (args.clean) { await prisma.ads.deleteMany(); console.log('[seed:ads] cleaned ads collection'); } const advertiserPrefixes = [ '广告商A', '广告商B', '广告商C', '品牌X', '品牌Y', '品牌Z', ] as const; const titlePrefixes = [ '爆款推荐', '限时福利', '新品上线', '今日热推', '官方活动', '专属礼包', ] as const; const batch: Prisma.AdsCreateManyInput[] = []; for (const adType of adTypes) { for (let idx = 0; idx < args.perType; idx++) { const { startDt, expiryDt } = makeTimeWindow(); const advertiser = makeShortLabel(pick(advertiserPrefixes), 20); const title = makeShortLabel(pick(titlePrefixes), 20); const adsCoverImg = Math.random() < 0.85 ? makeCoverKey(adType, idx) : null; const adsUrl = Math.random() < 0.9 ? makeAdsUrl(adType) : null; const adsContent = Math.random() < 0.8 ? makeContent(adType) : null; batch.push({ adType, advertiser, title, adsContent, adsCoverImg, adsUrl, imgSource: ImageSource.LOCAL_ONLY, startDt, expiryDt, seq: randInt(0, 9999), status: Math.random() < 0.9 ? 1 : 0, createAt: tsNow, updateAt: tsNow, }); } } const result = await prisma.ads.createMany({ data: batch }); console.log( `[seed:ads] adTypes=${adTypes.length} perType=${args.perType} inserted=${result.count} clean=${args.clean}`, ); } main() .catch((error) => { console.error('[seed:ads] failed:', error); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });