// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck // 批量创建开发测试租户脚本 import * as dotenv from 'dotenv'; import * as path from 'path'; // 根据 NODE_ENV 加载对应的环境配置文件 const nodeEnv = process.env.NODE_ENV || 'development'; const envFile = `.env.${nodeEnv}`; const backendDir = path.resolve(__dirname, '..'); const envPath = path.resolve(backendDir, envFile); dotenv.config({ path: envPath }); if (!process.env.DATABASE_URL) { dotenv.config({ path: path.resolve(backendDir, '.env') }); } if (!process.env.DATABASE_URL) { console.error('❌ 错误: 未找到 DATABASE_URL 环境变量'); process.exit(1); } import { PrismaClient } from '@prisma/client'; import * as bcrypt from 'bcrypt'; const prisma = new PrismaClient(); // ============================================ // 要创建的租户配置 // ============================================ const devTenants = [ { name: '学校管理端', code: 'school', description: '学校管理员端,管理学校信息、教师、学生等', roles: [ { code: 'school_admin', name: '学校管理员', description: '学校管理员', isDefault: true, permissions: [ 'workbench:read', 'user:create', 'user:read', 'user:update', 'user:delete', 'role:read', 'permission:read', 'school:create', 'school:read', 'school:update', 'department:create', 'department:read', 'department:update', 'department:delete', 'grade:create', 'grade:read', 'grade:update', 'grade:delete', 'class:create', 'class:read', 'class:update', 'class:delete', 'teacher:create', 'teacher:read', 'teacher:update', 'teacher:delete', 'student:create', 'student:read', 'student:update', 'student:delete', 'activity:read', 'notice:read', 'registration:read', 'work:read', 'homework:create', 'homework:read', 'homework:update', 'homework:delete', 'homework:publish', 'homework-submission:read', 'homework-review-rule:create', 'homework-review-rule:read', 'homework-review-rule:update', 'homework-review-rule:delete', 'homework-score:read', ], }, ], menus: ['工作台', '学校管理', '我的评审', '作业管理'], }, { name: '教师端', code: 'teacher', description: '教师端,可以报名活动、指导学生、管理作业', roles: [ { code: 'teacher', name: '教师', description: '教师角色', isDefault: true, permissions: [ 'workbench:read', 'grade:read', 'class:read', 'student:read', 'activity:read', 'activity:guidance', 'notice:read', 'registration:create', 'registration:read', 'registration:update', 'registration:delete', 'work:create', 'work:read', 'work:update', 'work:submit', 'homework:create', 'homework:read', 'homework:update', 'homework:delete', 'homework:publish', 'homework-submission:read', 'homework-review-rule:create', 'homework-review-rule:read', 'homework-review-rule:update', 'homework-review-rule:delete', 'homework-score:create', 'homework-score:read', ], }, ], menus: ['工作台', '我的评审', '作业管理'], }, { name: '学生端', code: 'student', description: '学生端,可以查看活动、上传作品、提交作业', roles: [ { code: 'student', name: '学生', description: '学生角色', isDefault: true, permissions: [ 'workbench:read', 'activity:read', 'notice:read', 'registration:read', 'work:create', 'work:read', 'work:update', 'work:submit', 'homework:read', 'homework-submission:create', 'homework-submission:read', 'homework-submission:update', 'homework-score:read', ], }, ], menus: ['工作台', '我的评审', '作业管理'], }, { name: '评委端', code: 'judge', description: '评委端,可以评审作品、打分', roles: [ { code: 'judge', name: '评委', description: '评委角色', isDefault: true, permissions: [ 'workbench:read', 'activity:read', 'notice:read', 'work:read', 'judge:read', 'judge:assign', 'review:read', 'review:create', 'review:update', ], }, ], menus: ['工作台', '我的评审'], }, ]; // ============================================ // 权限定义(完整列表) // ============================================ const allPermissions = [ // 工作台 { code: 'workbench:read', resource: 'workbench', action: 'read', name: '查看工作台', description: '允许查看工作台' }, // 用户管理 { code: 'user:create', resource: 'user', action: 'create', name: '创建用户', description: '允许创建新用户' }, { code: 'user:read', resource: 'user', action: 'read', name: '查看用户', description: '允许查看用户列表和详情' }, { code: 'user:update', resource: 'user', action: 'update', name: '更新用户', description: '允许更新用户信息' }, { code: 'user:delete', resource: 'user', action: 'delete', name: '删除用户', description: '允许删除用户' }, // 角色管理 { code: 'role:create', resource: 'role', action: 'create', name: '创建角色', description: '允许创建新角色' }, { code: 'role:read', resource: 'role', action: 'read', name: '查看角色', description: '允许查看角色列表和详情' }, { code: 'role:update', resource: 'role', action: 'update', name: '更新角色', description: '允许更新角色信息' }, { code: 'role:delete', resource: 'role', action: 'delete', name: '删除角色', description: '允许删除角色' }, { code: 'role:assign', resource: 'role', action: 'assign', name: '分配角色', description: '允许给用户分配角色' }, // 权限管理 { code: 'permission:read', resource: 'permission', action: 'read', name: '查看权限', description: '允许查看权限列表' }, // 学校管理 { code: 'school:create', resource: 'school', action: 'create', name: '创建学校', description: '允许创建学校信息' }, { code: 'school:read', resource: 'school', action: 'read', name: '查看学校', description: '允许查看学校信息' }, { code: 'school:update', resource: 'school', action: 'update', name: '更新学校', description: '允许更新学校信息' }, { code: 'school:delete', resource: 'school', action: 'delete', name: '删除学校', description: '允许删除学校信息' }, // 部门管理 { code: 'department:create', resource: 'department', action: 'create', name: '创建部门', description: '允许创建部门' }, { code: 'department:read', resource: 'department', action: 'read', name: '查看部门', description: '允许查看部门列表' }, { code: 'department:update', resource: 'department', action: 'update', name: '更新部门', description: '允许更新部门信息' }, { code: 'department:delete', resource: 'department', action: 'delete', name: '删除部门', description: '允许删除部门' }, // 年级管理 { code: 'grade:create', resource: 'grade', action: 'create', name: '创建年级', description: '允许创建年级' }, { code: 'grade:read', resource: 'grade', action: 'read', name: '查看年级', description: '允许查看年级列表' }, { code: 'grade:update', resource: 'grade', action: 'update', name: '更新年级', description: '允许更新年级信息' }, { code: 'grade:delete', resource: 'grade', action: 'delete', name: '删除年级', description: '允许删除年级' }, // 班级管理 { code: 'class:create', resource: 'class', action: 'create', name: '创建班级', description: '允许创建班级' }, { code: 'class:read', resource: 'class', action: 'read', name: '查看班级', description: '允许查看班级列表' }, { code: 'class:update', resource: 'class', action: 'update', name: '更新班级', description: '允许更新班级信息' }, { code: 'class:delete', resource: 'class', action: 'delete', name: '删除班级', description: '允许删除班级' }, // 教师管理 { code: 'teacher:create', resource: 'teacher', action: 'create', name: '创建教师', description: '允许创建教师' }, { code: 'teacher:read', resource: 'teacher', action: 'read', name: '查看教师', description: '允许查看教师列表' }, { code: 'teacher:update', resource: 'teacher', action: 'update', name: '更新教师', description: '允许更新教师信息' }, { code: 'teacher:delete', resource: 'teacher', action: 'delete', name: '删除教师', description: '允许删除教师' }, // 学生管理 { code: 'student:create', resource: 'student', action: 'create', name: '创建学生', description: '允许创建学生' }, { code: 'student:read', resource: 'student', action: 'read', name: '查看学生', description: '允许查看学生列表' }, { code: 'student:update', resource: 'student', action: 'update', name: '更新学生', description: '允许更新学生信息' }, { code: 'student:delete', resource: 'student', action: 'delete', name: '删除学生', description: '允许删除学生' }, // 我的评审 { code: 'activity:read', resource: 'activity', action: 'read', name: '查看我的评审', description: '允许查看已发布的我的评审' }, { code: 'activity:guidance', resource: 'activity', action: 'guidance', name: '指导学生', description: '允许指导学生参赛' }, // 活动报名 { code: 'registration:create', resource: 'registration', action: 'create', name: '创建报名', description: '允许报名活动' }, { code: 'registration:read', resource: 'registration', action: 'read', name: '查看报名', description: '允许查看报名记录' }, { code: 'registration:update', resource: 'registration', action: 'update', name: '更新报名', description: '允许更新报名信息' }, { code: 'registration:delete', resource: 'registration', action: 'delete', name: '取消报名', description: '允许取消报名' }, { code: 'registration:approve', resource: 'registration', action: 'approve', name: '审核报名', description: '允许审核报名' }, // 参赛作品 { code: 'work:create', resource: 'work', action: 'create', name: '上传作品', description: '允许上传参赛作品' }, { code: 'work:read', resource: 'work', action: 'read', name: '查看作品', description: '允许查看参赛作品' }, { code: 'work:update', resource: 'work', action: 'update', name: '更新作品', description: '允许更新作品信息' }, { code: 'work:delete', resource: 'work', action: 'delete', name: '删除作品', description: '允许删除作品' }, { code: 'work:submit', resource: 'work', action: 'submit', name: '提交作品', description: '允许提交作品' }, // 活动公告 { code: 'notice:read', resource: 'notice', action: 'read', name: '查看公告', description: '允许查看活动公告' }, // 评委管理 { code: 'judge:create', resource: 'judge', action: 'create', name: '创建评委', description: '允许创建评委' }, { code: 'judge:read', resource: 'judge', action: 'read', name: '查看评委', description: '允许查看评委' }, { code: 'judge:update', resource: 'judge', action: 'update', name: '更新评委', description: '允许更新评委' }, { code: 'judge:delete', resource: 'judge', action: 'delete', name: '删除评委', description: '允许删除评委' }, { code: 'judge:assign', resource: 'judge', action: 'assign', name: '分配评委', description: '允许分配评委' }, // 评审 { code: 'review:create', resource: 'review', action: 'create', name: '创建评审', description: '允许创建评审' }, { code: 'review:read', resource: 'review', action: 'read', name: '查看评审', description: '允许查看评审' }, { code: 'review:update', resource: 'review', action: 'update', name: '更新评审', description: '允许更新评审' }, { code: 'review:delete', resource: 'review', action: 'delete', name: '删除评审', description: '允许删除评审' }, // 作业管理 { code: 'homework:create', resource: 'homework', action: 'create', name: '创建作业', description: '允许创建作业' }, { code: 'homework:read', resource: 'homework', action: 'read', name: '查看作业', description: '允许查看作业列表' }, { code: 'homework:update', resource: 'homework', action: 'update', name: '更新作业', description: '允许更新作业信息' }, { code: 'homework:delete', resource: 'homework', action: 'delete', name: '删除作业', description: '允许删除作业' }, { code: 'homework:publish', resource: 'homework', action: 'publish', name: '发布作业', description: '允许发布作业' }, // 作业提交 { code: 'homework-submission:create', resource: 'homework-submission', action: 'create', name: '提交作业', description: '允许提交作业' }, { code: 'homework-submission:read', resource: 'homework-submission', action: 'read', name: '查看作业提交', description: '允许查看作业提交记录' }, { code: 'homework-submission:update', resource: 'homework-submission', action: 'update', name: '更新作业提交', description: '允许更新提交的作业' }, // 作业评审规则 { code: 'homework-review-rule:create', resource: 'homework-review-rule', action: 'create', name: '创建作业评审规则', description: '允许创建作业评审规则' }, { code: 'homework-review-rule:read', resource: 'homework-review-rule', action: 'read', name: '查看作业评审规则', description: '允许查看作业评审规则' }, { code: 'homework-review-rule:update', resource: 'homework-review-rule', action: 'update', name: '更新作业评审规则', description: '允许更新作业评审规则' }, { code: 'homework-review-rule:delete', resource: 'homework-review-rule', action: 'delete', name: '删除作业评审规则', description: '允许删除作业评审规则' }, // 作业评分 { code: 'homework-score:create', resource: 'homework-score', action: 'create', name: '作业评分', description: '允许对作业评分' }, { code: 'homework-score:read', resource: 'homework-score', action: 'read', name: '查看作业评分', description: '允许查看作业评分' }, ]; async function initDevTenants() { console.log('🚀 开始批量创建开发测试租户...\n'); try { // 获取所有菜单 const allMenus = await prisma.menu.findMany({ orderBy: [{ sort: 'asc' }, { id: 'asc' }], }); const results: any[] = []; for (const tenantConfig of devTenants) { console.log(`\n${'='.repeat(50)}`); console.log(`📦 创建租户: ${tenantConfig.name}`); console.log('='.repeat(50)); // 检查是否已存在 const existingTenant = await prisma.tenant.findFirst({ where: { code: tenantConfig.code } }); if (existingTenant) { console.log(` ⚠️ 租户 "${tenantConfig.code}" 已存在,跳过创建`); results.push({ name: tenantConfig.name, code: tenantConfig.code, status: 'skipped', username: 'admin', password: `admin@${tenantConfig.code}`, }); continue; } // 1. 创建租户 console.log('\n 🏢 步骤 1: 创建租户...'); const tenant = await prisma.tenant.create({ data: { name: tenantConfig.name, code: tenantConfig.code, description: tenantConfig.description, isSuper: 0, validState: 1, } }); console.log(` ✓ 租户创建成功: ${tenant.name} (${tenant.code})`); const tenantId = tenant.id; // 2. 创建权限 console.log('\n 📝 步骤 2: 创建权限...'); const createdPermissions: { [code: string]: number } = {}; for (const perm of allPermissions) { const existingPerm = await prisma.permission.findFirst({ where: { code: perm.code, tenantId } }); if (!existingPerm) { const permission = await prisma.permission.create({ data: { ...perm, tenantId, validState: 1 } }); createdPermissions[perm.code] = permission.id; } else { createdPermissions[perm.code] = existingPerm.id; } } console.log(` ✓ 共 ${Object.keys(createdPermissions).length} 个权限`); // 3. 创建角色并分配权限 console.log('\n 👥 步骤 3: 创建角色...'); let defaultRoleId: number | null = null; for (const roleConfig of tenantConfig.roles) { const role = await prisma.role.create({ data: { tenantId, name: roleConfig.name, code: roleConfig.code, description: roleConfig.description, validState: 1, } }); // 分配权限 for (const permCode of roleConfig.permissions) { const permissionId = createdPermissions[permCode]; if (permissionId) { await prisma.rolePermission.create({ data: { roleId: role.id, permissionId } }); } } if (roleConfig.isDefault) { defaultRoleId = role.id; } console.log(` ✓ 角色: ${role.name} (${roleConfig.permissions.length} 个权限)`); } // 4. 创建管理员用户 console.log('\n 👤 步骤 4: 创建管理员用户...'); const password = `admin@${tenantConfig.code}`; const hashedPassword = await bcrypt.hash(password, 10); const adminUser = await prisma.user.create({ data: { tenantId, username: 'admin', password: hashedPassword, nickname: `${tenantConfig.name}管理员`, validState: 1, } }); console.log(` ✓ 用户: admin`); // 5. 分配角色 if (defaultRoleId) { await prisma.userRole.create({ data: { userId: adminUser.id, roleId: defaultRoleId } }); console.log(` ✓ 已分配默认角色`); } // 6. 分配菜单 console.log('\n 📋 步骤 5: 分配菜单...'); const menuIds = new Set(); for (const menu of allMenus) { // 顶级菜单 if (!menu.parentId && tenantConfig.menus.includes(menu.name)) { menuIds.add(menu.id); } // 子菜单 if (menu.parentId) { const parentMenu = allMenus.find(m => m.id === menu.parentId); if (parentMenu && tenantConfig.menus.includes(parentMenu.name)) { menuIds.add(menu.id); } } } for (const menuId of menuIds) { await prisma.tenantMenu.create({ data: { tenantId: tenant.id, menuId } }); } console.log(` ✓ 分配 ${menuIds.size} 个菜单`); results.push({ name: tenantConfig.name, code: tenantConfig.code, status: 'created', username: 'admin', password: password, }); } // 输出汇总 console.log('\n\n' + '='.repeat(60)); console.log('🎉 开发测试租户创建完成!'); console.log('='.repeat(60)); console.log('\n📝 登录账号汇总:\n'); console.log('| 端\t\t| 租户编码\t| 用户名\t| 密码\t\t\t|'); console.log('|---------------|---------------|---------------|-----------------------|'); for (const r of results) { console.log(`| ${r.name}\t| ${r.code}\t\t| ${r.username}\t\t| ${r.password}\t\t|`); } console.log('\n'); } catch (error) { console.error('❌ 创建失败:', error); throw error; } finally { await prisma.$disconnect(); } } // 执行初始化 initDevTenants() .then(() => { console.log('✅ 脚本执行完成!'); process.exit(0); }) .catch((error) => { console.error('💥 脚本执行失败:', error); process.exit(1); });