// 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'; import * as readline from 'readline'; const prisma = new PrismaClient(); // ============================================ // 权限定义 // ============================================ 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: 'menu:read', resource: 'menu', 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: '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: '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: '允许查看作业评分' }, ]; // ============================================ // 角色定义 // ============================================ const normalTenantRoles = [ { code: 'school_admin', name: '学校管理员', description: '学校管理员,管理学校信息、教师、学生等', permissions: [ 'workbench:read', 'user:create', 'user:read', 'user:update', 'user:delete', 'role:create', 'role:read', 'role:update', 'role:delete', 'role:assign', 'permission:read', 'menu:read', // 学校管理 'school:create', 'school:read', 'school:update', 'school:delete', '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', ], }, { code: 'teacher', name: '教师', description: '教师角色,可以报名赛事、指导学生、管理作业', 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', ], }, { code: 'student', name: '学生', description: '学生角色,可以查看赛事、上传作品、提交作业', 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', ], }, ]; // 创建 readline 接口用于用户输入 function createReadlineInterface(): readline.Interface { return readline.createInterface({ input: process.stdin, output: process.stdout, }); } // 提示用户输入 function prompt(rl: readline.Interface, question: string): Promise { return new Promise((resolve) => { rl.question(question, (answer) => { resolve(answer.trim()); }); }); } async function initTenant() { const rl = createReadlineInterface(); try { console.log('🚀 开始创建普通租户...\n'); // 获取租户信息 const tenantName = await prompt(rl, '请输入租户名称: '); if (!tenantName) { console.error('❌ 错误: 租户名称不能为空'); process.exit(1); } const tenantCode = await prompt(rl, '请输入租户编码(英文): '); if (!tenantCode) { console.error('❌ 错误: 租户编码不能为空'); process.exit(1); } // 检查租户编码是否已存在 const existingTenant = await prisma.tenant.findFirst({ where: { code: tenantCode } }); if (existingTenant) { console.error(`❌ 错误: 租户编码 "${tenantCode}" 已存在`); process.exit(1); } rl.close(); // 1. 创建租户 console.log('\n🏢 步骤 1: 创建租户...'); const tenant = await prisma.tenant.create({ data: { name: tenantName, code: tenantCode, 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 permission = await prisma.permission.create({ data: { ...perm, tenantId, validState: 1 } }); createdPermissions[perm.code] = permission.id; } console.log(` ✓ 共创建 ${Object.keys(createdPermissions).length} 个权限`); // 3. 创建角色并分配权限 console.log('\n👥 步骤 3: 创建角色并分配权限...'); const createdRoles: any[] = []; for (const roleConfig of normalTenantRoles) { // 创建角色 const role = await prisma.role.create({ data: { tenantId, name: roleConfig.name, code: roleConfig.code, description: roleConfig.description, validState: 1, } }); // 分配权限给角色 let permCount = 0; for (const permCode of roleConfig.permissions) { const permissionId = createdPermissions[permCode]; if (permissionId) { await prisma.rolePermission.create({ data: { roleId: role.id, permissionId, } }); permCount++; } } createdRoles.push({ ...role, permCount }); console.log(` ✓ 创建角色: ${role.name} (${role.code}) - ${permCount} 个权限`); } // 4. 创建 admin 用户 console.log('\n👤 步骤 4: 创建 admin 用户...'); const password = `admin@${tenant.code}`; const hashedPassword = await bcrypt.hash(password, 10); const adminUser = await prisma.user.create({ data: { tenantId, username: 'admin', password: hashedPassword, nickname: '管理员', validState: 1, } }); console.log(` ✓ 创建用户: ${adminUser.username}`); // 5. 给用户分配 school_admin 角色 console.log('\n🔗 步骤 5: 分配角色给用户...'); const schoolAdminRole = createdRoles.find(r => r.code === 'school_admin'); if (schoolAdminRole) { await prisma.userRole.create({ data: { userId: adminUser.id, roleId: schoolAdminRole.id, } }); console.log(` ✓ 分配角色: ${schoolAdminRole.name}`); } // 6. 分配菜单给租户 console.log('\n📋 步骤 6: 分配菜单给租户...'); // 普通租户可见的菜单 const NORMAL_TENANT_MENUS = ['工作台', '学校管理', '赛事活动', '作业管理', '系统管理']; const NORMAL_TENANT_EXCLUDED_SYSTEM_MENUS = ['租户管理', '数据字典', '系统配置', '日志记录', '菜单管理', '权限管理']; const NORMAL_TENANT_EXCLUDED_ACTIVITY_MENUS = ['我的报名', '我的作品']; const allMenus = await prisma.menu.findMany({ orderBy: [{ sort: 'asc' }, { id: 'asc' }], }); const normalTenantMenuIds = new Set(); for (const menu of allMenus) { // 顶级菜单 if (!menu.parentId && NORMAL_TENANT_MENUS.includes(menu.name)) { normalTenantMenuIds.add(menu.id); } // 子菜单 if (menu.parentId) { const parentMenu = allMenus.find(m => m.id === menu.parentId); if (parentMenu && NORMAL_TENANT_MENUS.includes(parentMenu.name)) { // 系统管理下排除部分子菜单 if (parentMenu.name === '系统管理' && NORMAL_TENANT_EXCLUDED_SYSTEM_MENUS.includes(menu.name)) { continue; } // 赛事活动下排除部分子菜单(只保留活动列表) if (parentMenu.name === '赛事活动' && NORMAL_TENANT_EXCLUDED_ACTIVITY_MENUS.includes(menu.name)) { continue; } normalTenantMenuIds.add(menu.id); } } } let menuCount = 0; for (const menuId of normalTenantMenuIds) { await prisma.tenantMenu.create({ data: { tenantId: tenant.id, menuId: menuId, } }); menuCount++; } console.log(` ✓ 分配 ${menuCount} 个菜单`); // 7. 输出结果 console.log('\n' + '='.repeat(50)); console.log('🎉 普通租户创建完成!'); console.log('='.repeat(50)); console.log(` 租户名称: ${tenant.name}`); console.log(` 租户编码: ${tenant.code}`); console.log(` 用户名: admin`); console.log(` 密码: ${password}`); console.log(` 权限数量: ${Object.keys(createdPermissions).length}`); console.log(` 菜单数量: ${menuCount}`); console.log('\n 📊 角色列表:'); for (const role of createdRoles) { console.log(` - ${role.name} (${role.code}): ${role.permCount} 个权限`); } console.log('='.repeat(50)); } catch (error) { console.error('❌ 创建失败:', error); throw error; } finally { await prisma.$disconnect(); } } // 执行初始化 initTenant() .then(() => { console.log('\n✅ 租户创建脚本执行完成!'); process.exit(0); }) .catch((error) => { console.error('\n💥 租户创建脚本执行失败:', error); process.exit(1); });