2025-11-23 14:04:20 +08:00
|
|
|
// 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}`;
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
|
|
async function main() {
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('🚀 开始初始化超级租户...\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
|
|
|
|
|
// 检查是否已存在超级租户
|
2025-12-09 11:10:36 +08:00
|
|
|
let superTenant = await prisma.tenant.findFirst({
|
2025-11-23 14:04:20 +08:00
|
|
|
where: { isSuper: 1 },
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
if (superTenant) {
|
|
|
|
|
console.log('⚠️ 超级租户已存在,将更新菜单分配');
|
|
|
|
|
console.log(` 租户编码: ${superTenant.code}\n`);
|
|
|
|
|
} else {
|
|
|
|
|
// 创建超级租户
|
|
|
|
|
superTenant = await prisma.tenant.create({
|
|
|
|
|
data: {
|
|
|
|
|
name: '超级租户',
|
|
|
|
|
code: 'super',
|
|
|
|
|
domain: 'super',
|
|
|
|
|
description: '系统超级租户,拥有所有权限',
|
|
|
|
|
isSuper: 1,
|
|
|
|
|
validState: 1,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('✅ 超级租户创建成功!');
|
|
|
|
|
console.log(` 租户ID: ${superTenant.id}`);
|
|
|
|
|
console.log(` 租户编码: ${superTenant.code}`);
|
|
|
|
|
console.log(` 租户名称: ${superTenant.name}\n`);
|
|
|
|
|
}
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 创建或获取超级管理员用户
|
|
|
|
|
console.log('📋 步骤 2: 创建或获取超级管理员用户...\n');
|
|
|
|
|
let superAdmin = await prisma.user.findFirst({
|
|
|
|
|
where: {
|
2025-11-23 14:04:20 +08:00
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
username: 'admin',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
if (!superAdmin) {
|
|
|
|
|
const hashedPassword = await bcrypt.hash('admin@super', 10);
|
|
|
|
|
superAdmin = await prisma.user.create({
|
|
|
|
|
data: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
username: 'admin',
|
|
|
|
|
password: hashedPassword,
|
|
|
|
|
nickname: '超级管理员',
|
|
|
|
|
email: 'admin@super.com',
|
|
|
|
|
validState: 1,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
console.log('✅ 超级管理员用户创建成功!');
|
|
|
|
|
console.log(` 用户名: ${superAdmin.username}`);
|
|
|
|
|
console.log(` 密码: admin@super`);
|
|
|
|
|
console.log(` 用户ID: ${superAdmin.id}\n`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('✅ 超级管理员用户已存在');
|
|
|
|
|
console.log(` 用户名: ${superAdmin.username}`);
|
|
|
|
|
console.log(` 用户ID: ${superAdmin.id}\n`);
|
|
|
|
|
}
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 创建或获取超级管理员角色
|
|
|
|
|
console.log('📋 步骤 3: 创建或获取超级管理员角色...\n');
|
|
|
|
|
let superAdminRole = await prisma.role.findFirst({
|
|
|
|
|
where: {
|
2025-11-23 14:04:20 +08:00
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
code: 'super_admin',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
if (!superAdminRole) {
|
|
|
|
|
superAdminRole = await prisma.role.create({
|
|
|
|
|
data: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
name: '超级管理员',
|
|
|
|
|
code: 'super_admin',
|
|
|
|
|
description: '超级管理员角色,拥有所有权限',
|
|
|
|
|
validState: 1,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
console.log('✅ 超级管理员角色创建成功!');
|
|
|
|
|
console.log(` 角色编码: ${superAdminRole.code}\n`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('✅ 超级管理员角色已存在');
|
|
|
|
|
console.log(` 角色编码: ${superAdminRole.code}\n`);
|
|
|
|
|
}
|
2025-11-23 14:04:20 +08:00
|
|
|
|
|
|
|
|
// 将超级管理员角色分配给用户
|
2025-12-09 11:10:36 +08:00
|
|
|
const existingUserRole = await prisma.userRole.findUnique({
|
|
|
|
|
where: {
|
|
|
|
|
userId_roleId: {
|
|
|
|
|
userId: superAdmin.id,
|
|
|
|
|
roleId: superAdminRole.id,
|
|
|
|
|
},
|
2025-11-23 14:04:20 +08:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
if (!existingUserRole) {
|
|
|
|
|
await prisma.userRole.create({
|
|
|
|
|
data: {
|
|
|
|
|
userId: superAdmin.id,
|
|
|
|
|
roleId: superAdminRole.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
console.log('✅ 超级管理员角色已分配给用户');
|
|
|
|
|
} else {
|
|
|
|
|
console.log('✅ 超级管理员角色已分配给用户,跳过');
|
|
|
|
|
}
|
|
|
|
|
console.log('💡 提示: 权限初始化请使用 init:admin:permissions 脚本\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 为超级租户分配所有菜单
|
|
|
|
|
console.log('📋 步骤 4: 为超级租户分配所有菜单...\n');
|
|
|
|
|
|
|
|
|
|
// 获取所有有效菜单
|
|
|
|
|
const allMenus = await prisma.menu.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
validState: 1,
|
2025-11-23 14:04:20 +08:00
|
|
|
},
|
2025-12-09 11:10:36 +08:00
|
|
|
orderBy: [{ sort: 'asc' }, { id: 'asc' }],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (allMenus.length === 0) {
|
|
|
|
|
console.log('⚠️ 警告: 数据库中没有任何菜单');
|
|
|
|
|
console.log(' 请先运行 pnpm init:menus 初始化菜单');
|
|
|
|
|
} else {
|
|
|
|
|
console.log(` 找到 ${allMenus.length} 个菜单\n`);
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 获取超级租户已分配的菜单
|
|
|
|
|
const existingTenantMenus = await prisma.tenantMenu.findMany({
|
2025-11-23 14:04:20 +08:00
|
|
|
where: {
|
|
|
|
|
tenantId: superTenant.id,
|
2025-12-09 11:10:36 +08:00
|
|
|
},
|
|
|
|
|
select: {
|
|
|
|
|
menuId: true,
|
2025-11-23 14:04:20 +08:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
const existingMenuIds = new Set(existingTenantMenus.map((tm) => tm.menuId));
|
|
|
|
|
|
|
|
|
|
// 为超级租户分配所有菜单
|
|
|
|
|
let addedCount = 0;
|
|
|
|
|
const menuNames: string[] = [];
|
|
|
|
|
|
|
|
|
|
for (const menu of allMenus) {
|
|
|
|
|
if (!existingMenuIds.has(menu.id)) {
|
|
|
|
|
await prisma.tenantMenu.create({
|
|
|
|
|
data: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
menuId: menu.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
addedCount++;
|
|
|
|
|
menuNames.push(menu.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (addedCount > 0) {
|
|
|
|
|
console.log(`✅ 为超级租户添加了 ${addedCount} 个菜单:`);
|
|
|
|
|
menuNames.forEach((name) => {
|
|
|
|
|
console.log(` ✓ ${name}`);
|
2025-11-23 14:04:20 +08:00
|
|
|
});
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log(`\n✅ 超级租户现在拥有 ${allMenus.length} 个菜单\n`);
|
2025-11-23 14:04:20 +08:00
|
|
|
} else {
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log(`✅ 超级租户已拥有所有菜单(${allMenus.length} 个)\n`);
|
2025-11-23 14:04:20 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建租户管理菜单(如果不存在)
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('📋 步骤 5: 创建租户管理菜单(如果不存在)...\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
|
|
|
|
|
// 查找系统管理菜单(父菜单)
|
|
|
|
|
const systemMenu = await prisma.menu.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
name: '系统管理',
|
|
|
|
|
parentId: null,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (systemMenu) {
|
|
|
|
|
// 检查租户管理菜单是否已存在
|
|
|
|
|
const existingTenantMenu = await prisma.menu.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
name: '租户管理',
|
|
|
|
|
path: '/system/tenants',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let tenantMenu;
|
|
|
|
|
if (!existingTenantMenu) {
|
|
|
|
|
tenantMenu = await prisma.menu.create({
|
|
|
|
|
data: {
|
|
|
|
|
name: '租户管理',
|
|
|
|
|
path: '/system/tenants',
|
|
|
|
|
icon: 'TeamOutlined',
|
|
|
|
|
component: 'system/tenants/Index',
|
|
|
|
|
parentId: systemMenu.id,
|
|
|
|
|
permission: 'tenant:read',
|
|
|
|
|
sort: 7,
|
|
|
|
|
validState: 1,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('✅ 租户管理菜单创建成功');
|
|
|
|
|
|
|
|
|
|
// 为超级租户分配租户管理菜单
|
|
|
|
|
await prisma.tenantMenu.create({
|
|
|
|
|
data: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
menuId: tenantMenu.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
console.log('✅ 租户管理菜单已分配给超级租户\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
} else {
|
|
|
|
|
tenantMenu = existingTenantMenu;
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('✅ 租户管理菜单已存在');
|
2025-11-23 14:04:20 +08:00
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 检查是否已分配
|
2025-11-23 14:04:20 +08:00
|
|
|
const existingTenantMenuRelation = await prisma.tenantMenu.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
menuId: tenantMenu.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!existingTenantMenuRelation) {
|
|
|
|
|
await prisma.tenantMenu.create({
|
|
|
|
|
data: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
menuId: tenantMenu.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('✅ 租户管理菜单已分配给超级租户\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
} else {
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('✅ 租户管理菜单已分配给超级租户,跳过\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log('⚠️ 警告:未找到系统管理菜单,无法创建租户管理菜单\n');
|
2025-11-23 14:04:20 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-09 11:10:36 +08:00
|
|
|
// 验证菜单分配结果
|
|
|
|
|
const finalMenus = await prisma.tenantMenu.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
tenantId: superTenant.id,
|
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
menu: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('📊 初始化结果:');
|
2025-11-23 14:04:20 +08:00
|
|
|
console.log('========================================');
|
|
|
|
|
console.log('超级租户信息:');
|
|
|
|
|
console.log(` 租户编码: ${superTenant.code}`);
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log(` 租户名称: ${superTenant.name}`);
|
2025-11-23 14:04:20 +08:00
|
|
|
console.log(` 访问链接: http://your-domain.com/?tenant=${superTenant.code}`);
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
console.log('超级管理员登录信息:');
|
|
|
|
|
console.log(` 用户名: ${superAdmin.username}`);
|
2025-12-09 11:10:36 +08:00
|
|
|
console.log(` 密码: admin@super`);
|
2025-11-23 14:04:20 +08:00
|
|
|
console.log(` 租户编码: ${superTenant.code}`);
|
|
|
|
|
console.log('========================================');
|
2025-12-09 11:10:36 +08:00
|
|
|
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(' 权限初始化请使用: pnpm init:admin:permissions');
|
|
|
|
|
console.log(' 菜单初始化请使用: pnpm init:menus');
|
|
|
|
|
console.log('========================================');
|
2025-11-23 14:04:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main()
|
|
|
|
|
.then(() => {
|
|
|
|
|
console.log('\n🎉 初始化脚本执行完成!');
|
|
|
|
|
process.exit(0);
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
console.error('\n💥 初始化脚本执行失败:', error);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
})
|
|
|
|
|
.finally(async () => {
|
|
|
|
|
await prisma.$disconnect();
|
|
|
|
|
});
|