2026-04-17 12:50:06 +08:00

786 lines
25 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.

/**
* 数据库种子数据脚本
* 用于填充测试数据和演示数据
*/
import { PrismaClient, FeedbackStatus, FeedbackPriority, ActivityType } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import { Pool } from 'pg';
import { hash } from 'bcryptjs';
import { STANDARD_FINISH_CATALOG } from '../src/lib/standard-finish-catalog';
import { buildStandardFinishMaterialData } from '../src/lib/standard-finish-sync';
import 'dotenv/config';
// Prisma 7 需要使用适配器
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
async function main() {
console.log('🌱 开始填充数据库...');
// 清理现有数据
console.log('🧹 清理现有数据...');
await prisma.activityLog.deleteMany();
await prisma.feedback.deleteMany();
await prisma.shareLink.deleteMany();
await prisma.productHardware.deleteMany();
await prisma.panelMaterial.deleteMany();
await prisma.panel.deleteMany();
await prisma.assemblyStep.deleteMany();
await prisma.productSKU.deleteMany();
await prisma.product.deleteMany();
await prisma.productCategory.deleteMany();
await prisma.hardware.deleteMany();
await prisma.hardwareCategory.deleteMany();
await prisma.material.deleteMany();
await prisma.materialCategory.deleteMany();
await prisma.user.deleteMany();
await prisma.team.deleteMany();
await prisma.department.deleteMany();
// ============================================
// 创建饰面/花色标准库
// ============================================
console.log('🎨 创建饰面/花色标准分类...');
const materialCategories = await Promise.all(
STANDARD_FINISH_CATALOG.map((category) =>
prisma.materialCategory.create({
data: {
name: category.name,
icon: category.icon,
sortOrder: category.sortOrder,
isActive: true,
},
}),
),
);
const categoryIdByKey = new Map(
STANDARD_FINISH_CATALOG.map((category, index) => [category.key, materialCategories[index].id]),
);
console.log('🎨 创建饰面/花色标准预设...');
for (const category of STANDARD_FINISH_CATALOG) {
const categoryId = categoryIdByKey.get(category.key);
if (!categoryId) {
throw new Error(`缺少饰面分类 ID: ${category.key}`);
}
for (const material of category.materials) {
await prisma.material.create({
data: buildStandardFinishMaterialData({
material,
category,
categoryId,
}),
});
}
}
// ============================================
// 创建五金件分类和预置五金件
// ============================================
console.log('🔩 创建五金件分类...');
const hardwareCategories = await Promise.all([
prisma.hardwareCategory.create({
data: { name: '合页', icon: '📐', sortOrder: 1, description: '各类门板合页' },
}),
prisma.hardwareCategory.create({
data: { name: '螺丝', icon: '🔩', sortOrder: 2, description: '固定用螺丝' },
}),
prisma.hardwareCategory.create({
data: { name: '导轨', icon: '🛤️', sortOrder: 3, description: '抽屉滑轨' },
}),
prisma.hardwareCategory.create({
data: { name: '拉手', icon: '🚪', sortOrder: 4, description: '柜门拉手' },
}),
prisma.hardwareCategory.create({
data: { name: '连接件', icon: '🔗', sortOrder: 5, description: '板材连接件' },
}),
prisma.hardwareCategory.create({
data: { name: '支撑件', icon: '📏', sortOrder: 6, description: '层板支撑' },
}),
]);
// 创建分类映射
const hwCatMap: Record<string, string> = {};
hardwareCategories.forEach((cat) => {
hwCatMap[cat.name] = cat.id;
});
console.log('🔧 创建预置五金件...');
// 合页类
const hinges = [
{ name: '直角合页 50mm', code: 'HW-HINGE-001', specs: { size: '50x50mm', material: '不锈钢', angle: 90 } },
{ name: '直角合页 75mm', code: 'HW-HINGE-002', specs: { size: '75x75mm', material: '不锈钢', angle: 90 } },
{ name: '阻尼合页', code: 'HW-HINGE-003', specs: { size: '40mm', material: '锌合金', damping: true } },
{ name: '隐藏式合页', code: 'HW-HINGE-004', specs: { size: '35mm', material: '不锈钢', hidden: true } },
];
for (const hw of hinges) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['合页'],
specifications: hw.specs,
modelUrl: `/hardware/models/hinges/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/hinges/${hw.code}.jpg`,
dimensions: { width: 50, height: 50, depth: 5, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 螺丝类
const screws = [
{ name: '十字沉头螺丝 M4x20', code: 'HW-SCREW-001', specs: { type: 'M4', length: 20, head: '沉头' } },
{ name: '十字沉头螺丝 M4x30', code: 'HW-SCREW-002', specs: { type: 'M4', length: 30, head: '沉头' } },
{ name: '内六角螺丝 M6x25', code: 'HW-SCREW-003', specs: { type: 'M6', length: 25, head: '内六角' } },
{ name: '自攻螺丝 4x30', code: 'HW-SCREW-004', specs: { type: '4mm', length: 30, selfTapping: true } },
];
for (const hw of screws) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['螺丝'],
specifications: hw.specs,
modelUrl: `/hardware/models/screws/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/screws/${hw.code}.jpg`,
dimensions: { width: 8, height: hw.specs.length, depth: 8, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 导轨类
const rails = [
{ name: '三节滑轨 300mm', code: 'HW-RAIL-001', specs: { length: 300, sections: 3, loadCapacity: '25kg' } },
{ name: '三节滑轨 400mm', code: 'HW-RAIL-002', specs: { length: 400, sections: 3, loadCapacity: '30kg' } },
{ name: '三节滑轨 500mm', code: 'HW-RAIL-003', specs: { length: 500, sections: 3, loadCapacity: '35kg' } },
{ name: '阻尼缓冲滑轨 400mm', code: 'HW-RAIL-004', specs: { length: 400, sections: 3, damping: true } },
];
for (const hw of rails) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['导轨'],
specifications: hw.specs,
modelUrl: `/hardware/models/rails/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/rails/${hw.code}.jpg`,
dimensions: { width: 45, height: hw.specs.length, depth: 12, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 拉手类
const handles = [
{ name: '简约拉手 96mm', code: 'HW-HANDLE-001', specs: { holeSpacing: 96, style: '简约' } },
{ name: '简约拉手 128mm', code: 'HW-HANDLE-002', specs: { holeSpacing: 128, style: '简约' } },
{ name: '隐形拉手 64mm', code: 'HW-HANDLE-003', specs: { holeSpacing: 64, style: '隐形' } },
{ name: '圆形拉手', code: 'HW-HANDLE-004', specs: { diameter: 35, style: '圆形' } },
];
for (const hw of handles) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['拉手'],
specifications: hw.specs,
modelUrl: `/hardware/models/handles/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/handles/${hw.code}.jpg`,
dimensions: { width: 120, height: 25, depth: 30, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 连接件类
const connectors = [
{ name: '三合一连接件', code: 'HW-CONN-001', specs: { type: '三合一', material: '锌合金' } },
{ name: '偏心轮连接件', code: 'HW-CONN-002', specs: { type: '偏心轮', diameter: 15 } },
{ name: '角码连接件', code: 'HW-CONN-003', specs: { type: '角码', angle: 90 } },
{ name: '隐形连接件', code: 'HW-CONN-004', specs: { type: '隐形', hidden: true } },
];
for (const hw of connectors) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['连接件'],
specifications: hw.specs,
modelUrl: `/hardware/models/connectors/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/connectors/${hw.code}.jpg`,
dimensions: { width: 15, height: 15, depth: 15, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 支撑件类
const supports = [
{ name: '层板托 5mm', code: 'HW-SUPP-001', specs: { pinDiameter: 5, material: '金属' } },
{ name: '层板托 7mm', code: 'HW-SUPP-002', specs: { pinDiameter: 7, material: '金属' } },
{ name: '隔板支撑', code: 'HW-SUPP-003', specs: { type: '隔板', adjustable: false } },
{ name: '可调节层板托', code: 'HW-SUPP-004', specs: { type: '可调节', adjustable: true } },
];
for (const hw of supports) {
await prisma.hardware.create({
data: {
name: hw.name,
code: hw.code,
categoryId: hwCatMap['支撑件'],
specifications: hw.specs,
modelUrl: `/hardware/models/supports/${hw.code}.glb`,
thumbnailUrl: `/hardware/thumbnails/supports/${hw.code}.jpg`,
dimensions: { width: 10, height: 20, depth: 10, unit: 'mm' },
isPreset: true,
isActive: true,
},
});
}
// 创建产品分类
console.log('📁 创建产品分类...');
const categories = await Promise.all([
prisma.productCategory.create({
data: { name: '沙发', description: '各类沙发产品', sortOrder: 1 },
}),
prisma.productCategory.create({
data: { name: '餐桌', description: '餐厅用桌', sortOrder: 2 },
}),
prisma.productCategory.create({
data: { name: '书柜', description: '书房家具', sortOrder: 3 },
}),
prisma.productCategory.create({
data: { name: '床', description: '卧室床类', sortOrder: 4 },
}),
prisma.productCategory.create({
data: { name: '椅子', description: '各类座椅', sortOrder: 5 },
}),
prisma.productCategory.create({
data: { name: '茶几', description: '客厅茶几', sortOrder: 6 },
}),
prisma.productCategory.create({
data: { name: '电视柜', description: '客厅电视柜', sortOrder: 7 },
}),
prisma.productCategory.create({
data: { name: '衣柜', description: '卧室衣柜', sortOrder: 8 },
}),
]);
// 分类映射(方便后面使用)
const categoryMap: Record<string, string> = {};
categories.forEach(cat => {
categoryMap[cat.name] = cat.id;
});
// 创建部门结构
console.log('🏢 创建部门结构...');
const designDept = await prisma.department.create({
data: {
name: '设计部',
fullPath: '设计部',
orderIndex: 1,
},
});
const design3DDept = await prisma.department.create({
data: {
name: '3D设计组',
fullPath: '设计部/3D设计组',
parentId: designDept.id,
orderIndex: 1,
},
});
const _productDept = await prisma.department.create({
data: {
name: '产品部',
fullPath: '产品部',
orderIndex: 2,
},
});
const _marketingDept = await prisma.department.create({
data: {
name: '市场部',
fullPath: '市场部',
orderIndex: 3,
},
});
// 创建小组
console.log('👥 创建小组...');
const modelingTeam = await prisma.team.create({
data: {
name: '建模小组',
description: '负责3D模型制作',
departmentId: design3DDept.id,
orderIndex: 1,
},
});
const animationTeam = await prisma.team.create({
data: {
name: '动画小组',
description: '负责装配动画制作',
departmentId: design3DDept.id,
orderIndex: 2,
},
});
// 创建用户
console.log('👤 创建用户...');
const hashedPassword = await hash('Admin123456!', 10);
const _superAdmin = await prisma.user.create({
data: {
email: 'admin@furniture3d.com',
name: '系统管理员',
password: hashedPassword,
role: 'SUPER_ADMIN',
isFirstLogin: false,
status: 'ACTIVE',
},
});
const deptManager = await prisma.user.create({
data: {
email: 'design.manager@furniture3d.com',
name: '张设计',
password: hashedPassword,
role: 'SUPERVISOR',
departmentId: designDept.id,
isFirstLogin: false,
status: 'ACTIVE',
},
});
// 更新部门负责人
await prisma.department.update({
where: { id: designDept.id },
data: { managerId: deptManager.id },
});
const teamLeader = await prisma.user.create({
data: {
email: 'modeling.leader@furniture3d.com',
name: '李组长',
password: hashedPassword,
role: 'SUPERVISOR',
departmentId: design3DDept.id,
teamId: modelingTeam.id,
isFirstLogin: false,
status: 'ACTIVE',
},
});
// 更新小组组长
await prisma.team.update({
where: { id: modelingTeam.id },
data: { leaderId: teamLeader.id },
});
// 创建设计师
const designers = [];
for (let i = 1; i <= 5; i++) {
const designer = await prisma.user.create({
data: {
email: `designer${i}@furniture3d.com`,
name: `设计师${i}号`,
password: hashedPassword,
role: 'DESIGNER',
departmentId: design3DDept.id,
teamId: i <= 3 ? modelingTeam.id : animationTeam.id,
isFirstLogin: false,
status: 'ACTIVE',
},
});
designers.push(designer);
}
// 创建产品
console.log('🛋️ 创建产品数据...');
const products = [];
// ============================================
// 本地模型路径说明:
// 所有模型存放在 public/uploads/models/ 目录下
// 前端访问路径为 /uploads/models/xxx.glb
// ============================================
// 产品1:现代简约沙发
const sofa = await prisma.product.create({
data: {
modelNumber: 'SF-001',
name: '现代简约沙发',
description: '北欧风格,简约设计,舒适坐感',
categoryId: categoryMap['沙发'],
modelUrl: '/uploads/models/demo/SheenChair.glb', // 本地模型:椅子(4.2MB)
thumbnailUrl: '/uploads/models/demo/thumbnails/sofa.jpg',
createdBy: designers[0].id,
departmentId: design3DDept.id,
visibility: 'PUBLIC',
collaboratorUsers: {
create: [
{
userId: designers[1].id,
role: 'editor',
},
],
},
},
});
products.push(sofa);
// 产品2:实木餐桌
const table = await prisma.product.create({
data: {
modelNumber: 'TB-001',
name: '实木餐桌',
description: '橡木材质,可容纳6人',
categoryId: categoryMap['餐桌'],
modelUrl: '/uploads/models/demo/Horse.glb', // 本地模型:马(177KB)
createdBy: designers[1].id,
departmentId: design3DDept.id,
visibility: 'DEPARTMENT',
},
});
products.push(table);
// 产品3:书柜
const bookshelf = await prisma.product.create({
data: {
modelNumber: 'BS-001',
name: '多层书柜',
description: '5层设计,大容量储物',
categoryId: categoryMap['书柜'],
modelUrl: '/uploads/models/demo/Flamingo.glb', // 本地模型:火烈鸟(76KB)
createdBy: designers[2].id,
departmentId: design3DDept.id,
visibility: 'TEAM',
},
});
products.push(bookshelf);
// 本地演示模型列表(循环使用)
const demoModels = [
'/uploads/models/demo/SheenChair.glb',
'/uploads/models/demo/Horse.glb',
'/uploads/models/demo/Flamingo.glb',
'/uploads/models/demo/Soldier.glb',
'/uploads/models/demo/Parrot.glb',
];
// 创建更多产品(用于性能测试)
const categoryNames = ['沙发', '餐桌', '书柜', '床', '衣柜'];
for (let i = 4; i <= 20; i++) {
const product = await prisma.product.create({
data: {
modelNumber: `PRD-${String(i).padStart(3, '0')}`,
name: `测试产品${i}`,
description: `这是第${i}个测试产品`,
categoryId: categoryMap[categoryNames[i % categoryNames.length]],
modelUrl: demoModels[i % demoModels.length], // 循环使用本地模型
createdBy: designers[i % designers.length].id,
departmentId: design3DDept.id,
visibility: ['PUBLIC', 'DEPARTMENT', 'TEAM'][i % 3],
},
});
products.push(product);
}
// 创建 SKU
console.log('📦 创建 SKU 数据...');
const skus = [];
// 沙发 SKU
const sofaColors = ['米白色', '浅灰色', '深灰色'];
const sofaSizes = ['双人座', '三人座'];
for (const color of sofaColors) {
for (const size of sofaSizes) {
const sku = await prisma.productSKU.create({
data: {
skuCode: `SF-001-${color.substring(0, 2)}-${size.substring(0, 2)}`,
productId: sofa.id,
ownerId: designers[0].id,
attributes: { color, size, material: '布艺' },
price: size === '双人座' ? 2999 : 3999,
costPrice: size === '双人座' ? 1500 : 2000,
stock: Math.floor(Math.random() * 50) + 10,
images: [
`https://example.com/sofa-${color}-${size}-1.jpg`,
`https://example.com/sofa-${color}-${size}-2.jpg`,
],
isActive: true,
},
});
skus.push(sku);
}
}
// 为其他产品创建 SKU
for (let i = 1; i < Math.min(products.length, 10); i++) {
const product = products[i];
for (let j = 1; j <= 3; j++) {
const sku = await prisma.productSKU.create({
data: {
skuCode: `${product.modelNumber}-V${j}`,
productId: product.id,
ownerId: designers[i % designers.length].id,
attributes: { variant: `变体${j}` },
price: 1000 + Math.random() * 4000,
costPrice: 500 + Math.random() * 2000,
stock: Math.floor(Math.random() * 100),
images: [`https://example.com/${product.modelNumber}-v${j}.jpg`],
isActive: Math.random() > 0.2,
},
});
skus.push(sku);
}
}
// 创建装配步骤(使用演示模型的实际节点)
console.log('🔧 创建装配步骤...');
await prisma.assemblyStep.createMany({
data: [
{
productId: sofa.id,
order: 1,
title: '初始位置',
description: '模型从初始位置开始',
partIds: ['Scene'],
transform: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] },
duration: 1500,
easing: 'power2.inOut',
},
{
productId: sofa.id,
order: 2,
title: '旋转展示',
description: '360度旋转展示模型',
partIds: ['Scene'],
transform: { position: [0, 0, 0], rotation: [0, Math.PI * 2, 0], scale: [1, 1, 1] },
duration: 3000,
easing: 'power2.inOut',
},
{
productId: sofa.id,
order: 3,
title: '放大查看',
description: '放大查看细节',
partIds: ['Scene'],
transform: { position: [0, 0, 0], rotation: [0, Math.PI * 2, 0], scale: [1.5, 1.5, 1.5] },
duration: 2000,
easing: 'power2.inOut',
},
{
productId: sofa.id,
order: 4,
title: '恢复原状',
description: '恢复到初始状态',
partIds: ['Scene'],
transform: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] },
duration: 2000,
easing: 'power2.inOut',
},
],
});
// 创建板件
console.log('📏 创建板件数据...');
await prisma.panel.createMany({
data: [
{
productId: sofa.id,
code: 'P001',
name: '底座板',
gltfNodeName: 'base',
length: 200,
width: 80,
thickness: 5,
},
{
productId: sofa.id,
code: 'P002',
name: '靠背板',
gltfNodeName: 'back',
length: 200,
width: 60,
thickness: 3,
},
{
productId: sofa.id,
code: 'P003',
name: '左扶手',
gltfNodeName: 'arm-left',
length: 80,
width: 20,
thickness: 3,
},
{
productId: sofa.id,
code: 'P004',
name: '右扶手',
gltfNodeName: 'arm-right',
length: 80,
width: 20,
thickness: 3,
},
],
});
// 创建分享链接
console.log('🔗 创建分享链接...');
await prisma.shareLink.createMany({
data: [
{
productId: sofa.id,
token: 'share-sofa-demo-001',
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天后过期
viewCount: Math.floor(Math.random() * 100),
},
{
productId: table.id,
token: 'share-table-demo-001',
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7天后过期
viewCount: Math.floor(Math.random() * 50),
},
],
});
// 创建反馈
console.log('💬 创建反馈数据...');
const feedbackCategories = ['模型错误', '尺寸错误', '材质错误', '装配步骤错误', '其他'];
const feedbackStatuses: FeedbackStatus[] = ['PENDING', 'IN_PROGRESS', 'RESOLVED', 'CLOSED'];
const feedbackPriorities: FeedbackPriority[] = ['LOW', 'MEDIUM', 'HIGH', 'URGENT'];
for (let i = 0; i < 30; i++) {
const feedbackData = {
title: `反馈问题 ${i + 1}`,
description: `这是第 ${i + 1} 个反馈问题的详细描述`,
category: feedbackCategories[i % feedbackCategories.length],
priority: feedbackPriorities[i % feedbackPriorities.length],
status: feedbackStatuses[i % feedbackStatuses.length],
productId: products[i % products.length].id,
submitterId: designers[i % designers.length].id,
assigneeId: i % 2 === 0 ? teamLeader.id : null,
images: i % 3 === 0 ? [`https://example.com/feedback-${i}.jpg`] : [],
resolution: i % 4 === 3 ? '问题已修复' : null,
resolvedAt: i % 4 === 3 ? new Date() : null,
...(i % 4 === 0
? {
comments: [
{
userId: teamLeader.id,
content: 'I will handle this issue',
createdAt: new Date().toISOString(),
},
],
}
: {}),
};
// 只在需要时添加 comments 字段
if (i % 4 === 0) {
feedbackData.comments = [
{
userId: teamLeader.id,
content: '我来处理这个问题',
createdAt: new Date().toISOString(),
},
];
}
await prisma.feedback.create({
data: feedbackData,
});
}
// 创建操作日志
console.log('📝 创建操作日志...');
const activityTypes: ActivityType[] = [
'USER_LOGIN',
'PRODUCT_CREATE',
'PRODUCT_UPDATE',
'SKU_CREATE',
'SKU_UPDATE',
'FILE_UPLOAD',
'SHARE_LINK_CREATE',
'FEEDBACK_CREATE',
];
for (let i = 0; i < 100; i++) {
const createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000); // 过去30天内
await prisma.activityLog.create({
data: {
userId: designers[i % designers.length].id,
type: activityTypes[i % activityTypes.length],
action: `执行了 ${activityTypes[i % activityTypes.length]} 操作`,
targetType: i % 2 === 0 ? 'Product' : 'ProductSKU',
targetId: i % 2 === 0 ? products[i % products.length].id : skus[i % skus.length]?.id,
changes: {
field: 'name',
before: '旧值',
after: '新值',
},
metadata: {
ip: `192.168.1.${Math.floor(Math.random() * 255)}`,
userAgent: 'Mozilla/5.0',
device: 'Desktop',
},
createdAt,
},
});
}
console.log('✅ 数据填充完成!');
console.log('\n📊 数据统计:');
console.log(` - 材质分类:${await prisma.materialCategory.count()} 个`);
console.log(` - 预置材质:${await prisma.material.count()} 个`);
console.log(` - 五金件分类:${await prisma.hardwareCategory.count()} 个`);
console.log(` - 预置五金件:${await prisma.hardware.count()} 个`);
console.log(` - 部门:${await prisma.department.count()} 个`);
console.log(` - 小组:${await prisma.team.count()} 个`);
console.log(` - 用户:${await prisma.user.count()} 个`);
console.log(` - 产品:${await prisma.product.count()} 个`);
console.log(` - SKU:${await prisma.productSKU.count()} 个`);
console.log(` - 装配步骤:${await prisma.assemblyStep.count()} 个`);
console.log(` - 板件:${await prisma.panel.count()} 个`);
console.log(` - 分享链接:${await prisma.shareLink.count()} 个`);
console.log(` - 反馈:${await prisma.feedback.count()} 个`);
console.log(` - 操作日志:${await prisma.activityLog.count()} 条`);
console.log('\n🔑 测试账号:');
console.log(' - 超级管理员:admin@furniture3d.com / Admin123456!');
console.log(' - 部门管理员:design.manager@furniture3d.com / Admin123456!');
console.log(' - 组长:modeling.leader@furniture3d.com / Admin123456!');
console.log(' - 设计师:designer1@furniture3d.com / Admin123456!');
}
main()
.catch((e) => {
console.error('❌ 数据填充失败:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
await pool.end();
});