"feat: add EventItem, EventReview models with seed data and mock files, update User and Event schemas"

This commit is contained in:
louiscklaw
2025-06-03 15:29:05 +08:00
parent a0a4ffcb4e
commit 24920fb313
52 changed files with 2140 additions and 56 deletions

View File

@@ -0,0 +1,36 @@
import { PrismaClient } from '@prisma/client';
import { format, parseISO } from 'date-fns';
const L_ERROR = 0;
const L_WARN = 1;
const L_INFO = 2;
const L_DEBUG = 3;
const L_TRACE = 4;
const prisma = new PrismaClient();
async function accessLog() {
await prisma.accessLog.upsert({
where: { id: '0' },
update: {},
create: {
userId: '0',
message: 'helloworld',
metadata: { hello: 'world' },
},
});
console.log('seed AccessLog done');
}
const accessLogSeed = accessLog()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { accessLogSeed };

View File

@@ -0,0 +1,71 @@
import { PrismaClient } from '@prisma/client';
import { format, parseISO } from 'date-fns';
const L_ERROR = 0;
const L_WARN = 1;
const L_INFO = 2;
const L_DEBUG = 3;
const L_TRACE = 4;
const prisma = new PrismaClient();
async function appLog() {
await prisma.appLog.upsert({
where: { id: '0' },
update: {},
create: {
message: 'helloworld',
level: L_ERROR,
},
});
await prisma.appLog.upsert({
where: { id: '1' },
update: {},
create: {
message: 'warning message',
level: L_WARN,
},
});
await prisma.appLog.upsert({
where: { id: '2' },
update: {},
create: {
message: 'info message',
level: L_INFO,
},
});
await prisma.appLog.upsert({
where: { id: '3' },
update: {},
create: {
message: 'debug message',
level: L_DEBUG,
},
});
await prisma.appLog.upsert({
where: { id: '4' },
update: {},
create: {
message: 'trace message',
level: L_TRACE,
},
});
console.log('seed AppLog done');
}
const appLogSeed = appLog()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { appLogSeed };

View File

@@ -2,12 +2,43 @@ import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function member() {
for (let i = 0; i < 100; i++) {
[
{
email: 'tom@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1671656349322-41de944d259b`,
sex: 'M',
},
{
email: 'may@exampl.com',
avatar: `https://images.unsplash.com/photo-1522075469751-3a6694fb2f61`,
sex: 'F',
},
{
email: 'june@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1723867331866-e112500178a4`,
sex: 'M',
},
{
email: 'april@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1682089894837-e01e5cb8e471`,
sex: 'F',
},
{
email: 'susan@exampl.com',
avatar: `https://images.unsplash.com/photo-1485893086445-ed75865251e0`,
sex: 'M',
},
{
email: 'peter@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1722945763962-305a5a769cc8`,
sex: 'F',
},
].forEach(async (m, i) => {
const john = await prisma.member.upsert({
where: { email: `member${i}@example.com` },
where: { email: m.email },
update: {},
create: {
email: `member${i}@example.com`,
email: m.email,
name: `member_${i}`,
age: 20 + i,
rank: i % 2 ? 'VIP' : 'NON_VIP',
@@ -25,11 +56,42 @@ async function member() {
self_introduction: 'Get me know me before you love me. Get me know me before you love me.',
music: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
pets: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
character: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic']
}
character: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
avatar: m.avatar,
sex: m.sex,
},
});
});
for (let i = 0; i < 3; i++) {
const john = await prisma.member.upsert({
where: { email: `member${i}@example.com` },
update: {},
create: {
email: `member${i}@example.com`,
name: `member_${i}`,
age: 20 + i,
rank: i % 2 ? 'VIP' : 'NON_VIP',
verified: i % 3 ? 'NOT_VERIFIED' : 'VERIFIED',
hobbies: ['fishing', 'basketball', 'piano'],
distance: `${40 + Math.random() * 40}km`,
location_area: 'Sai Kung',
greetings: 'Hi, I am ',
gender: 'man',
tall_cm: 172 + Math.random() * 10,
weight_kg: 60 + Math.random() * 50,
occupation: 'doctor',
language: ['English', 'French', 'Chinese'],
education: ['Degree of Computer'],
self_introduction: 'Get me know me before you love me. Get me know me before you love me.',
music: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
pets: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
character: ['Classic', 'Classic', 'Classic', 'Classic', 'Classic', 'Classic'],
avatar: '',
sex: i % 2 ? 'M' : 'F',
},
});
}
console.log('seed member done');
}

View File

@@ -78,11 +78,12 @@ export const _mock = {
avatar: (index: number) => `${CONFIG.basePath}/assets/images/avatar/avatar-${index + 1}.webp`,
travel: (index: number) => `${CONFIG.basePath}/assets/images/travel/travel-${index + 1}.webp`,
course: (index: number) => `${CONFIG.basePath}/assets/images/course/course-${index + 1}.webp`,
company: (index: number) =>
`${CONFIG.basePath}/assets/images/company/company-${index + 1}.webp`,
product: (index: number) =>
`${CONFIG.basePath}/assets/images/m-product/product-${index + 1}.webp`,
portrait: (index: number) =>
`${CONFIG.basePath}/assets/images/portrait/portrait-${index + 1}.webp`,
company: (index: number) => `${CONFIG.basePath}/assets/images/company/company-${index + 1}.webp`,
product: (index: number) => `${CONFIG.basePath}/assets/images/m-product/product-${index + 1}.webp`,
portrait: (index: number) => `${CONFIG.basePath}/assets/images/portrait/portrait-${index + 1}.webp`,
//
event: (index: number) => `${CONFIG.basePath}/assets/images/m-product/product-${index + 1}.webp`,
},
//
eventName: (index: number) => _eventNames[index],
};

View File

@@ -0,0 +1,277 @@
import { _mock } from './_mock';
import { _tags } from './assets';
import { PrismaClient, EventReview } from '@prisma/client';
import { _eventsReview } from './eventReview';
import { CONFIG } from './global-config';
import _ from 'lodash';
const prisma = new PrismaClient();
const COLORS = ['#FF4842', '#1890FF', '#FFC0CB', '#00AB55', '#FFC107', '#7F00FF', '#000000', '#FFFFFF'];
const DESCRIPTION = `
<h6>Specifications</h6>
<table>
<tbody>
<tr>
<td>Category</td>
<td>Mobile</td>
</tr>
<tr>
<td>Manufacturer</td>
<td>Apple</td>
</tr>
<tr>
<td>Warranty</td>
<td>12 Months</td>
</tr>
<tr>
<td>Serial number</td>
<td>358607726380311</td>
</tr>
<tr>
<td>Ships from</td>
<td>United States</td>
</tr>
</tbody>
</table>
<h6>Event details</h6>
<ul>
<li>
<p>The foam sockliner feels soft and comfortable</p>
</li>
<li>
<p>Pull tab</p>
</li>
<li>
<p>Not intended for use as Personal Protective Equipment</p>
</li>
<li>
<p>Colour Shown: White/Black/Oxygen Purple/Action Grape</p>
</li>
<li>
<p>Style: 921826-109</p>
</li>
<li>
<p>Country/Region of Origin: China</p>
</li>
</ul>
<h6>Benefits</h6>
<ul>
<li>
<p>Mesh and synthetic materials on the upper keep the fluid look of the OG while adding comfort</p>
and durability.
</li>
<li>
<p>Originally designed for performance running, the full-length Max Air unit adds soft, comfortable cushio</p>
ning underfoot.
</li>
<li>
<p>The foam midsole feels springy and soft.</p>
</li>
<li>
<p>The rubber outsole adds traction and durability.</p>
</li>
</ul>
<h6>Delivery and returns</h6>
<p>Your order of $200 or more gets free standard delivery.</p>
<ul>
<li>
<p>Standard delivered 4-5 Business Days</p>
</li>
<li>
<p>Express delivered 2-4 Business Days</p>
</li>
</ul>
<p>Orders are processed and delivered Monday-Friday (excluding public holidays)</p>
`;
const getColorSliceForIndex = (index: number) => {
if (index === 0) return COLORS.slice(0, 2);
if (index === 1) return COLORS.slice(1, 3);
if (index === 2) return COLORS.slice(2, 4);
if (index === 3) return COLORS.slice(3, 6);
if (index === 4 || index === 16 || index === 19) return COLORS.slice(4, 6);
if (index === 5 || index === 17) return COLORS.slice(5, 6);
if (index === 6 || index === 18) return COLORS.slice(0, 2);
if (index === 7) return COLORS.slice(4, 6);
if (index === 8) return COLORS.slice(2, 4);
if (index === 9 || index === 11) return COLORS.slice(2, 6);
if (index === 10) return COLORS.slice(3, 6);
if (index === 12) return COLORS.slice(2, 7);
if (index === 13) return COLORS.slice(4, 7);
if (index === 14) return COLORS.slice(0, 2);
if (index === 15) return COLORS.slice(5, 8);
return COLORS.slice(2, 6); // Default case
};
const generateAttachments = () => Array.from({ length: 20 }, (_, index) => _mock.image.event(index));
const generateReviews = () => {
const attachments = generateAttachments();
};
const generateRatings = () =>
Array.from({ length: 5 }, (_, index) => ({
name: `${index + 1} Star`,
starCount: _mock.number.nativeL(index),
reviewCount: _mock.number.nativeL(index + 1),
}));
const generateImages = () => Array.from({ length: 8 }, (_, index) => _mock.image.event(index));
const _events = () =>
Array.from({ length: 2 }, (_, index) => {
const reviews = generateReviews();
const images = generateImages();
const ratings = generateRatings();
//
const publish = index % 3 ? 'published' : 'draft';
const category = (index % 2 && 'Shose') || (index % 3 && 'Apparel') || 'Accessories';
const gender = (index % 2 && ['Men']) || (index % 3 && ['Women', 'Kids']) || ['Kids'];
const available = (index % 2 && 72) || (index % 3 && 10) || 0;
const inventoryType = (index % 2 && 'in stock') || (index % 3 && 'low stock') || 'out of stock';
const priceSale = index % 3 ? undefined : _mock.number.price(index);
return {
id: _mock.id(index).toString(),
sku: `WW75K521${index}YW/SV`,
name: _mock.eventName(index),
gender,
images,
reviews,
publish,
ratings,
category,
available,
priceSale,
taxes: 10,
quantity: 80,
inventoryType,
tags: _tags.slice(0, 5),
code: `38BEE27${index}`,
description: DESCRIPTION,
createdAt: _mock.time(index),
price: _mock.number.price(index),
coverUrl: _mock.image.event(index),
colors: getColorSliceForIndex(index),
totalRatings: _mock.number.rating(index),
totalSold: _mock.number.nativeM(index + 1),
totalReviews: _mock.number.nativeL(index + 1),
newLabel: { enabled: [1, 2, 3].includes(index), content: 'NEW' },
saleLabel: { enabled: [4, 5].includes(index), content: 'SALE' },
sizes: ['6', '7', '8', '8.5', '9', '9.5', '10', '10.5', '11', '11.5', '12', '13'],
subDescription: 'Featuring the original ripple design inspired by Japanese bullet trains, the Nike Air Max 97 lets you push your style full-speed ahead.',
};
});
const memberList = (num_of_member: number) =>
_.shuffle([
{
email: 'tom@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1671656349322-41de944d259b`,
sex: 'M',
},
{
email: 'may@exampl.com',
avatar: `https://images.unsplash.com/photo-1522075469751-3a6694fb2f61`,
sex: 'F',
},
{
email: 'june@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1723867331866-e112500178a4`,
sex: 'M',
},
{
email: 'april@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1682089894837-e01e5cb8e471`,
sex: 'F',
},
{
email: 'susan@exampl.com',
avatar: `https://images.unsplash.com/photo-1485893086445-ed75865251e0`,
sex: 'M',
},
{
email: 'peter@exampl.com',
avatar: `https://plus.unsplash.com/premium_photo-1722945763962-305a5a769cc8`,
sex: 'F',
},
]).slice(0, num_of_member);
async function eventItem() {
const temp_events = _events();
for (let i = 0; i < temp_events.length; i++) {
const temp_pr = _eventsReview();
const temp = await prisma.eventItem.upsert({
where: { id: temp_events[i].id.toString() },
update: {},
create: {
id: temp_events[i].id,
//
name: temp_events[i].name,
code: temp_events[i].code,
price: temp_events[i].price,
taxes: temp_events[i].taxes,
tags: temp_events[i].tags,
sizes: temp_events[i].sizes,
publish: temp_events[i].publish,
gender: temp_events[i].gender,
coverUrl: temp_events[i].coverUrl,
images: temp_events[i].images,
colors: temp_events[i].colors,
quantity: temp_events[i].quantity,
category: temp_events[i].category,
available: temp_events[i].available,
totalSold: temp_events[i].totalSold,
description: temp_events[i].description,
totalRatings: temp_events[i].totalRatings,
totalReviews: temp_events[i].totalReviews,
inventoryType: temp_events[i].inventoryType,
subDescription: temp_events[i].subDescription,
priceSale: temp_events[i].priceSale,
newLabel: temp_events[i].newLabel,
saleLabel: temp_events[i].saleLabel,
ratings: temp_events[i].ratings,
// review: { create: temp_pr },
reviews: { create: temp_pr },
sku: temp_events[i].sku,
//
eventDate: new Date(_mock.time(i)),
title: temp_events[i].name,
currency: 'HKD',
duration_m: 120,
ageBottom: 18,
ageTop: 60,
location: 'Hong Kong',
avatar: temp_events[i].images,
//
joinMembers: memberList(5 - i),
},
});
}
console.log('seed eventItem done');
}
const EventItem = eventItem()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { EventItem as EventItemSeed };

View File

@@ -0,0 +1,61 @@
import { _mock } from './_mock';
import { _tags } from './assets';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const generateAttachments = () => Array.from({ length: 20 }, (_, index) => _mock.image.event(index));
const generateReviews = () => {
const attachments = generateAttachments();
return Array.from({ length: 8 }, (_, index) => ({
// id: _mock.id(index),
name: _mock.fullName(index),
postedAt: _mock.time(index),
comment: _mock.sentence(index),
isPurchased: _mock.boolean(index),
rating: _mock.number.rating(index),
avatarUrl: _mock.image.avatar(index),
helpful: _mock.number.nativeL(index),
attachments: (index === 1 && attachments.slice(0, 1)) || (index === 3 && attachments.slice(2, 4)) || (index === 5 && attachments.slice(5, 8)) || [],
}));
};
export const _eventsReview = () => {
return generateReviews();
};
async function eventReview() {
const temp_pr = _eventsReview();
for (let i = 0; i < temp_pr.length; i++) {
const temp = await prisma.eventReview.upsert({
where: { id: i.toString() },
update: {},
create: {
name: temp_pr[i].name,
rating: temp_pr[i].rating,
comment: temp_pr[i].comment,
helpful: temp_pr[i].helpful,
avatarUrl: temp_pr[i].avatarUrl,
isPurchased: temp_pr[i].isPurchased,
attachments: temp_pr[i].attachments,
postedAt: temp_pr[i].postedAt,
},
});
}
console.log('seed eventReview done');
}
const EventReview = eventReview()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { EventReview as EventReviewSeed };

View File

@@ -4,16 +4,7 @@ import { _tags } from './assets';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const COLORS = [
'#FF4842',
'#1890FF',
'#FFC0CB',
'#00AB55',
'#FFC107',
'#7F00FF',
'#000000',
'#FFFFFF'
];
const COLORS = ['#FF4842', '#1890FF', '#FFC0CB', '#00AB55', '#FFC107', '#7F00FF', '#000000', '#FFFFFF'];
const DESCRIPTION = `
<h6>Specifications</h6>
@@ -113,8 +104,7 @@ const getColorSliceForIndex = (index: number) => {
return COLORS.slice(2, 6); // Default case
};
const generateAttachments = () =>
Array.from({ length: 20 }, (_, index) => _mock.image.product(index));
const generateAttachments = () => Array.from({ length: 20 }, (_, index) => _mock.image.product(index));
const generateReviews = () => {
const attachments = generateAttachments();
@@ -128,11 +118,7 @@ const generateReviews = () => {
rating: _mock.number.rating(index),
avatarUrl: _mock.image.avatar(index),
helpful: _mock.number.nativeL(index),
attachments:
(index === 1 && attachments.slice(0, 1)) ||
(index === 3 && attachments.slice(2, 4)) ||
(index === 5 && attachments.slice(5, 8)) ||
[]
attachments: (index === 1 && attachments.slice(0, 1)) || (index === 3 && attachments.slice(2, 4)) || (index === 5 && attachments.slice(5, 8)) || [],
}));
};
@@ -140,7 +126,7 @@ const generateRatings = () =>
Array.from({ length: 5 }, (_, index) => ({
name: `${index + 1} Star`,
starCount: _mock.number.nativeL(index),
reviewCount: _mock.number.nativeL(index + 1)
reviewCount: _mock.number.nativeL(index + 1),
}));
const generateImages = () => Array.from({ length: 8 }, (_, index) => _mock.image.product(index));
@@ -164,8 +150,8 @@ async function productReview() {
avatarUrl: temp_pr[i].avatarUrl,
isPurchased: temp_pr[i].isPurchased,
attachments: temp_pr[i].attachments,
postedAt: temp_pr[i].postedAt
}
postedAt: temp_pr[i].postedAt,
},
});
}
console.log('seed productReview done');

View File

@@ -8,8 +8,9 @@ async function superuser() {
create: {
email: 'admin1@123.com',
name: 'Admin1',
password: 'Aa12345678'
}
password: 'Aa12345678',
admin: true,
},
});
// swagger test
@@ -19,8 +20,9 @@ async function superuser() {
create: {
email: 'fake@example.com',
name: 'swagger user',
password: 'password1'
}
password: 'password1',
admin: true,
},
});
console.log('seed superuser done');

View File

@@ -9,16 +9,29 @@ async function user() {
email: 'alice@prisma.io',
name: 'Alice',
password: 'Aa12345678',
emailVerified: new Date(),
},
});
const bob = await prisma.user.upsert({
await prisma.user.upsert({
where: { email: 'demo@minimals.cc' },
update: {},
create: {
email: 'demo@minimals.cc',
name: 'Demo',
password: '@2Minimal',
emailVerified: new Date(),
},
});
await prisma.user.upsert({
where: { email: 'bob@prisma.io' },
update: {},
create: {
email: 'bob@prisma.io',
name: 'Bob',
password: 'Aa12345678',
emailVerified: new Date(),
},
});
console.log('seed user done');

View File

@@ -39,10 +39,12 @@ async function userItem() {
//
username: 'admin@123.com',
password: await generateHash('Aa1234567'),
//
isAdmin: true,
},
});
for (let i = 1; i < 20; i++) {
for (let i = 1; i < 3; i++) {
const CJK_LOCALES = {
en: enFaker,
zh: zhFaker,
@@ -77,6 +79,8 @@ async function userItem() {
//
username: randomFaker.internet.username(),
password: await generateHash('Abc1234!'),
//
isAdmin: false,
},
});
}