library-picturebook-activity/backend/scripts/init-admin-permissions.ts
aid 418aa57ea8 Day4: 超管端设计优化 + UGC绘本创作社区P0实现
一、超管端设计优化
- 文档管理SOP体系建立,docs目录重组
- 统一用户管理:跨租户全局视角,合并用户管理+公众用户
- 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作)
- 菜单精简:移除评委管理/评审规则/通知管理
- Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一

二、UGC绘本创作社区P0
- 数据库:10张新表(user_works/user_work_pages/work_tags等)
- 子女账号独立化:Child升级为独立User,家长切换+独立登录
- 用户作品库:CRUD+发布审核,8个API
- AI创作流程:提交→生成→保存到作品库,4个API
- 作品广场:首页改造为推荐流,标签+搜索+排序
- 内容审核(超管端):作品审核+作品管理+标签管理
- 活动联动:WorkSelector作品选择器
- 布局改造:底部5Tab(发现/创作/活动/作品库/我的)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:20:25 +08:00

599 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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();
// 定义所有基础权限
const permissions = [
// 用户管理权限
{
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:create',
resource: 'permission',
action: 'create',
name: '创建权限',
description: '允许创建新权限',
},
{
code: 'permission:read',
resource: 'permission',
action: 'read',
name: '查看权限',
description: '允许查看权限列表和详情',
},
{
code: 'permission:update',
resource: 'permission',
action: 'update',
name: '更新权限',
description: '允许更新权限信息',
},
{
code: 'permission:delete',
resource: 'permission',
action: 'delete',
name: '删除权限',
description: '允许删除权限',
},
// 菜单管理权限
{
code: 'menu:create',
resource: 'menu',
action: 'create',
name: '创建菜单',
description: '允许创建新菜单',
},
{
code: 'menu:read',
resource: 'menu',
action: 'read',
name: '查看菜单',
description: '允许查看菜单列表和详情',
},
{
code: 'menu:update',
resource: 'menu',
action: 'update',
name: '更新菜单',
description: '允许更新菜单信息',
},
{
code: 'menu:delete',
resource: 'menu',
action: 'delete',
name: '删除菜单',
description: '允许删除菜单',
},
// 数据字典权限
{
code: 'dict:create',
resource: 'dict',
action: 'create',
name: '创建字典',
description: '允许创建新字典',
},
{
code: 'dict:read',
resource: 'dict',
action: 'read',
name: '查看字典',
description: '允许查看字典列表和详情',
},
{
code: 'dict:update',
resource: 'dict',
action: 'update',
name: '更新字典',
description: '允许更新字典信息',
},
{
code: 'dict:delete',
resource: 'dict',
action: 'delete',
name: '删除字典',
description: '允许删除字典',
},
// 系统配置权限
{
code: 'config:create',
resource: 'config',
action: 'create',
name: '创建配置',
description: '允许创建新配置',
},
{
code: 'config:read',
resource: 'config',
action: 'read',
name: '查看配置',
description: '允许查看配置列表和详情',
},
{
code: 'config:update',
resource: 'config',
action: 'update',
name: '更新配置',
description: '允许更新配置信息',
},
{
code: 'config:delete',
resource: 'config',
action: 'delete',
name: '删除配置',
description: '允许删除配置',
},
// 日志管理权限
{
code: 'log:read',
resource: 'log',
action: 'read',
name: '查看日志',
description: '允许查看系统日志',
},
{
code: 'log:delete',
resource: 'log',
action: 'delete',
name: '删除日志',
description: '允许删除系统日志',
},
// 活动管理权限
{
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: 'notice:create',
resource: 'notice',
action: 'create',
name: '创建公告',
description: '允许创建活动公告',
},
{
code: 'notice:read',
resource: 'notice',
action: 'read',
name: '查看公告',
description: '允许查看活动公告',
},
{
code: 'notice:update',
resource: 'notice',
action: 'update',
name: '更新公告',
description: '允许更新活动公告',
},
{
code: 'notice:delete',
resource: 'notice',
action: 'delete',
name: '删除公告',
description: '允许删除活动公告',
},
// 报名管理权限
{
code: 'registration:read',
resource: 'registration',
action: 'read',
name: '查看报名',
description: '允许查看报名列表',
},
{
code: 'registration:audit',
resource: 'registration',
action: 'audit',
name: '审核报名',
description: '允许审核报名申请',
},
// 作品管理权限
{
code: 'work:read',
resource: 'work',
action: 'read',
name: '查看作品',
description: '允许查看参赛作品',
},
{
code: 'work:update',
resource: 'work',
action: 'update',
name: '更新作品',
description: '允许更新作品状态',
},
// 评审管理权限
{
code: 'review:read',
resource: 'review',
action: 'read',
name: '查看评审',
description: '允许查看评审信息',
},
{
code: 'review:assign',
resource: 'review',
action: 'assign',
name: '分配评审',
description: '允许分配作品给评委',
},
{
code: 'review:score',
resource: 'review',
action: 'score',
name: '评分',
description: '允许对作品进行评分',
},
// 评委管理权限
{
code: 'judge:create',
resource: 'judge',
action: 'create',
name: '添加评委',
description: '允许添加活动评委',
},
{
code: 'judge:read',
resource: 'judge',
action: 'read',
name: '查看评委',
description: '允许查看评委列表',
},
{
code: 'judge:delete',
resource: 'judge',
action: 'delete',
name: '移除评委',
description: '允许移除活动评委',
},
// 成果管理权限
{
code: 'result:read',
resource: 'result',
action: 'read',
name: '查看成果',
description: '允许查看成果信息',
},
{
code: 'result:publish',
resource: 'result',
action: 'publish',
name: '发布成果',
description: '允许发布活动结果',
},
{
code: 'result:award',
resource: 'result',
action: 'award',
name: '设置奖项',
description: '允许设置作品奖项',
},
// 用户密码管理权限
{
code: 'user:password:update',
resource: 'user',
action: 'password:update',
name: '修改用户密码',
description: '允许修改用户密码',
},
];
async function initAdminPermissions() {
try {
console.log('🚀 开始为超级管理员admin用户初始化权限...\n');
// 1. 检查 admin 用户是否存在(先获取超级租户)
console.log('👤 步骤 1: 检查 admin 用户...');
const superTenant = await prisma.tenant.findUnique({
where: { code: 'super' },
});
if (!superTenant) {
console.error('❌ 错误: 超级租户不存在!');
console.error(' 请先运行 pnpm init:super-tenant 创建超级租户');
process.exit(1);
}
const adminUser = await prisma.user.findUnique({
where: { tenantId_username: { tenantId: superTenant.id, username: 'admin' } },
});
if (!adminUser) {
console.error('❌ 错误: admin 用户不存在!');
console.error(' 请先运行 pnpm init:admin 创建 admin 用户');
process.exit(1);
}
console.log(
`✅ admin 用户存在: ${adminUser.username} (${adminUser.nickname})\n`,
);
// 2. 创建或更新所有权限
console.log('📝 步骤 2: 确保所有权限存在...');
const createdPermissions = [];
for (const perm of permissions) {
const permission = await prisma.permission.upsert({
where: { tenantId_code: { tenantId: superTenant.id, code: perm.code } },
update: {
name: perm.name,
resource: perm.resource,
action: perm.action,
description: perm.description,
},
create: { ...perm, tenantId: superTenant.id },
});
createdPermissions.push(permission);
}
console.log(`✅ 共确保 ${createdPermissions.length} 个权限存在\n`);
// 3. 创建或获取超级管理员角色
console.log('👤 步骤 3: 确保超级管理员角色存在...');
const adminRole = await prisma.role.upsert({
where: { tenantId_code: { tenantId: superTenant.id, code: 'super_admin' } },
update: {
name: '超级管理员',
description: '拥有系统所有权限的超级管理员角色',
validState: 1,
},
create: {
tenantId: superTenant.id,
name: '超级管理员',
code: 'super_admin',
description: '拥有系统所有权限的超级管理员角色',
validState: 1,
},
});
console.log(
`✅ 超级管理员角色已确保存在: ${adminRole.name} (${adminRole.code})\n`,
);
// 4. 确保超级管理员角色拥有所有权限
console.log('🔗 步骤 4: 为超级管理员角色分配所有权限...');
const existingRolePermissions = await prisma.rolePermission.findMany({
where: { roleId: adminRole.id },
select: { permissionId: true },
});
const existingPermissionIds = new Set(
existingRolePermissions.map((rp) => rp.permissionId),
);
let addedCount = 0;
for (const permission of createdPermissions) {
if (!existingPermissionIds.has(permission.id)) {
await prisma.rolePermission.create({
data: {
roleId: adminRole.id,
permissionId: permission.id,
},
});
addedCount++;
}
}
if (addedCount > 0) {
console.log(`✅ 为超级管理员角色添加了 ${addedCount} 个权限\n`);
} else {
console.log(
`✅ 超级管理员角色已拥有所有权限(${createdPermissions.length} 个)\n`,
);
}
// 5. 确保 admin 用户拥有超级管理员角色
console.log('🔗 步骤 5: 确保 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(`✅ 已为 admin 用户分配超级管理员角色\n`);
} else {
console.log(`✅ admin 用户已拥有超级管理员角色\n`);
}
// 6. 验证结果
console.log('🔍 步骤 6: 验证结果...');
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);
});
});
console.log(`\n📊 初始化结果:`);
console.log(` 用户名: ${adminUser.username}`);
console.log(` 昵称: ${adminUser.nickname}`);
console.log(` 角色: ${roleCodes.join(', ')}`);
console.log(` 权限数量: ${permissionCodes.size}`);
console.log(` 权限列表:`);
Array.from(permissionCodes)
.sort()
.forEach((code) => {
console.log(` - ${code}`);
});
console.log(`\n✅ 超级管理员权限初始化完成!`);
} catch (error) {
console.error('❌ 初始化失败:', error);
throw error;
} finally {
await prisma.$disconnect();
}
}
// 执行初始化
initAdminPermissions()
.then(() => {
console.log('\n🎉 权限初始化脚本执行完成!');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 权限初始化脚本执行失败:', error);
process.exit(1);
});