gongxiangp2/lib/user-permission-inherit.ts

193 lines
6.1 KiB
TypeScript

import { Prisma } from '@prisma/client'
import { prisma } from './prisma'
import {
normalizeTaskDetailOnlyFlags,
unionPermissionPacks,
type MaterialPermissionFlags,
type PermissionPackMap,
} from './organization-material-permissions'
function isMissingInheritTable(error: unknown) {
return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2021'
}
export const DEFAULT_INHERIT_PACKS = [
{ slug: 'operations', name: '运营', sourceDepartment: '运营中心' },
{ slug: 'visual', name: '视觉', sourceDepartment: '视觉中心' },
] as const
export function packItemsToMap(
items: Array<{
materialType: string
canView: boolean
canDownload: boolean
canDownloadSource: boolean
canShare: boolean
canEditAssets?: boolean
canAccessPrivate?: boolean
}>,
): PermissionPackMap {
const pack: PermissionPackMap = {}
for (const item of items) {
const taskDetailOnlyFlags = normalizeTaskDetailOnlyFlags(item.materialType, item)
pack[item.materialType] = {
canView: item.canView,
canDownload: item.canDownload,
canDownloadSource: item.canDownloadSource,
canShare: item.canShare,
canEditAssets: taskDetailOnlyFlags.canEditAssets,
canAccessPrivate: taskDetailOnlyFlags.canAccessPrivate,
}
}
return pack
}
export async function ensureDefaultPermissionPacks() {
for (const preset of DEFAULT_INHERIT_PACKS) {
const pack = await prisma.materialPermissionPack.upsert({
where: { slug: preset.slug },
update: {},
create: { slug: preset.slug, name: preset.name },
select: { id: true, _count: { select: { items: true } } },
})
if (pack._count.items > 0) continue
const sourceRows = await prisma.departmentMaterialPermission.findMany({
where: { department: preset.sourceDepartment },
select: {
materialType: true,
canView: true,
canDownload: true,
canDownloadSource: true,
canShare: true,
},
})
if (sourceRows.length === 0) continue
await prisma.materialPermissionPackItem.createMany({
data: sourceRows.map((row) => ({
packId: pack.id,
materialType: row.materialType,
canView: row.canView,
canDownload: row.canDownload,
canDownloadSource: row.canDownloadSource,
canShare: row.canShare,
canEditAssets: false,
canAccessPrivate: false,
})),
})
}
}
const PACK_ITEM_SELECT = {
materialType: true,
canView: true,
canDownload: true,
canDownloadSource: true,
canShare: true,
canEditAssets: true,
canAccessPrivate: true,
} as const
export async function loadUserInheritPack(userId: string): Promise<PermissionPackMap | null> {
try {
const assignments = await prisma.userPermissionInherit.findMany({
where: { userId },
select: {
pack: {
select: {
items: { select: PACK_ITEM_SELECT },
},
},
},
})
if (assignments.length === 0) return null
return unionPermissionPacks(...assignments.map((row) => packItemsToMap(row.pack.items)))
} catch (error) {
if (isMissingInheritTable(error)) return null
throw error
}
}
/**
* 任务-详情单图编辑授权线:用户的花名册路径权限组(部门组+个人组并集)
* 对「任务-详情」勾选了编辑图片即获得。与上传者/管理员/托管部门的既有
* 授权线叠加,任何一边允许即可编辑。
*/
export async function canUserEditTaskDetailAssets(userId: string): Promise<boolean> {
if (!userId) return false
try {
const { resolveRosterPermissionPack } = await import('./department-permission')
const pack = await resolveRosterPermissionPack(userId)
return pack?.['任务-详情']?.canEditAssets === true
} catch (error) {
console.error('[user-permission-inherit] 读取任务-详情编辑权限失败:', error)
return false
}
}
/**
* 私密任务-详情查看/下载:只认权限组勾选,不给上传者/管理员额外豁免。
*/
export async function canUserAccessPrivateTaskDetail(userId: string): Promise<boolean> {
if (!userId) return false
try {
const { resolveRosterPermissionPack } = await import('./department-permission')
const pack = await resolveRosterPermissionPack(userId)
return pack?.['任务-详情']?.canAccessPrivate === true
} catch (error) {
console.error('[user-permission-inherit] 读取任务-详情私密权限失败:', error)
return false
}
}
/**
* 视频提前访问豁免:用户加入的任意权限组开启了该组级能力即获得。
* 部门进组是素材可见范围的底子,不参与此判定;豁免按"人进组是加餐"的口径只认个人挂载。
*/
export async function resolveUserVideoEarlyAccess(userId: string): Promise<boolean> {
if (!userId) return false
try {
const assignment = await prisma.userPermissionInherit.findFirst({
where: { userId, pack: { videoEarlyAccess: true } },
select: { id: true },
})
return Boolean(assignment)
} catch (error) {
if (isMissingInheritTable(error)) return false
throw error
}
}
export async function savePermissionPackItems(
slug: string,
permissions: Array<MaterialPermissionFlags & { materialType: string }>,
) {
const pack = await prisma.materialPermissionPack.findUnique({
where: { slug },
select: { id: true },
})
if (!pack) throw new Error('没有这套继承权限')
await prisma.$transaction([
prisma.materialPermissionPackItem.deleteMany({ where: { packId: pack.id } }),
prisma.materialPermissionPackItem.createMany({
data: permissions
.filter((item) => item.materialType)
.map((item) => {
const taskDetailOnlyFlags = normalizeTaskDetailOnlyFlags(item.materialType, item)
return {
packId: pack.id,
materialType: item.materialType,
canView: item.canView,
canDownload: item.canDownload,
canDownloadSource: item.canDownloadSource,
canShare: item.canShare,
canEditAssets: taskDetailOnlyFlags.canEditAssets,
canAccessPrivate: taskDetailOnlyFlags.canAccessPrivate,
}
}),
}),
])
}