flnxi-Wu/prisma/seed.ts

218 lines
6.0 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.

/**
* Prisma 数据库种子脚本
*
* 用于初始化数据库的基础数据,包括:
* 1. 内置模板
* 2. 默认管理员账户(可选)
* 3. 默认部门、店铺、场景
*
* 使用方法:
* npx prisma db seed
*
* 或在 package.json 中配置后自动执行:
* "prisma": {
* "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
* }
*/
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcryptjs'
import {
BUILTIN_TEMPLATE_KEYS,
createBuiltinTemplate,
} from '../lib/upload-template-presets'
const prisma = new PrismaClient()
// ============================================================================
// 种子函数
// ============================================================================
async function seedBuiltinTemplates() {
console.log('正在创建内置模板...')
let created = 0
let skipped = 0
for (const key of BUILTIN_TEMPLATE_KEYS) {
const template = createBuiltinTemplate(key)
const templateId = template.id
// 检查是否已存在
const existing = await prisma.uploadTemplate.findUnique({
where: { id: templateId },
})
if (existing) {
console.log(` 跳过: ${templateId} (已存在)`)
skipped++
continue
}
// 创建模板和字段
await prisma.$transaction(async (tx) => {
await tx.uploadTemplate.create({
data: {
id: templateId,
name: template.name,
description: template.description,
status: 'active',
materialType: template.materialType,
materialKind: template.materialKind,
version: 1,
},
})
for (let i = 0; i < template.fields.length; i++) {
const field = template.fields[i]
await tx.uploadField.create({
data: {
templateId,
key: field.key,
label: field.label,
type: field.type,
required: field.required,
orderIndex: i,
layoutWidth: field.layoutWidth,
visibleOnHomeFilter: field.visibleOnHomeFilter,
isSearchable: field.isSearchable,
addToCategory: field.addToCategory || false,
},
})
}
})
console.log(` 创建: ${templateId}`)
created++
}
console.log(`内置模板: 创建 ${created} 个, 跳过 ${skipped} 个`)
}
async function seedDefaultAdmin() {
console.log('正在检查默认管理员...')
const adminEmail = process.env.INIT_ADMIN_EMAIL || 'admin@local.flnxi'
const configuredPassword = process.env.INIT_ADMIN_PASSWORD?.trim()
// 检查是否已有管理员
const existingAdmin = await prisma.user.findFirst({
where: { role: 'ADMIN' },
})
if (existingAdmin) {
console.log(` 跳过: 已存在管理员 (${existingAdmin.email})`)
return
}
if (!configuredPassword) {
console.log(' 跳过: 未设置 INIT_ADMIN_PASSWORD,不在 seed 日志中生成或输出管理员密码')
console.log(' 请通过系统激活/初始化流程生成一次性管理员密码,或设置 INIT_ADMIN_PASSWORD 后重新执行 seed')
return
}
// 创建默认管理员
const hashedPassword = await bcrypt.hash(configuredPassword, 10)
const admin = await prisma.user.create({
data: {
email: adminEmail,
username: adminEmail.split('@')[0],
password: hashedPassword,
role: 'ADMIN',
isSuperAdmin: true,
isActive: true,
canDownload: true,
department: '系统管理',
},
})
console.log(` 创建: 管理员 ${admin.email}`)
await prisma.systemConfig.upsert({
where: { key: 'initial_admin_state' },
update: {
value: JSON.stringify({
userId: admin.id,
email: admin.email,
mustChangePassword: true,
createdAt: new Date().toISOString(),
}),
},
create: {
key: 'initial_admin_state',
value: JSON.stringify({
userId: admin.id,
email: admin.email,
mustChangePassword: true,
createdAt: new Date().toISOString(),
}),
},
})
console.log(' 初始管理员密码来源: 环境变量 INIT_ADMIN_PASSWORD(已写入哈希,不在日志中输出明文)')
}
async function seedDefaultData() {
console.log('正在创建默认数据...')
// 默认部门
const dept = await prisma.department.findFirst({ where: { name: '默认部门' } })
if (!dept) {
await prisma.department.create({ data: { name: '默认部门' } })
console.log(' 创建: 默认部门')
} else {
console.log(' 跳过: 默认部门 (已存在)')
}
// 默认店铺
const category = await prisma.category.findFirst({ where: { name: '默认店铺' } })
if (!category) {
await prisma.category.create({ data: { name: '默认店铺' } })
console.log(' 创建: 默认店铺')
} else {
console.log(' 跳过: 默认店铺 (已存在)')
}
// 默认场景
const scene = await prisma.scene.findFirst({ where: { name: '默认场景' } })
if (!scene) {
await prisma.scene.create({ data: { name: '默认场景' } })
console.log(' 创建: 默认场景')
} else {
console.log(' 跳过: 默认场景 (已存在)')
}
}
// ============================================================================
// 主函数
// ============================================================================
async function main() {
console.log('========================================')
console.log('开始数据库种子初始化')
console.log('========================================\n')
try {
await seedBuiltinTemplates()
console.log('')
await seedDefaultAdmin()
console.log('')
await seedDefaultData()
console.log('')
console.log('========================================')
console.log('数据库种子初始化完成')
console.log('========================================')
} catch (error) {
console.error('种子初始化失败:', error)
throw error
}
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})