feat(catalog): add product catalog APIs

This commit is contained in:
lai_hong
2026-09-13 11:17:53 +08:00
parent 63c0013076
commit 59e398fcf9
15 changed files with 655 additions and 68 deletions
+32 -39
View File
@@ -1,46 +1,39 @@
import { PrismaClient } from '@prisma/client';
import { Prisma, PrismaClient, ProductStatus } from '@prisma/client';
const prisma = new PrismaClient();
const oss = 'https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com';
const catalog = [
{ id: 'notebook-small', categoryId: 'cat-notebook-small', categoryName: '微雕笔记本(小)', sort: 1, name: '微雕笔记本(小)', price: 12, originalPrice: 18, leadTime: '3-5个工作日', subtitle: '掌心间的专属印记 · 激光微雕', iconImg: '/icon/书本.png', description: '便携 A6 笔记本,PU 封面可激光微雕名字或图案。', story: '让纸笔的温度留下专属印记。', scene: '课堂笔记、旅行手账、日常备忘。', tags: ['伴手礼', '学生', '手账'], specs: [['尺寸', '105 × 148mmA6'], ['材质', '高档PU皮革']], tone: [28, 22, 18], mask: { shape: 'rect', width: 300, height: 420, borderRadius: 8 }, images: [`${oss}/img/book_small/The1.jpg`] },
{ id: 'notebook-large', categoryId: 'cat-notebook-large', categoryName: '微雕笔记本(大)', sort: 2, name: '微雕笔记本(大)', price: 45, originalPrice: 58, leadTime: '3-5个工作日', subtitle: '大篇幅定制首选 · 180°平摊', iconImg: '/icon/书本_大.png', description: 'A4 大开本,适合呈现班级名单、团队口号或公司 Logo。', story: '为一群人的记忆留下郑重的书写空间。', scene: '毕业纪念、企业礼品、团队伴手礼。', tags: ['毕业礼', '团建', '企业定制'], specs: [['尺寸', '210 × 297mmA4'], ['开合', '180°平摊']], tone: [35, 28, 22], mask: { shape: 'rect', width: 340, height: 480, borderRadius: 8 }, images: [`${oss}/img/book_big/The1.jpg`, `${oss}/img/book_big/The2.jpg`] },
{ id: 'coaster', categoryId: 'cat-coaster', categoryName: '铜质杯垫', sort: 3, name: '铜质杯垫', price: 25, originalPrice: 35, leadTime: '5-7个工作日', subtitle: '黄铜质感 · 拉丝哑光', iconImg: '/icon/杯子.png', description: '拉丝黄铜与软木防滑垫组合,适合刻印名字、日期或祝福。', story: '让日常器物留下温柔的氧化与记忆。', scene: '乔迁礼、新婚回礼、办公桌布置。', tags: ['乔迁礼', '新婚贺礼', '桌面美学'], specs: [['直径', '100mm'], ['材质', '黄铜 + 软木']], tone: [42, 30, 18], mask: { shape: 'circle', width: 280, height: 280 }, images: [`${oss}/img/cup/The1.jpg`] },
{ id: 'penbox', categoryId: 'cat-penbox', categoryName: '竹制笔盒', sort: 4, name: '竹制笔盒', price: 75, originalPrice: 98, leadTime: '7-10个工作日', subtitle: '天然楠竹 · 磁吸开合', iconImg: '/icon/笔盒.png', description: '天然楠竹打磨抛光,磁吸盒盖与分层隔板兼具实用和雅致。', story: '每一道竹纹都不重复,也值得留下不同的名字。', scene: '书房案头、教师节礼物、书法爱好者。', tags: ['文房', '书法', '教师节'], specs: [['尺寸', '200 × 65 × 30mm'], ['材质', '天然楠竹']], tone: [30, 24, 16], mask: { shape: 'rect', width: 320, height: 160, borderRadius: 12 }, images: [`${oss}/img/penbox/The1.jpg`, `${oss}/img/penbox/The2.jpg`] },
{ id: 'booklamp', categoryId: 'cat-booklamp', categoryName: '书本型灯', sort: 5, name: '书本型灯', price: 45, originalPrice: 68, leadTime: '5-7个工作日', subtitle: '开合即亮 · LED暖光', iconImg: '/icon/书灯.png', description: '打开即亮的暖光书灯,封面可定制文字、图案或照片。', story: '打开一本书,也打开一束为你而亮的光。', scene: '情侣礼物、毕业纪念、床头夜灯。', tags: ['情侣礼', '毕业纪念', '夜灯'], specs: [['光源', 'LED 暖白光(3000K'], ['续航', '6-8小时']], tone: [25, 20, 15], mask: { shape: 'rect', width: 320, height: 240, borderRadius: 4 }, images: [`${oss}/img/booklight/The1.jpg`, `${oss}/img/booklight/The2.jpg`] },
];
async function main() {
// 分类
const apparel = await prisma.category.upsert({
where: { id: 'cat-apparel' },
update: {},
create: { id: 'cat-apparel', name: '服饰', sort: 1 },
});
const tshirt = await prisma.category.upsert({
where: { id: 'cat-tshirt' },
update: {},
create: { id: 'cat-tshirt', name: 'T恤', parentId: apparel.id, sort: 1 },
});
// 示例商品
await prisma.product.upsert({
where: { id: 'prod-demo-tshirt' },
update: {},
create: {
id: 'prod-demo-tshirt',
name: '示例定制 T恤',
categoryId: tshirt.id,
price: 99.0,
images: [],
description: '用于本地开发联调的示例商品,可在此设计清单中上传自定义图案。',
status: 'ON_SALE',
},
});
for (const product of catalog) {
await prisma.category.upsert({
where: { id: product.categoryId },
update: { name: product.categoryName, sort: product.sort },
create: { id: product.categoryId, name: product.categoryName, sort: product.sort },
});
const { categoryName, ...data } = product;
if (!categoryName) throw new Error('分类名称不能为空');
const productData: Prisma.ProductUncheckedCreateInput = {
...data,
specs: data.specs as Prisma.InputJsonValue,
mask: data.mask as Prisma.InputJsonValue,
status: ProductStatus.ON_SALE,
};
await prisma.product.upsert({ where: { id: product.id }, update: productData, create: productData });
}
// eslint-disable-next-line no-console
console.log('Seed 完成:分类 + 示例商品已写入');
console.log('Seed 完成:5 个在售商品与稳定分类已写入');
}
main()
.catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
main().catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}).finally(async () => prisma.$disconnect());