Files
vtuber/packages/db/prisma/seed.ts

355 lines
9.6 KiB
TypeScript
Raw Permalink 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 } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.cartItem.deleteMany();
await prisma.cart.deleteMany();
await prisma.orderItem.deleteMany();
await prisma.order.deleteMany();
await prisma.inventory.deleteMany();
await prisma.productVariant.deleteMany();
await prisma.product.deleteMany();
await prisma.liveMessage.deleteMany();
await prisma.liveRoomChecklistItem.deleteMany();
await prisma.liveRoomScript.deleteMany();
await prisma.liveRoomRoleAssignment.deleteMany();
await prisma.liveRoom.deleteMany();
await prisma.liveTeamMember.deleteMany();
await prisma.user.deleteMany();
const user = await prisma.user.create({
data: {
email: 'streamer-fan@vtuber.local',
name: 'Demo User',
},
});
const room = await prisma.liveRoom.create({
data: {
title: 'VTuber Demo Live',
hostName: 'Ari',
description: '歡迎來到 VTuber Demo 直播間!',
isActive: true,
status: 'PLANNING',
streamUrl: 'https://videos.pexels.com/video-files/7297923/7297923-uhd_3840_2160_30fps.mp4',
liveGoal: '示範真人+主播互動+主推商品成交閉環',
plannedStartAt: new Date(),
plannedEndAt: new Date(Date.now() + 90 * 60 * 1000),
},
});
const teamMembers = await Promise.all([
prisma.liveTeamMember.create({
data: {
displayName: '主持人-Ari',
role: 'HOST',
phone: '0911000001',
email: 'host@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '編導-Jess',
role: 'DIRECTOR',
phone: '0911000002',
email: 'director@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '導播-Kim',
role: 'PRODUCER',
phone: '0911000003',
email: 'producer@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '小編-Lina',
role: 'EDITOR',
phone: '0911000004',
email: 'editor@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '客服-Ann',
role: 'SUPPORT',
phone: '0911000005',
email: 'support@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '物流-Wei',
role: 'WAREHOUSE',
phone: '0911000006',
email: 'warehouse@vtuber.local',
},
}),
prisma.liveTeamMember.create({
data: {
displayName: '行銷-May',
role: 'MARKETING',
phone: '0911000007',
email: 'marketing@vtuber.local',
},
}),
]);
const [hostMember, directorMember, producerMember, editorMember, supportMember, warehouseMember, marketingMember] = teamMembers;
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: hostMember.id,
role: 'HOST',
isPrimary: true,
note: '直播前3分鐘啟播確認',
shiftStartAt: new Date(Date.now() - 30 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: directorMember.id,
role: 'DIRECTOR',
note: '節奏控場,提醒商品切換順序',
shiftStartAt: new Date(Date.now() - 20 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: producerMember.id,
role: 'PRODUCER',
note: '音畫輸出、畫面切換',
shiftStartAt: new Date(Date.now() - 20 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: editorMember.id,
role: 'EDITOR',
note: '補充彈幕腳本文案',
shiftStartAt: new Date(Date.now() - 10 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: supportMember.id,
role: 'SUPPORT',
note: '回覆 DM 與加購問題',
shiftStartAt: new Date(Date.now() - 10 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: warehouseMember.id,
role: 'WAREHOUSE',
note: '監控出貨、保留庫存',
shiftStartAt: new Date(Date.now() - 10 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
await prisma.liveRoomRoleAssignment.create({
data: {
liveRoomId: room.id,
teamMemberId: marketingMember.id,
role: 'MARKETING',
note: '活動主打標語與互動口號',
shiftStartAt: new Date(Date.now() - 10 * 60 * 1000),
shiftEndAt: new Date(Date.now() + 120 * 60 * 1000),
},
});
const productsCreated: {
id: string;
variants: Array<{ id: string }>;
}[] = [];
for (let i = 1; i <= 10; i += 1) {
const basePrice = 250 + i * 80;
const product = await prisma.product.create({
data: {
name: `Demo Product ${i}`,
slug: `demo-product-${i}`,
description: `這是第 ${i} 個直播間示範商品`,
imageUrl: `https://picsum.photos/seed/vtuber-${i}/640/640`,
basePrice,
liveRoomId: room.id,
},
});
const variant = await prisma.productVariant.create({
data: {
productId: product.id,
sku: `VT-P-${i.toString().padStart(3, '0')}`,
name: '標準款',
size: 'M',
price: basePrice,
},
});
await prisma.inventory.create({
data: {
productVariantId: variant.id,
quantity: 50 + i * 10,
reserved: 0,
},
});
productsCreated.push({
id: product.id,
variants: [{ id: variant.id }],
});
}
await prisma.liveRoomScript.create({
data: {
liveRoomId: room.id,
sequence: 1,
cue: '00:00',
title: '開場破冰與今晚主推',
content:
'先報告福利機制、引導觀眾先看 1~3 號主推商品,最後加入購物車可贈運費券。',
ownerRole: 'HOST',
isDone: false,
targetProductId: productsCreated[0]?.id,
},
});
await prisma.liveRoomScript.create({
data: {
liveRoomId: room.id,
sequence: 2,
cue: '00:20',
title: '第一輪上鏡',
content:
'切到主推款 Demo Product 1補充材質、顏色、穿搭組合並引導觀眾留言問價。',
ownerRole: 'HOST',
isDone: false,
targetProductId: productsCreated[1]?.id,
},
});
await prisma.liveRoomScript.create({
data: {
liveRoomId: room.id,
sequence: 3,
cue: '01:20',
title: '加碼互動與秒殺',
content: '導播轉場到購物掛件,客服同步回覆尺寸、庫存問題,提醒直播限時加價購。',
ownerRole: 'PRODUCER',
isDone: false,
targetProductId: productsCreated[2]?.id,
},
});
await prisma.liveRoomScript.create({
data: {
liveRoomId: room.id,
sequence: 4,
cue: '02:10',
title: '結尾總結',
content: '複盤今晚三款重點,提示未付款者可直接在結帳頁完成,下播後將開啟補貨預購名單。',
ownerRole: 'HOST',
isDone: false,
targetProductId: productsCreated[3]?.id,
},
});
await prisma.liveRoomChecklistItem.create({
data: {
liveRoomId: room.id,
title: '直播前 15 分鐘檢查音訊與畫面',
ownerRole: 'PRODUCER',
description: '確認鏡頭、麥克風、延遲與貨幣換算腳本無誤。',
isRequired: true,
isDone: true,
completedAt: new Date(Date.now() - 10 * 60 * 1000),
},
});
await prisma.liveRoomChecklistItem.create({
data: {
liveRoomId: room.id,
title: '補貨規則與庫存同步',
ownerRole: 'WAREHOUSE',
description: '確認各款庫存不低於 10 件,避免前 10 分鐘售罄。',
isRequired: true,
isDone: false,
},
});
await prisma.liveRoomChecklistItem.create({
data: {
liveRoomId: room.id,
title: '客服值守回覆',
ownerRole: 'SUPPORT',
description: '監看留言問題回覆:價格、尺寸、運送、下單步驟。',
isRequired: true,
isDone: false,
},
});
const sampleMessages = [
'主播你好,這款材質是什麼?',
'有沒有打折?',
'今晚能配這個紅色款嗎?',
'有現貨嗎?',
'這件能搭配什麼配件?',
'有貨到付款嗎?',
'什麼時候會補貨?',
'寄送多久可以到?',
'有試穿影片嗎?',
'價格會再便宜一點嗎?',
'尺寸表可以再清楚一點嗎?',
'包裝是不是有品牌紙袋?',
'可以直接加到購物車嗎?',
'有其他顏色嗎?',
'這張是第一個回應!',
'直播間氣氛很棒',
'主播的穿搭很有氛圍',
'我先看一下其他款',
'結帳流程有點複雜',
'今晚還會有其他新品嗎?',
];
for (const msg of sampleMessages) {
await prisma.liveMessage.create({
data: {
liveRoomId: room.id,
userName: `觀眾-${Math.floor(Math.random() * 99) + 1}`,
message: msg,
userId: user.id,
},
});
}
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (error) => {
console.error(error);
await prisma.$disconnect();
process.exit(1);
});