/** * 广东省立中山图书馆 - 租户初始化脚本 * * 运行方式: * npx ts-node -r tsconfig-paths/register scripts/init-guangdong-library.ts * * 功能: * 1. 创建广东省图租户(library 类型) * 2. 创建管理员账号 * 3. 分配必要的角色和权限 * 4. 分配菜单 */ import { PrismaClient } from '@prisma/client'; import * as bcrypt from 'bcrypt'; const prisma = new PrismaClient(); async function main() { console.log('开始初始化广东省图数据...\n'); // 1. 创建租户 const tenantCode = 'gdlib'; let tenant = await prisma.tenant.findUnique({ where: { code: tenantCode } }); if (tenant) { console.log(`租户 ${tenantCode} 已存在 (ID: ${tenant.id}),跳过创建`); } else { tenant = await prisma.tenant.create({ data: { name: '广东省立中山图书馆', code: tenantCode, tenantType: 'library', description: '广东省图少儿绘本创作活动主办方', isSuper: 0, validState: 1, }, }); console.log(`创建租户: ${tenant.name} (ID: ${tenant.id})`); } const tenantId = tenant.id; // 2. 创建权限 const permissions = [ // 活动管理 { code: 'contest:create', resource: 'contest', action: 'create', name: '创建活动', description: '允许创建活动' }, { code: 'contest:read', resource: 'contest', action: 'read', name: '查看活动', description: '允许查看活动' }, { code: 'contest:update', resource: 'contest', action: 'update', name: '更新活动', description: '允许更新活动' }, { code: 'contest:delete', resource: 'contest', action: 'delete', name: '删除活动', description: '允许删除活动' }, { code: 'contest:publish', resource: 'contest', action: 'publish', name: '发布活动', description: '允许发布活动' }, { code: 'contest:finish', resource: 'contest', action: 'finish', name: '结束活动', description: '允许结束活动' }, // 报名管理 { code: 'registration:approve', resource: 'registration', action: 'approve', name: '审核报名', description: '允许审核报名' }, { code: 'registration:read', resource: 'registration', action: 'read', name: '查看报名', description: '允许查看报名记录' }, // 评委管理 { code: 'judge:read', resource: 'judge', action: 'read', name: '查看评委', description: '允许查看评委' }, { code: 'judge:create', resource: 'judge', action: 'create', name: '添加评委', description: '允许添加评委' }, { code: 'judge:assign', resource: 'judge', action: 'assign', name: '分配评委', description: '允许分配评委' }, // 评审规则 { code: 'review-rule:read', resource: 'review-rule', action: 'read', name: '查看评审规则', description: '允许查看评审规则' }, { code: 'review-rule:create', resource: 'review-rule', action: 'create', name: '创建评审规则', description: '允许创建评审规则' }, // 成果发布 { code: 'result:read', resource: 'result', action: 'read', name: '查看成果', description: '允许查看活动成果' }, { code: 'result:publish', resource: 'result', action: 'publish', name: '发布成果', description: '允许发布活动成果' }, // 公告 { code: 'notice:create', resource: 'notice', action: 'create', name: '创建公告', description: '允许创建活动公告' }, { code: 'notice:read', resource: 'notice', action: 'read', name: '查看公告', description: '允许查看活动公告' }, // 用户管理 { code: 'user:read', resource: 'user', action: 'read', name: '查看用户', description: '允许查看用户' }, { code: 'user:create', resource: 'user', action: 'create', name: '创建用户', description: '允许创建用户' }, // 角色管理 { code: 'role:read', resource: 'role', action: 'read', name: '查看角色', description: '允许查看角色' }, // 菜单 { code: 'menu:read', resource: 'menu', action: 'read', name: '查看菜单', description: '允许查看菜单' }, ]; const permissionIds: number[] = []; for (const perm of permissions) { const existing = await prisma.permission.findFirst({ where: { tenantId, code: perm.code }, }); if (existing) { permissionIds.push(existing.id); } else { const created = await prisma.permission.create({ data: { ...perm, tenantId, validState: 1 }, }); permissionIds.push(created.id); } } console.log(`权限已创建/确认: ${permissionIds.length} 个`); // 3. 创建管理员角色 let adminRole = await prisma.role.findFirst({ where: { tenantId, code: 'tenant_admin' }, }); if (!adminRole) { adminRole = await prisma.role.create({ data: { tenantId, name: '机构管理员', code: 'tenant_admin', description: '广东省图机构管理员,管理活动和报名', validState: 1, }, }); console.log(`创建角色: ${adminRole.name}`); } // 分配所有权限给管理员角色 for (const permId of permissionIds) { const existing = await prisma.rolePermission.findFirst({ where: { roleId: adminRole.id, permissionId: permId }, }); if (!existing) { await prisma.rolePermission.create({ data: { roleId: adminRole.id, permissionId: permId }, }); } } console.log(`角色权限已分配`); // 4. 创建管理员账号 const adminUsername = 'admin'; let adminUser = await prisma.user.findFirst({ where: { tenantId, username: adminUsername }, }); if (!adminUser) { const hashedPassword = await bcrypt.hash('admin@gdlib', 10); adminUser = await prisma.user.create({ data: { tenantId, username: adminUsername, password: hashedPassword, nickname: '广东省图管理员', userSource: 'admin_created', status: 'enabled', validState: 1, roles: { create: [{ roleId: adminRole.id }], }, }, }); console.log(`创建管理员: ${adminUsername} / admin@gdlib`); } else { console.log(`管理员 ${adminUsername} 已存在`); } // 5. 分配菜单(活动管理 + 系统管理) const menuNames = ['活动管理', '系统管理']; const menus = await prisma.menu.findMany({ where: { name: { in: menuNames }, parentId: null }, include: { children: true }, }); for (const menu of menus) { // 分配父菜单 const existing = await prisma.tenantMenu.findFirst({ where: { tenantId, menuId: menu.id }, }); if (!existing) { await prisma.tenantMenu.create({ data: { tenantId, menuId: menu.id }, }); } // 分配子菜单 for (const child of menu.children || []) { const childExisting = await prisma.tenantMenu.findFirst({ where: { tenantId, menuId: child.id }, }); if (!childExisting) { await prisma.tenantMenu.create({ data: { tenantId, menuId: child.id }, }); } } } console.log(`菜单已分配: ${menuNames.join(', ')}`); console.log('\n广东省图初始化完成!'); console.log(`登录地址: /${tenantCode}/login`); console.log(`账号: admin / admin@gdlib`); } main() .catch((e) => { console.error('初始化失败:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });