import { PrismaClient, UserRole, TenantType, TenantStatus } from '@prisma/client'; import bcrypt from 'bcryptjs'; import { v4 as uuidv4 } from 'uuid'; const prisma = new PrismaClient(); // 默认密码 const DEFAULT_PASSWORD = '123456'; async function main() { console.log('🌱 开始创建种子数据...\n'); // 加密默认密码 const hashedDefaultPassword = await bcrypt.hash(DEFAULT_PASSWORD, 10); // ============================================ // 1. 创建内部租户(运营团队专用) // ============================================ console.log('📌 创建内部租户...'); const internalTenant = await prisma.tenants.upsert({ where: { code: 'yizhengxun' }, update: {}, create: { id: uuidv4(), code: 'yizhengxun', name: '壹证循科技', type: TenantType.INTERNAL, status: TenantStatus.ACTIVE, config: { logo: null, backgroundImage: null, primaryColor: '#1890ff', systemName: 'AI临床研究平台 - 运营管理端', }, total_quota: BigInt(999999999), updated_at: new Date(), }, }); console.log(` ✅ 内部租户创建成功: ${internalTenant.name}`); // 为内部租户开放所有模块(超级管理员完整权限) const internalModules = ['AIA', 'ASL', 'PKB', 'DC', 'SSA', 'ST', 'RVW', 'IIT', 'RM', 'AIA_PROTOCOL']; for (const moduleCode of internalModules) { await prisma.tenant_modules.upsert({ where: { tenant_id_module_code: { tenant_id: internalTenant.id, module_code: moduleCode } }, update: {}, create: { id: uuidv4(), tenant_id: internalTenant.id, module_code: moduleCode, is_enabled: true, }, }); } console.log(` ✅ 内部租户模块订阅: ${internalModules.join(', ')}`); // ============================================ // 1.5 创建公共租户(个人用户池) // ============================================ console.log('📌 创建公共租户(个人用户)...'); const publicTenant = await prisma.tenants.upsert({ where: { code: 'public' }, update: {}, create: { id: uuidv4(), code: 'public', name: '个人用户', type: TenantType.PUBLIC, status: TenantStatus.ACTIVE, config: { logo: null, backgroundImage: null, primaryColor: '#1890ff', systemName: 'AI临床研究平台', }, total_quota: BigInt(100000), updated_at: new Date(), }, }); console.log(` ✅ 公共租户创建成功: ${publicTenant.name}`); // 为公共租户开放部分模块 const publicModules = ['PKB', 'RVW']; for (const moduleCode of publicModules) { await prisma.tenant_modules.upsert({ where: { tenant_id_module_code: { tenant_id: publicTenant.id, module_code: moduleCode } }, update: {}, create: { id: uuidv4(), tenant_id: publicTenant.id, module_code: moduleCode, is_enabled: true, }, }); } console.log(` ✅ 公共租户模块订阅: ${publicModules.join(', ')}`); // ============================================ // 2. 创建超级管理员 // ============================================ console.log('📌 创建超级管理员...'); const superAdmin = await prisma.user.upsert({ where: { phone: '13800000001' }, update: {}, create: { phone: '13800000001', password: hashedDefaultPassword, is_default_password: false, name: '超级管理员', tenant_id: internalTenant.id, role: UserRole.SUPER_ADMIN, status: 'active', isTrial: false, }, }); console.log(` ✅ 超级管理员创建成功: ${superAdmin.phone}`); // ============================================ // 3. 创建Prompt工程师账号 // ============================================ console.log('📌 创建Prompt工程师账号...'); const promptEngineer = await prisma.user.upsert({ where: { phone: '13800000002' }, update: {}, create: { phone: '13800000002', password: hashedDefaultPassword, is_default_password: false, name: 'Prompt工程师', tenant_id: internalTenant.id, role: UserRole.PROMPT_ENGINEER, status: 'active', isTrial: false, }, }); console.log(` ✅ Prompt工程师创建成功: ${promptEngineer.phone}`); // ============================================ // 4. 创建示例租户(医院) // ============================================ console.log('📌 创建示例租户(医院)...'); const hospitalTenant = await prisma.tenants.upsert({ where: { code: 'demo-hospital' }, update: {}, create: { id: uuidv4(), code: 'demo-hospital', name: '示范医院', type: TenantType.HOSPITAL, status: TenantStatus.ACTIVE, config: { logo: null, backgroundImage: null, primaryColor: '#1890ff', systemName: '示范医院临床研究平台', }, total_quota: BigInt(1000000), contact_name: '张主任', contact_phone: '13800138000', contact_email: 'zhang@demo-hospital.com', updated_at: new Date(), }, }); console.log(` ✅ 示例医院租户创建成功: ${hospitalTenant.name}`); // ============================================ // 5. 创建医院科室 // ============================================ console.log('📌 创建医院科室...'); const cardiology = await prisma.departments.upsert({ where: { id: 'dept-cardiology' }, update: {}, create: { id: 'dept-cardiology', tenant_id: hospitalTenant.id, name: '心内科', description: '心血管内科', updated_at: new Date(), }, }); const neurology = await prisma.departments.upsert({ where: { id: 'dept-neurology' }, update: {}, create: { id: 'dept-neurology', tenant_id: hospitalTenant.id, name: '神经内科', description: '神经内科', updated_at: new Date(), }, }); console.log(` ✅ 科室创建成功: 心内科、神经内科`); // ============================================ // 6. 创建示例租户(药企) // ============================================ console.log('📌 创建示例租户(药企)...'); const pharmaTenant = await prisma.tenants.upsert({ where: { code: 'demo-pharma' }, update: {}, create: { id: uuidv4(), code: 'demo-pharma', name: '示范药企', type: TenantType.PHARMA, status: TenantStatus.ACTIVE, config: { logo: null, backgroundImage: null, primaryColor: '#52c41a', systemName: '示范药企IIT管理平台', }, total_quota: BigInt(2000000), contact_name: '李经理', contact_phone: '13900139000', contact_email: 'li@demo-pharma.com', updated_at: new Date(), }, }); console.log(` ✅ 示例药企租户创建成功: ${pharmaTenant.name}`); // ============================================ // 7. 创建医院管理员 // ============================================ console.log('📌 创建医院管理员...'); const hospitalAdmin = await prisma.user.upsert({ where: { phone: '13800138001' }, update: {}, create: { phone: '13800138001', password: hashedDefaultPassword, is_default_password: true, name: '张主任', tenant_id: hospitalTenant.id, department_id: cardiology.id, role: UserRole.HOSPITAL_ADMIN, status: 'active', isTrial: false, }, }); console.log(` ✅ 医院管理员创建成功: ${hospitalAdmin.phone} (${hospitalAdmin.name})`); // 创建租户成员关系 await prisma.tenant_members.upsert({ where: { tenant_id_user_id: { tenant_id: hospitalTenant.id, user_id: hospitalAdmin.id } }, update: {}, create: { id: uuidv4(), tenant_id: hospitalTenant.id, user_id: hospitalAdmin.id, role: UserRole.HOSPITAL_ADMIN, }, }); // ============================================ // 8. 创建普通医生用户 // ============================================ console.log('📌 创建普通医生用户...'); const doctor1 = await prisma.user.upsert({ where: { phone: '13800138002' }, update: {}, create: { phone: '13800138002', password: hashedDefaultPassword, is_default_password: true, name: '李医生', tenant_id: hospitalTenant.id, department_id: cardiology.id, role: UserRole.USER, status: 'active', isTrial: false, }, }); const doctor2 = await prisma.user.upsert({ where: { phone: '13800138003' }, update: {}, create: { phone: '13800138003', password: hashedDefaultPassword, is_default_password: true, name: '王医生', tenant_id: hospitalTenant.id, department_id: neurology.id, role: UserRole.USER, status: 'active', isTrial: false, }, }); console.log(` ✅ 普通用户创建成功: ${doctor1.name}、${doctor2.name}`); // 创建租户成员关系 for (const user of [doctor1, doctor2]) { await prisma.tenant_members.upsert({ where: { tenant_id_user_id: { tenant_id: hospitalTenant.id, user_id: user.id } }, update: {}, create: { id: uuidv4(), tenant_id: hospitalTenant.id, user_id: user.id, role: UserRole.USER, }, }); } // ============================================ // 9. 创建权限数据 // ============================================ console.log('📌 创建权限数据...'); const permissionsData = [ // Prompt管理权限 { code: 'prompt:view', name: '查看Prompt', description: '查看Prompt列表和历史版本', module: 'prompt' }, { code: 'prompt:edit', name: '编辑Prompt', description: '创建/修改DRAFT版本', module: 'prompt' }, { code: 'prompt:debug', name: '调试Prompt', description: '开启调试模式(核心权限)', module: 'prompt' }, { code: 'prompt:publish', name: '发布Prompt', description: '发布DRAFT为ACTIVE', module: 'prompt' }, // 租户管理权限 { code: 'tenant:view', name: '查看租户', description: '查看租户列表', module: 'tenant' }, { code: 'tenant:create', name: '创建租户', description: '创建新租户', module: 'tenant' }, { code: 'tenant:edit', name: '编辑租户', description: '编辑租户信息', module: 'tenant' }, { code: 'tenant:delete', name: '删除租户', description: '删除租户', module: 'tenant' }, // 用户管理权限 { code: 'user:view', name: '查看用户', description: '查看用户列表', module: 'user' }, { code: 'user:create', name: '创建用户', description: '创建新用户', module: 'user' }, { code: 'user:edit', name: '编辑用户', description: '编辑用户信息', module: 'user' }, { code: 'user:delete', name: '删除用户', description: '删除用户', module: 'user' }, // 配额管理权限 { code: 'quota:view', name: '查看配额', description: '查看配额使用情况', module: 'quota' }, { code: 'quota:allocate', name: '分配配额', description: '分配配额给科室/用户', module: 'quota' }, // 审计日志权限 { code: 'audit:view', name: '查看审计日志', description: '查看操作审计日志', module: 'audit' }, // 用户运营权限(可访问租户管理/用户管理/运营日志) { code: 'ops:user-ops', name: '用户运营', description: '运营管理端用户运营视图权限', module: 'ops' }, ]; for (const perm of permissionsData) { await prisma.permissions.upsert({ where: { code: perm.code }, update: {}, create: perm, }); } console.log(` ✅ ${permissionsData.length} 个权限创建成功`); // ============================================ // 10. 创建角色-权限关联 // ============================================ console.log('📌 创建角色-权限关联...'); const allPermissions = await prisma.permissions.findMany(); const permissionMap = new Map(allPermissions.map(p => [p.code, p.id])); // SUPER_ADMIN 拥有所有权限 for (const perm of allPermissions) { await prisma.role_permissions.upsert({ where: { role_permission_id: { role: UserRole.SUPER_ADMIN, permission_id: perm.id } }, update: {}, create: { role: UserRole.SUPER_ADMIN, permission_id: perm.id, }, }); } // PROMPT_ENGINEER 拥有Prompt相关权限 const promptPermCodes = ['prompt:view', 'prompt:edit', 'prompt:debug', 'prompt:publish']; for (const code of promptPermCodes) { const permId = permissionMap.get(code); if (permId) { await prisma.role_permissions.upsert({ where: { role_permission_id: { role: UserRole.PROMPT_ENGINEER, permission_id: permId } }, update: {}, create: { role: UserRole.PROMPT_ENGINEER, permission_id: permId, }, }); } } // HOSPITAL_ADMIN 拥有用户、配额、审计权限 const hospitalAdminPermCodes = ['user:view', 'user:create', 'user:edit', 'quota:view', 'quota:allocate', 'audit:view']; for (const code of hospitalAdminPermCodes) { const permId = permissionMap.get(code); if (permId) { await prisma.role_permissions.upsert({ where: { role_permission_id: { role: UserRole.HOSPITAL_ADMIN, permission_id: permId } }, update: {}, create: { role: UserRole.HOSPITAL_ADMIN, permission_id: permId, }, }); } } console.log(' ✅ 角色-权限关联创建成功'); // ============================================ // 11. 跳过Prompt模板(表尚未创建) // ============================================ console.log('📌 跳过Prompt模板创建(capability_schema.prompt_templates 尚未创建)'); console.log('💡 提示:Protocol Agent 配置请运行独立脚本: npx tsx prisma/seed-protocol-agent.ts'); // ============================================ // 12. 创建租户模块订阅 // ============================================ console.log('📌 创建租户模块订阅...'); const hospitalModules = ['ASL', 'DC', 'PKB', 'AIA', 'RVW']; for (const moduleCode of hospitalModules) { await prisma.tenant_modules.upsert({ where: { tenant_id_module_code: { tenant_id: hospitalTenant.id, module_code: moduleCode } }, update: {}, create: { id: uuidv4(), tenant_id: hospitalTenant.id, module_code: moduleCode, is_enabled: true, }, }); } // 药企只订阅IIT和DC const pharmaModules = ['IIT', 'DC']; for (const moduleCode of pharmaModules) { await prisma.tenant_modules.upsert({ where: { tenant_id_module_code: { tenant_id: pharmaTenant.id, module_code: moduleCode } }, update: {}, create: { id: uuidv4(), tenant_id: pharmaTenant.id, module_code: moduleCode, is_enabled: true, }, }); } console.log(' ✅ 租户模块订阅创建成功'); // ============================================ // 完成 // ============================================ console.log('\n🎉 种子数据创建完成!\n'); console.log('╔════════════════════════════════════════════════════════════╗'); console.log('║ 📝 测试账号信息 ║'); console.log('╠════════════════════════════════════════════════════════════╣'); console.log('║ 🔐 登录方式1:手机号 + 验证码 ║'); console.log('║ 🔐 登录方式2:手机号 + 密码 ║'); console.log('╠════════════════════════════════════════════════════════════╣'); console.log('║ 【运营管理员】 ║'); console.log('║ 超级管理员: 13800000001 / 123456 ║'); console.log('║ Prompt工程师: 13800000002 / 123456 ║'); console.log('╠════════════════════════════════════════════════════════════╣'); console.log('║ 【医院端 - demo-hospital】 ║'); console.log('║ 医院管理员: 13800138001 / 123456 (张主任) ║'); console.log('║ 普通医生: 13800138002 / 123456 (李医生·心内科) ║'); console.log('║ 普通医生: 13800138003 / 123456 (王医生·神内科) ║'); console.log('╠════════════════════════════════════════════════════════════╣'); console.log('║ 【药企端 - demo-pharma】 ║'); console.log('║ (暂无用户,可通过管理端添加) ║'); console.log('╠════════════════════════════════════════════════════════════╣'); console.log('║ 【个人用户 - public】 ║'); console.log('║ 通用登录入口: /login ║'); console.log('║ 可用模块: PKB, RVW ║'); console.log('╚════════════════════════════════════════════════════════════╝'); console.log('\n📌 租户专属登录URL:'); console.log(' - 通用登录: /login'); console.log(' - 医院端: /t/demo-hospital/login'); console.log(' - 药企端: /t/demo-pharma/login'); console.log('\n⚠️ 提示:普通用户首次登录会提示修改默认密码'); } main() .then(async () => { await prisma.$disconnect(); }) .catch(async (e) => { console.error('❌ 种子数据创建失败:', e); await prisma.$disconnect(); process.exit(1); });