2026-03-27 22:20:25 +08:00
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
|
// @ts-nocheck
|
|
|
|
|
|
// 加载环境变量(必须在其他导入之前)
|
|
|
|
|
|
import * as dotenv from 'dotenv';
|
|
|
|
|
|
import * as path from 'path';
|
|
|
|
|
|
import * as fs from 'fs';
|
|
|
|
|
|
|
|
|
|
|
|
// 根据 NODE_ENV 加载对应的环境配置文件
|
|
|
|
|
|
const nodeEnv = process.env.NODE_ENV || 'development';
|
|
|
|
|
|
const envFile = `.env.${nodeEnv}`;
|
|
|
|
|
|
// scripts 目录的父目录就是 backend 目录
|
|
|
|
|
|
const backendDir = path.resolve(__dirname, '..');
|
|
|
|
|
|
const envPath = path.resolve(backendDir, envFile);
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试加载环境特定的配置文件
|
|
|
|
|
|
dotenv.config({ path: envPath });
|
|
|
|
|
|
|
|
|
|
|
|
// 如果环境特定文件不存在,尝试加载默认的 .env 文件
|
|
|
|
|
|
if (!process.env.DATABASE_URL) {
|
|
|
|
|
|
dotenv.config({ path: path.resolve(backendDir, '.env') });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证必要的环境变量
|
|
|
|
|
|
if (!process.env.DATABASE_URL) {
|
|
|
|
|
|
console.error('❌ 错误: 未找到 DATABASE_URL 环境变量');
|
|
|
|
|
|
console.error(` 请确保存在以下文件之一:`);
|
|
|
|
|
|
console.error(` - ${envPath}`);
|
|
|
|
|
|
console.error(` - ${path.resolve(backendDir, '.env')}`);
|
|
|
|
|
|
console.error(` 或者设置 NODE_ENV 环境变量(当前: ${nodeEnv})`);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
import { PrismaClient } from '@prisma/client';
|
|
|
|
|
|
import * as bcrypt from 'bcrypt';
|
|
|
|
|
|
|
|
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
|
|
|
|
|
|
|
|
// 从 JSON 文件加载权限数据
|
|
|
|
|
|
const permissionsFilePath = path.resolve(backendDir, 'data', 'permissions.json');
|
|
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(permissionsFilePath)) {
|
|
|
|
|
|
console.error(`❌ 错误: 权限数据文件不存在: ${permissionsFilePath}`);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const permissions = JSON.parse(fs.readFileSync(permissionsFilePath, 'utf-8'));
|
|
|
|
|
|
|
|
|
|
|
|
async function initTenantMenuAndPermissions(tenantCode: string) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log(`🚀 开始为租户 "${tenantCode}" 初始化菜单和权限...\n`);
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 查找或创建租户
|
|
|
|
|
|
console.log(`📋 步骤 1: 查找或创建租户 "${tenantCode}"...`);
|
|
|
|
|
|
let tenant = await prisma.tenant.findUnique({
|
|
|
|
|
|
where: { code: tenantCode },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!tenant) {
|
|
|
|
|
|
// 创建租户
|
|
|
|
|
|
tenant = await prisma.tenant.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
name: `${tenantCode} 租户`,
|
|
|
|
|
|
code: tenantCode,
|
|
|
|
|
|
domain: tenantCode,
|
|
|
|
|
|
description: `租户 ${tenantCode}`,
|
|
|
|
|
|
isSuper: 0,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ 租户创建成功: ${tenant.name} (${tenant.code})\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (tenant.validState !== 1) {
|
|
|
|
|
|
console.error(`❌ 错误: 租户 "${tenantCode}" 状态无效!`);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log(`✅ 找到租户: ${tenant.name} (${tenant.code})\n`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 查找或创建 admin 用户
|
|
|
|
|
|
console.log(`👤 步骤 2: 查找或创建 admin 用户...`);
|
|
|
|
|
|
let adminUser = await prisma.user.findFirst({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
username: 'admin',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const password = `admin@${tenantCode}`;
|
|
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
|
|
|
|
|
|
|
|
if (!adminUser) {
|
|
|
|
|
|
adminUser = await prisma.user.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
username: 'admin',
|
|
|
|
|
|
password: hashedPassword,
|
|
|
|
|
|
nickname: '管理员',
|
|
|
|
|
|
email: `admin@${tenantCode}.com`,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ admin 用户创建成功: ${adminUser.username}\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 更新密码(确保密码是最新的)
|
|
|
|
|
|
adminUser = await prisma.user.update({
|
|
|
|
|
|
where: { id: adminUser.id },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
password: hashedPassword,
|
|
|
|
|
|
nickname: '管理员',
|
|
|
|
|
|
email: `admin@${tenantCode}.com`,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ admin 用户已存在: ${adminUser.username}\n`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 查找或创建 admin 角色
|
|
|
|
|
|
console.log(`👤 步骤 3: 查找或创建 admin 角色...`);
|
|
|
|
|
|
let adminRole = await prisma.role.findFirst({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
code: 'admin',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!adminRole) {
|
|
|
|
|
|
adminRole = await prisma.role.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
name: '管理员',
|
|
|
|
|
|
code: 'admin',
|
|
|
|
|
|
description: '租户管理员角色,拥有租户的所有权限',
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ admin 角色创建成功: ${adminRole.name} (${adminRole.code})\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
adminRole = await prisma.role.update({
|
|
|
|
|
|
where: { id: adminRole.id },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
name: '管理员',
|
|
|
|
|
|
description: '租户管理员角色,拥有租户的所有权限',
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ admin 角色已存在: ${adminRole.name} (${adminRole.code})\n`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 为 admin 用户分配 admin 角色
|
|
|
|
|
|
console.log(`🔗 步骤 4: 为 admin 用户分配 admin 角色...`);
|
|
|
|
|
|
const existingUserRole = await prisma.userRole.findUnique({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
userId_roleId: {
|
|
|
|
|
|
userId: adminUser.id,
|
|
|
|
|
|
roleId: adminRole.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!existingUserRole) {
|
|
|
|
|
|
await prisma.userRole.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
userId: adminUser.id,
|
|
|
|
|
|
roleId: adminRole.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`✅ 角色分配成功\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`✅ 用户已拥有 admin 角色\n`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 初始化租户权限(如果不存在则创建)
|
|
|
|
|
|
console.log(`📝 步骤 5: 初始化租户权限...`);
|
|
|
|
|
|
const createdPermissions = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const perm of permissions) {
|
|
|
|
|
|
// 检查权限是否已存在
|
|
|
|
|
|
const existingPermission = await prisma.permission.findFirst({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
code: perm.code,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!existingPermission) {
|
|
|
|
|
|
// 创建权限
|
|
|
|
|
|
const permission = await prisma.permission.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
code: perm.code,
|
|
|
|
|
|
resource: perm.resource,
|
|
|
|
|
|
action: perm.action,
|
|
|
|
|
|
name: perm.name,
|
|
|
|
|
|
description: perm.description,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
createdPermissions.push(permission);
|
|
|
|
|
|
console.log(` ✓ 创建权限: ${perm.code} - ${perm.name}`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 更新现有权限(确保信息是最新的)
|
|
|
|
|
|
const permission = await prisma.permission.update({
|
|
|
|
|
|
where: { id: existingPermission.id },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
name: perm.name,
|
|
|
|
|
|
resource: perm.resource,
|
|
|
|
|
|
action: perm.action,
|
|
|
|
|
|
description: perm.description,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
createdPermissions.push(permission);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`✅ 共确保 ${createdPermissions.length} 个权限存在\n`);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取租户的所有有效权限
|
|
|
|
|
|
const tenantPermissions = await prisma.permission.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 为 admin 角色分配所有权限
|
|
|
|
|
|
console.log(`🔗 步骤 6: 为 admin 角色分配所有权限...`);
|
|
|
|
|
|
const existingRolePermissions = await prisma.rolePermission.findMany({
|
|
|
|
|
|
where: { roleId: adminRole.id },
|
|
|
|
|
|
select: { permissionId: true },
|
|
|
|
|
|
});
|
|
|
|
|
|
const existingPermissionIds = new Set(
|
|
|
|
|
|
existingRolePermissions.map((rp) => rp.permissionId),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let addedPermissionCount = 0;
|
|
|
|
|
|
for (const permission of tenantPermissions) {
|
|
|
|
|
|
if (!existingPermissionIds.has(permission.id)) {
|
|
|
|
|
|
await prisma.rolePermission.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
roleId: adminRole.id,
|
|
|
|
|
|
permissionId: permission.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
addedPermissionCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (addedPermissionCount > 0) {
|
|
|
|
|
|
console.log(`✅ 为 admin 角色添加了 ${addedPermissionCount} 个权限`);
|
|
|
|
|
|
console.log(`✅ admin 角色现在拥有 ${tenantPermissions.length} 个权限\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`✅ admin 角色已拥有所有权限(${tenantPermissions.length} 个)\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 为租户分配所有菜单
|
|
|
|
|
|
console.log(`📋 步骤 7: 为租户分配所有菜单...`);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有有效菜单
|
|
|
|
|
|
const allMenus = await prisma.menu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
orderBy: [{ sort: 'asc' }, { id: 'asc' }],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (allMenus.length === 0) {
|
|
|
|
|
|
console.log('⚠️ 警告: 数据库中没有任何菜单');
|
|
|
|
|
|
console.log(' 请先运行 pnpm init:menus 初始化菜单\n');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(` 找到 ${allMenus.length} 个菜单\n`);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取租户已分配的菜单
|
|
|
|
|
|
const existingTenantMenus = await prisma.tenantMenu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
select: {
|
|
|
|
|
|
menuId: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const existingMenuIds = new Set(existingTenantMenus.map((tm) => tm.menuId));
|
|
|
|
|
|
|
|
|
|
|
|
// 为租户分配所有菜单
|
|
|
|
|
|
let addedMenuCount = 0;
|
|
|
|
|
|
const menuNames: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const menu of allMenus) {
|
|
|
|
|
|
if (!existingMenuIds.has(menu.id)) {
|
|
|
|
|
|
await prisma.tenantMenu.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
menuId: menu.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
addedMenuCount++;
|
|
|
|
|
|
menuNames.push(menu.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (addedMenuCount > 0) {
|
|
|
|
|
|
console.log(`✅ 为租户添加了 ${addedMenuCount} 个菜单:`);
|
|
|
|
|
|
menuNames.forEach((name) => {
|
|
|
|
|
|
console.log(` ✓ ${name}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(`\n✅ 租户现在拥有 ${allMenus.length} 个菜单\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`✅ 租户已拥有所有菜单(${allMenus.length} 个)\n`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 验证结果
|
|
|
|
|
|
console.log('🔍 步骤 8: 验证结果...');
|
|
|
|
|
|
const userWithRoles = await prisma.user.findUnique({
|
|
|
|
|
|
where: { id: adminUser.id },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
roles: {
|
|
|
|
|
|
include: {
|
|
|
|
|
|
role: {
|
|
|
|
|
|
include: {
|
|
|
|
|
|
permissions: {
|
|
|
|
|
|
include: {
|
|
|
|
|
|
permission: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const roleCodes = userWithRoles?.roles.map((ur) => ur.role.code) || [];
|
|
|
|
|
|
const permissionCodes = new Set<string>();
|
|
|
|
|
|
userWithRoles?.roles.forEach((ur) => {
|
|
|
|
|
|
ur.role.permissions.forEach((rp) => {
|
|
|
|
|
|
permissionCodes.add(rp.permission.code);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const finalMenus = await prisma.tenantMenu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
include: {
|
|
|
|
|
|
menu: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`\n📊 初始化结果:`);
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('租户信息:');
|
|
|
|
|
|
console.log(` 租户名称: ${tenant.name}`);
|
|
|
|
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
|
|
|
|
console.log(` 访问链接: http://your-domain.com/?tenant=${tenant.code}`);
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('管理员登录信息:');
|
|
|
|
|
|
console.log(` 用户名: ${adminUser.username}`);
|
|
|
|
|
|
console.log(` 密码: ${password}`);
|
|
|
|
|
|
console.log(` 昵称: ${adminUser.nickname}`);
|
|
|
|
|
|
console.log(` 邮箱: ${adminUser.email}`);
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('角色和权限:');
|
|
|
|
|
|
console.log(` 角色: ${roleCodes.join(', ')}`);
|
|
|
|
|
|
console.log(` 权限数量: ${permissionCodes.size}`);
|
|
|
|
|
|
if (permissionCodes.size > 0 && permissionCodes.size <= 20) {
|
|
|
|
|
|
console.log(` 权限列表:`);
|
|
|
|
|
|
Array.from(permissionCodes)
|
|
|
|
|
|
.sort()
|
|
|
|
|
|
.forEach((code) => {
|
|
|
|
|
|
console.log(` - ${code}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
} else if (permissionCodes.size > 20) {
|
|
|
|
|
|
console.log(` 权限列表(前20个):`);
|
|
|
|
|
|
Array.from(permissionCodes)
|
|
|
|
|
|
.sort()
|
|
|
|
|
|
.slice(0, 20)
|
|
|
|
|
|
.forEach((code) => {
|
|
|
|
|
|
console.log(` - ${code}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(` ... 还有 ${permissionCodes.size - 20} 个权限`);
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('菜单分配:');
|
|
|
|
|
|
console.log(` 已分配菜单数: ${finalMenus.length}`);
|
|
|
|
|
|
if (finalMenus.length > 0) {
|
|
|
|
|
|
const topLevelMenus = finalMenus.filter((tm) => !tm.menu.parentId);
|
|
|
|
|
|
console.log(` 顶级菜单数: ${topLevelMenus.length}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log(`\n✅ 租户菜单和权限初始化完成!`);
|
|
|
|
|
|
console.log(`\n💡 现在可以使用以下凭据登录:`);
|
|
|
|
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
|
|
|
|
console.log(` 用户名: ${adminUser.username}`);
|
|
|
|
|
|
console.log(` 密码: ${password}`);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('❌ 初始化失败:', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
await prisma.$disconnect();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取命令行参数
|
|
|
|
|
|
const tenantCode = process.argv[2];
|
|
|
|
|
|
|
|
|
|
|
|
if (!tenantCode) {
|
|
|
|
|
|
console.error('❌ 错误: 请提供租户编码作为参数');
|
|
|
|
|
|
console.error(' 使用方法:');
|
|
|
|
|
|
console.error(' pnpm init:tenant-menu-permissions <租户编码>');
|
|
|
|
|
|
console.error(' 示例:');
|
|
|
|
|
|
console.error(' pnpm init:tenant-menu-permissions tenant1');
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行初始化
|
|
|
|
|
|
initTenantMenuAndPermissions(tenantCode)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
console.log('\n🎉 初始化脚本执行完成!');
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
|
console.error('\n💥 初始化脚本执行失败:', error);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|