218 lines
6.0 KiB
TypeScript
218 lines
6.0 KiB
TypeScript
/**
|
||
* 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()
|
||
})
|