import { prisma } from './prisma'; import { filterSelectableMaterialTypes, MATERIAL_TYPE_OPTIONS } from './material-type-options'; import { getCompatibleVideoMaterialTypes, isVideoMaterialType } from './video-material-types'; /** * 判断是否是超级管理员(从数据库读取) */ export async function isSuperAdmin(userId: string | null | undefined): Promise { if (!userId) { return false; } const user = await prisma.user.findUnique({ where: { id: userId }, select: { isSuperAdmin: true }, }); return user?.isSuperAdmin ?? false; } /** * 同步版本的超级管理员判断(用于已经获取到用户信息的情况) */ export function isSuperAdminSync(isSuperAdmin: boolean): boolean { return isSuperAdmin; } /** * 角色权限检查辅助函数(阶段四新增) */ /** * 判断是否是系统管理员(包括ADMIN和SYSTEM_ADMIN,向后兼容) */ export function isSystemAdmin(role: string | null | undefined): boolean { if (!role) return false; // ADMIN 视为 SYSTEM_ADMIN(向后兼容) return role === 'ADMIN' || role === 'SYSTEM_ADMIN'; } /** * 判断是否是部门管理员 */ export function isDepartmentAdmin(role: string | null | undefined): boolean { if (!role) return false; return role === 'DEPT_ADMIN'; } /** * 判断是否是管理员(系统管理员或部门管理员) */ export function isAdmin(role: string | null | undefined): boolean { if (!role) return false; // 包括所有管理员角色:ADMIN(兼容)、SYSTEM_ADMIN、DEPT_ADMIN return isSystemAdmin(role) || isDepartmentAdmin(role); } /** * 判断是否有系统管理权限(系统管理员或超级管理员) */ export function hasSystemAdminPermission( role: string | null | undefined, isSuperAdminFlag: boolean = false ): boolean { return isSuperAdminFlag || isSystemAdmin(role); } /** * 判断是否有部门管理权限(部门管理员、系统管理员或超级管理员) */ export function hasDepartmentAdminPermission( role: string | null | undefined, isSuperAdminFlag: boolean = false ): boolean { return isSuperAdminFlag || isSystemAdmin(role) || isDepartmentAdmin(role); } /** * 获取当前超级管理员数量 */ export async function getSuperAdminCount(): Promise { return await prisma.user.count({ where: { isSuperAdmin: true }, }); } /** * 检查是否可以添加新的超级管理员(最多4个) */ export async function canAddSuperAdmin(): Promise { const count = await getSuperAdminCount(); return count < 4; } // 默认素材类型列表(作为后备,用于新部门创建默认权限配置时) // 如果数据库中已有素材,会优先使用数据库中的素材类型 // 已按分类排序 export const DEFAULT_MATERIAL_TYPES = MATERIAL_TYPE_OPTIONS; /** * 从数据库中获取所有素材类型(去重) */ export async function getAllMaterialTypes(): Promise { try { // 从materials表中查询所有不重复的materialType const materials = await prisma.material.findMany({ select: { materialType: true, }, distinct: ['materialType'], }); const dbTypes = materials.map((m) => m.materialType).filter(Boolean); // 合并数据库中的类型和默认类型,去重 const allTypesSet = new Set([...dbTypes, ...DEFAULT_MATERIAL_TYPES]); // 按照 DEFAULT_MATERIAL_TYPES 的顺序排序,未在列表中的类型放在最后 const sortedTypes = [ ...DEFAULT_MATERIAL_TYPES.filter(type => allTypesSet.has(type)), ...Array.from(allTypesSet).filter(type => !DEFAULT_MATERIAL_TYPES.includes(type as any)).sort() ]; return sortedTypes; } catch (error) { console.error('[getAllMaterialTypes] Error:', error); // 如果查询失败,返回默认类型列表 return [...DEFAULT_MATERIAL_TYPES]; } } export interface DepartmentPermission { department: string; materialType: string; canView: boolean; canDownload: boolean; canShare: boolean; canDownloadSource: boolean; linkedToDepartment: string | null; } async function findDepartmentPermissionRecord( department: string, materialType: string ) { const normalizedType = String(materialType || '').trim(); const lookupTypes = isVideoMaterialType(normalizedType) ? getCompatibleVideoMaterialTypes(normalizedType) : [normalizedType]; for (const lookupType of lookupTypes) { const permission = await prisma.departmentMaterialPermission.findUnique({ where: { department_materialType: { department, materialType: lookupType, }, }, }); if (permission) { return permission; } } return null; } /** * 获取部门的实际权限(考虑关联部门和权限继承) * * @param department 部门名称 * @param materialType 素材类型 * @param departments 部门列表(用于权限继承检查,可选) * @param descendantMap 部门后裔映射(可选,用于加速权限继承检查) * @returns 部门权限配置 */ export async function getDepartmentPermission( department: string, materialType: string, departments?: Array<{ id: string; name: string; parentId?: string | null }>, descendantMap?: Map> ): Promise { // 先查询当前部门的权限配置 const permission = await findDepartmentPermissionRecord(department, materialType); // 如果有关联部门,从关联部门读取权限 if (permission?.linkedToDepartment) { const linkedPermission = await findDepartmentPermissionRecord( permission.linkedToDepartment, materialType ); if (linkedPermission) { return { department, materialType, canView: linkedPermission.canView, canDownload: linkedPermission.canDownload, canShare: linkedPermission.canShare, canDownloadSource: linkedPermission.canDownloadSource, linkedToDepartment: permission.linkedToDepartment, }; } } // 如果当前部门有明确的权限配置,直接返回 if (permission) { return { department: permission.department, materialType: permission.materialType, canView: permission.canView, canDownload: permission.canDownload, canShare: permission.canShare, canDownloadSource: permission.canDownloadSource, linkedToDepartment: permission.linkedToDepartment, }; } // 权限继承:如果没有找到当前部门的权限配置,尝试从父部门继承 if (departments && departments.length > 0) { const { buildDepartmentDescendants, isDepartmentWithinScope } = await import('@/lib/departments'); // 构建部门映射 const deptMap = new Map(); departments.forEach(dept => { deptMap.set(dept.name, dept); }); // 获取当前部门的父部门链 const currentDept = deptMap.get(department); if (currentDept?.parentId) { // 查找父部门 const parentDept = departments.find(d => d.id === currentDept.parentId); if (parentDept) { console.log(`[Permission Inheritance] 部门 ${department} 无权限配置,尝试从父部门 ${parentDept.name} 继承 ${materialType} 权限`); // 递归获取父部门的权限 const parentPermission = await getDepartmentPermission( parentDept.name, materialType, departments, descendantMap ); // 如果父部门有权限配置,继承父部门的权限 if (parentPermission) { console.log(`[Permission Inheritance] 成功从父部门 ${parentDept.name} 继承权限: canDownload=${parentPermission.canDownload}`); return { department, materialType, canView: parentPermission.canView, canDownload: parentPermission.canDownload, canShare: parentPermission.canShare, canDownloadSource: parentPermission.canDownloadSource, linkedToDepartment: parentPermission.linkedToDepartment || parentDept.name, }; } else { console.log(`[Permission Inheritance] 父部门 ${parentDept.name} 也没有 ${materialType} 权限配置`); } } else { console.log(`[Permission Inheritance] 找不到父部门 (parentId=${currentDept.parentId})`); } } else { console.log(`[Permission Inheritance] 部门 ${department} 没有父部门`); } } // 如果都没有找到,返回 null(表示没有权限配置) console.log(`[Permission Inheritance] 部门 ${department} 对于 ${materialType} 没有找到任何权限配置`); return null; } /** * 获取部门所有素材类型的权限(支持权限继承) * * @param department 部门名称 * @param departments 部门列表(用于权限继承检查,可选) * @param materialTypes 素材类型列表(可选,如果不提供则从数据库获取) * @returns 部门所有素材类型的权限配置 */ export async function getDepartmentPermissions( department: string, departments?: Array<{ id: string; name: string; parentId?: string | null }>, materialTypes?: string[] ): Promise> { // 如果没有提供素材类型列表,从数据库获取所有素材类型 const allMaterialTypes = materialTypes || await getAllMaterialTypes(); // 获取部门后裔映射(用于加速权限继承检查) const { buildDepartmentDescendants } = await import('@/lib/departments'); const descendantMap = departments && departments.length > 0 ? buildDepartmentDescendants(departments) : undefined; const result: Record = {}; // 为每种素材类型获取权限(包括继承的权限) for (const materialType of allMaterialTypes) { const permission = await getDepartmentPermission( department, materialType, departments, descendantMap ); if (permission) { result[materialType] = permission; } } return result; } /** * 获取用户部门名称(双读:优先使用 departmentId,回退到 department) * 阶段二新增:支持外键关联 */ export async function getUserDepartmentName( user: { department?: string | null; departmentId?: string | null; departmentRef?: { name: string } | null } | null | undefined ): Promise { if (!user) { return null; } // 优先使用新字段(departmentId + departmentRef) if (user.departmentRef?.name) { return user.departmentRef.name; } // 回退到旧字段(department) return user.department || null; } /** * 获取用户有查看权限的素材类型列表(支持权限继承) */ export async function getUserViewableMaterialTypes( userDepartment: string | null | undefined, userRole: string, managedDepartment: string | null | undefined, userId?: string | null | undefined ): Promise { // 超级管理员可以看到所有素材类型 if (userId && await isSuperAdmin(userId)) { return await getAllMaterialTypes(); } // 如果没有部门信息,返回空数组 const department = managedDepartment || userDepartment; if (!department) { return []; } // 获取部门列表(用于权限继承检查) const departments = await prisma.department.findMany({ select: { id: true, name: true, parentId: true }, }); // 获取该部门所有素材类型的权限(包括继承的权限) const permissions = await getDepartmentPermissions(department, departments); // 获取有查看权限的素材类型 const viewableTypes = Object.values(permissions) .filter((p) => p.canView) .map((p) => p.materialType); // 按照 DEFAULT_MATERIAL_TYPES 的顺序排序 const sortedTypes = [ ...DEFAULT_MATERIAL_TYPES.filter(type => viewableTypes.includes(type as string)), ...viewableTypes.filter(type => !DEFAULT_MATERIAL_TYPES.includes(type as any)).sort() ]; return sortedTypes; } /** * 检查用户是否有查看权限(支持权限继承) */ export async function canUserViewMaterial( userDepartment: string | null | undefined, userRole: string, managedDepartment: string | null | undefined, materialType: string, userId?: string | null | undefined ): Promise { // 超级管理员可以查看所有素材 if (userId && await isSuperAdmin(userId)) { return true; } // 如果没有部门信息,不允许查看 const department = managedDepartment || userDepartment; if (!department) { return false; } // 获取部门列表(用于权限继承检查) const departments = await prisma.department.findMany({ select: { id: true, name: true, parentId: true }, }); const permission = await getDepartmentPermission(department, materialType, departments); return permission?.canView ?? false; } /** * 检查用户是否有下载权限(支持权限继承) */ export async function canUserDownloadMaterial( userDepartment: string | null | undefined, userRole: string, managedDepartment: string | null | undefined, materialType: string, userId?: string | null | undefined ): Promise { // 超级管理员可以下载所有素材 if (userId && await isSuperAdmin(userId)) { console.log(`[Permission] 超级管理员 ${userId} 允许下载 ${materialType}`); return true; } // 如果没有部门信息,不允许下载 const department = managedDepartment || userDepartment; if (!department) { console.log(`[Permission] 用户无部门信息,拒绝下载 ${materialType}`); return false; } // 获取部门列表(用于权限继承检查) const departments = await prisma.department.findMany({ select: { id: true, name: true, parentId: true }, }); const permission = await getDepartmentPermission(department, materialType, departments); // 增强日志:记录权限检查结果 console.log(`[Permission] 用户部门: ${department}, 素材类型: ${materialType}, 权限: ${permission?.canDownload ?? false}, 继承自: ${permission?.linkedToDepartment || '无'}`); return permission?.canDownload ?? false; } /** * 检查是否是分享链接访问 */ export function isShareLinkAccess(request: Request): boolean { const url = new URL(request.url); return url.searchParams.has('share') || url.searchParams.has('shareId'); } /** * 为部门创建默认权限配置 * 使用数据库中实际存在的所有素材类型 */ export async function createDefaultPermissionsForDepartment( department: string ): Promise { const existingPermissions = await prisma.departmentMaterialPermission.findMany({ where: { department }, }); const existingTypes = new Set(existingPermissions.map((p: any) => p.materialType)); // 获取数据库中所有素材类型 const allMaterialTypes = filterSelectableMaterialTypes(await getAllMaterialTypes()); const newPermissions = allMaterialTypes .filter((type) => !existingTypes.has(type)) .map((materialType) => ({ department, materialType, canView: false, // 默认不允许查看 canDownload: false, // 默认不允许下载 canShare: true, // 默认允许分享 linkedToDepartment: null, })); if (newPermissions.length > 0) { await prisma.departmentMaterialPermission.createMany({ data: newPermissions, }); } } /** * 检查用户是否有下载源文件(PSD)权限 * 用于任务-详情类型的 PSD 下载权限控制 */ export async function canUserDownloadSource( userDepartment: string | null | undefined, userRole: string, managedDepartment: string | null | undefined, materialType: string, userId?: string | null | undefined ): Promise { // 超级管理员可以下载所有源文件 if (userId && await isSuperAdmin(userId)) { return true; } // 如果没有部门信息,不允许下载 const department = managedDepartment || userDepartment; if (!department) { return false; } // 查询部门权限配置 const permission = await prisma.departmentMaterialPermission.findUnique({ where: { department_materialType: { department, materialType, }, }, select: { canDownloadSource: true, linkedToDepartment: true, }, }); // 如果有关联部门,从关联部门读取权限 if (permission?.linkedToDepartment) { const linkedPermission = await prisma.departmentMaterialPermission.findUnique({ where: { department_materialType: { department: permission.linkedToDepartment, materialType, }, }, select: { canDownloadSource: true, }, }); return linkedPermission?.canDownloadSource ?? false; } return permission?.canDownloadSource ?? false; }