786 lines
25 KiB
TypeScript
786 lines
25 KiB
TypeScript
/**
|
||
* 数据库种子数据脚本
|
||
* 用于填充测试数据和演示数据
|
||
*/
|
||
|
||
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();
|
||
});
|