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';
|
|
|
|
|
|
|
|
|
|
|
|
// 根据 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';
|
|
|
|
|
|
|
|
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
|
|
console.log('🚀 开始创建 LinkSea 普通租户...\n');
|
|
|
|
|
|
|
|
|
|
|
|
const tenantCode = 'linksea';
|
|
|
|
|
|
const menuNames = ['活动管理', '系统管理'];
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 查找或创建租户
|
|
|
|
|
|
console.log(`📋 步骤 1: 查找或创建租户 "${tenantCode}"...`);
|
|
|
|
|
|
let tenant = await prisma.tenant.findUnique({
|
|
|
|
|
|
where: { code: tenantCode },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!tenant) {
|
|
|
|
|
|
// 创建普通租户
|
|
|
|
|
|
tenant = await prisma.tenant.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
name: 'LinkSea 租户',
|
|
|
|
|
|
code: tenantCode,
|
|
|
|
|
|
domain: tenantCode,
|
|
|
|
|
|
description: 'LinkSea 普通租户',
|
|
|
|
|
|
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. 查找指定的菜单(顶级菜单)
|
|
|
|
|
|
console.log(`📋 步骤 2: 查找菜单 "${menuNames.join('", "')}"...`);
|
|
|
|
|
|
const menus = await prisma.menu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
name: { in: menuNames },
|
|
|
|
|
|
parentId: null, // 只查找顶级菜单
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (menus.length === 0) {
|
|
|
|
|
|
console.error(`❌ 错误: 未找到指定的菜单!`);
|
|
|
|
|
|
console.error(` 请确保菜单 "${menuNames.join('", "')}" 已初始化`);
|
|
|
|
|
|
console.error(` 运行: pnpm init:menus`);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (menus.length !== menuNames.length) {
|
|
|
|
|
|
const foundMenuNames = menus.map((m) => m.name);
|
|
|
|
|
|
const missingMenus = menuNames.filter(
|
|
|
|
|
|
(name) => !foundMenuNames.includes(name),
|
|
|
|
|
|
);
|
|
|
|
|
|
console.warn(`⚠️ 警告: 部分菜单未找到: ${missingMenus.join(', ')}`);
|
|
|
|
|
|
console.log(` 找到的菜单: ${foundMenuNames.join(', ')}\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`✅ 找到 ${menus.length} 个菜单:`);
|
|
|
|
|
|
menus.forEach((menu) => {
|
|
|
|
|
|
console.log(` ✓ ${menu.name}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 递归获取菜单及其所有子菜单
|
|
|
|
|
|
console.log(`📋 步骤 3: 获取菜单及其所有子菜单...`);
|
|
|
|
|
|
const menuIds = new Set<number>();
|
|
|
|
|
|
|
|
|
|
|
|
// 递归函数:获取菜单及其所有子菜单的ID
|
|
|
|
|
|
async function getMenuAndChildrenIds(menuId: number) {
|
|
|
|
|
|
menuIds.add(menuId);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有子菜单
|
|
|
|
|
|
const children = await prisma.menu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
parentId: menuId,
|
|
|
|
|
|
validState: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 递归获取子菜单的子菜单
|
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
|
await getMenuAndChildrenIds(child.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 为每个顶级菜单获取所有子菜单
|
|
|
|
|
|
for (const menu of menus) {
|
|
|
|
|
|
await getMenuAndChildrenIds(menu.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const menuIdArray = Array.from(menuIds);
|
|
|
|
|
|
console.log(`✅ 共找到 ${menuIdArray.length} 个菜单(包括子菜单)\n`);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 获取租户已分配的菜单
|
|
|
|
|
|
console.log(`📋 步骤 4: 检查租户已分配的菜单...`);
|
|
|
|
|
|
const existingTenantMenus = await prisma.tenantMenu.findMany({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
select: {
|
|
|
|
|
|
menuId: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const existingMenuIds = new Set(existingTenantMenus.map((tm) => tm.menuId));
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 为租户分配菜单(只分配新的菜单)
|
|
|
|
|
|
console.log(`📋 步骤 5: 为租户分配菜单...`);
|
|
|
|
|
|
const menusToAdd = menuIdArray.filter((id) => !existingMenuIds.has(id));
|
|
|
|
|
|
|
|
|
|
|
|
if (menusToAdd.length === 0) {
|
|
|
|
|
|
console.log(`✅ 租户已拥有所有指定的菜单\n`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let addedCount = 0;
|
|
|
|
|
|
const menuNamesToAdd: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const menuId of menusToAdd) {
|
|
|
|
|
|
const menu = await prisma.menu.findUnique({
|
|
|
|
|
|
where: { id: menuId },
|
|
|
|
|
|
select: { name: true },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await prisma.tenantMenu.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
tenantId: tenant.id,
|
|
|
|
|
|
menuId: menuId,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
addedCount++;
|
|
|
|
|
|
if (menu) {
|
|
|
|
|
|
menuNamesToAdd.push(menu.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`✅ 为租户添加了 ${addedCount} 个菜单:`);
|
|
|
|
|
|
menuNamesToAdd.forEach((name) => {
|
|
|
|
|
|
console.log(` ✓ ${name}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`\n✅ 租户现在拥有 ${menuIdArray.length} 个菜单(包括子菜单)\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 验证结果
|
|
|
|
|
|
console.log('📊 初始化结果:');
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('租户信息:');
|
|
|
|
|
|
console.log(` 租户编码: ${tenant.code}`);
|
|
|
|
|
|
console.log(` 租户名称: ${tenant.name}`);
|
|
|
|
|
|
console.log(` 租户类型: ${tenant.isSuper === 1 ? '超级租户' : '普通租户'}`);
|
|
|
|
|
|
console.log(` 访问链接: http://your-domain.com/?tenant=${tenant.code}`);
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('分配的菜单:');
|
|
|
|
|
|
console.log(` 顶级菜单: ${menuNames.join(', ')}`);
|
|
|
|
|
|
console.log(` 菜单总数: ${menuIdArray.length} 个(包括子菜单)`);
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
console.log('\n💡 提示:');
|
|
|
|
|
|
console.log(' 如需创建管理员账号,请运行: pnpm init:tenant-admin linksea');
|
|
|
|
|
|
console.log('========================================');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main()
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
console.log('\n🎉 LinkSea 租户创建脚本执行完成!');
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
|
console.error('\n💥 LinkSea 租户创建脚本执行失败:', error);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(async () => {
|
|
|
|
|
|
await prisma.$disconnect();
|
|
|
|
|
|
});
|