180 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 为现有产品添加更多 SKU 数据
*
* 运行: npx tsx scripts/add-more-skus.ts
*/
const CONFIRM_TOKEN = 'add-local-demo-skus'
type PrismaSingleton = typeof import('../src/lib/prisma')['prisma']
let prisma: PrismaSingleton
function assertLocalScriptAllowed() {
if (!process.argv.includes(`--confirm=${CONFIRM_TOKEN}`)) {
throw new Error(`add-more-skus 只允许显式本地补数:请追加 --confirm=${CONFIRM_TOKEN}。`)
}
if (process.env.NODE_ENV === 'production') {
throw new Error('add-more-skus 禁止在 NODE_ENV=production 下执行。')
}
const databaseUrl = process.env.DATABASE_URL || ''
if (!databaseUrl) {
throw new Error('add-more-skus 必须先配置 DATABASE_URL。')
}
const host = new URL(databaseUrl).hostname
if (!['localhost', '127.0.0.1', '::1'].includes(host)) {
throw new Error('add-more-skus 只允许连接本机数据库。')
}
}
// 示例模型 URL(使用公开的 GLTF 模型)
const sampleModels = [
'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/Box/glTF-Binary/Box.glb',
'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/Duck/glTF-Binary/Duck.glb',
'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/Avocado/glTF-Binary/Avocado.glb',
]
// 示例缩略图(使用占位图服务)
const sampleThumbnails = [
'https://via.placeholder.com/400x300/008D74/FFFFFF?text=SKU+Preview+1',
'https://via.placeholder.com/400x300/47C4AF/FFFFFF?text=SKU+Preview+2',
'https://via.placeholder.com/400x300/96D4C9/333333?text=SKU+Preview+3',
'https://via.placeholder.com/400x300/B9E2D5/333333?text=SKU+Preview+4',
'https://via.placeholder.com/400x300/CFEAE2/333333?text=SKU+Preview+5',
]
// 颜色选项
const colors = ['米白色', '浅灰色', '深灰色', '原木色', '胡桃色', '黑色', '白色']
// 尺寸选项
const sizes = ['小号', '中号', '大号', 'S', 'M', 'L', 'XL']
// 材质选项
const materials = ['实木', '板材', '金属', '布艺', '皮革', '玻璃']
async function addMoreSkus() {
assertLocalScriptAllowed()
;({ prisma } = await import('../src/lib/prisma'))
console.log('开始为产品添加更多 SKU...\n')
// 获取所有用户作为潜在负责人
const users = await prisma.user.findMany({
where: { status: 'ACTIVE' },
select: { id: true, name: true }
})
if (users.length === 0) {
console.log('错误: 没有找到活跃用户')
return
}
console.log(`找到 ${users.length} 个活跃用户`)
// 获取 SKU 数量较少的产品(0-2个)
const products = await prisma.product.findMany({
include: {
skus: true,
_count: { select: { skus: true } }
},
orderBy: { createdAt: 'asc' }
})
console.log(`找到 ${products.length} 个产品\n`)
let totalCreated = 0
for (const product of products) {
const existingSkuCount = product._count.skus
// 如果已有 3 个以上 SKU,跳过
if (existingSkuCount >= 3) {
console.log(`[跳过] ${product.modelNumber} 已有 ${existingSkuCount} 个 SKU`)
continue
}
// 为每个产品添加 3-5 个 SKU
const skusToAdd = Math.min(5 - existingSkuCount, 3 + Math.floor(Math.random() * 3))
console.log(`\n[处理] ${product.modelNumber} (${product.name}) - 添加 ${skusToAdd} 个 SKU`)
for (let i = 0; i < skusToAdd; i++) {
const skuIndex = existingSkuCount + i + 1
const color = colors[Math.floor(Math.random() * colors.length)]
const size = sizes[Math.floor(Math.random() * sizes.length)]
const material = materials[Math.floor(Math.random() * materials.length)]
const owner = users[Math.floor(Math.random() * users.length)]
const skuCode = `${product.modelNumber}-${color.substring(0, 2)}-${size}`.trim().toUpperCase()
const normalizedSkuCode = skuCode.trim().toUpperCase()
// 检查 SKU 编码是否已存在
const existing = await prisma.productSKU.findUnique({
where: { skuCode: normalizedSkuCode }
})
if (existing) {
console.log(` - 跳过: ${skuCode} 已存在`)
continue
}
const price = 500 + Math.floor(Math.random() * 5000)
const costPrice = Math.floor(price * (0.4 + Math.random() * 0.3))
const sku = await prisma.productSKU.create({
data: {
productId: product.id,
ownerId: owner.id,
skuCode,
price,
costPrice,
stock: Math.floor(Math.random() * 100),
isActive: Math.random() > 0.1, // 90% 概率上架
attributes: {
颜色: color,
尺寸: size,
材质: material,
},
images: [
sampleThumbnails[skuIndex % sampleThumbnails.length],
sampleThumbnails[(skuIndex + 1) % sampleThumbnails.length],
],
modelUrl: sampleModels[skuIndex % sampleModels.length],
}
})
console.log(` ✓ 创建 SKU: ${sku.skuCode} (${color}/${size}/${material}) - ¥${price}`)
totalCreated++
}
}
console.log(`\n========================================`)
console.log(`完成! 共创建 ${totalCreated} 个新 SKU`)
console.log(`========================================\n`)
// 显示统计
const stats = await prisma.product.findMany({
select: {
modelNumber: true,
name: true,
_count: { select: { skus: true } }
},
orderBy: { createdAt: 'asc' },
take: 20
})
console.log('产品 SKU 统计 (前20个):')
console.log('----------------------------------------')
stats.forEach(p => {
console.log(`${p.modelNumber.padEnd(12)} ${p.name.padEnd(20)} ${p._count.skus} 个 SKU`)
})
}
addMoreSkus()
.catch((error) => {
console.error(error)
process.exitCode = 1
})
.finally(async () => {
await prisma?.$disconnect()
})