196 lines
6.2 KiB
TypeScript
196 lines
6.2 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: { name: preset.name },
|
|
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 { seedDefaultGroupDepartments } = await import('./permission-groups')
|
|
await seedDefaultGroupDepartments()
|
|
}
|
|
|
|
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,
|
|
}
|
|
}),
|
|
}),
|
|
])
|
|
}
|