chenpei 683f86c7af feat: 权限组可挂部门和多人
一组就是一份权限。部门进组当底子,人进组当加餐,能看的取并集。
2026-08-20 12:41:12 +08:00

168 lines
6.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { Prisma } from '@prisma/client'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { isSuperAdmin } from '@/lib/department-permission'
import { BINDING_STATUS } from '@/lib/employee-directory'
import { findNearestGroupMemberships } from '@/lib/organization-material-permissions'
import { ensureDefaultPermissionPacks } from '@/lib/user-permission-inherit'
async function requireSuperAdmin() {
const session = await getServerSession(authOptions)
const userId = (session?.user as { id?: string } | undefined)?.id
if (!session || !(await isSuperAdmin(userId))) return null
return session
}
export async function GET(request: NextRequest) {
try {
if (!(await requireSuperAdmin())) {
return NextResponse.json({ error: '需要超级管理员权限' }, { status: 403 })
}
await ensureDefaultPermissionPacks()
const keyword = String(new URL(request.url).searchParams.get('q') || '').trim()
if (keyword.length < 1) {
return NextResponse.json({ people: [], groups: [] })
}
const [bindings, packs] = await Promise.all([
prisma.userEmployeeBinding.findMany({
where: {
status: BINDING_STATUS.ACTIVE,
user: { isActive: true, dingtalkUserId: { not: null } },
OR: [
{ user: { username: { contains: keyword, mode: 'insensitive' } } },
{ user: { realName: { contains: keyword, mode: 'insensitive' } } },
{ employeeProfile: { displayName: { contains: keyword, mode: 'insensitive' } } },
{ employeeProfile: { jobNumber: { contains: keyword, mode: 'insensitive' } } },
],
},
take: 20,
select: {
userId: true,
user: {
select: {
username: true,
realName: true,
permissionInherits: {
select: { pack: { select: { slug: true, name: true } } },
},
},
},
employeeProfile: {
select: { displayName: true, departmentPath: true, departmentName: true, jobNumber: true },
},
},
}),
prisma.materialPermissionPack.findMany({
orderBy: { name: 'asc' },
select: { id: true, slug: true, name: true },
}),
])
const departmentRows = await prisma.materialPermissionPackDepartment
.findMany({
select: { packId: true, departmentPath: true },
})
.catch((error) => {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2021') {
return [] as Array<{ packId: string; departmentPath: string }>
}
throw error
})
const memberships = departmentRows.flatMap((row) => {
const pack = packs.find((item) => item.id === row.packId)
if (!pack) return []
return [{ departmentPath: row.departmentPath, slug: pack.slug, name: pack.name }]
})
return NextResponse.json({
groups: packs.map((pack) => ({ slug: pack.slug, name: pack.name })),
people: bindings.map((binding) => {
const path = binding.employeeProfile.departmentPath
const nearest = path ? findNearestGroupMemberships(path, memberships) : null
const personalGroups = binding.user.permissionInherits.map((row) => ({
slug: row.pack.slug,
name: row.pack.name,
}))
return {
userId: binding.userId,
username: binding.user.username,
displayName: binding.employeeProfile.displayName || binding.user.realName || binding.user.username,
jobNumber: binding.employeeProfile.jobNumber,
departmentPath: path,
departmentName: binding.employeeProfile.departmentName,
departmentGroups: nearest
? nearest.items.map((item) => ({
slug: item.slug,
name: item.name,
attachedPath: nearest.attachedPath,
source: nearest.attachedPath === path ? 'own' : 'inherit',
}))
: [],
personalGroups,
}
}),
})
} catch (error) {
console.error('[user-permissions] GET failed', error)
return NextResponse.json({ error: '搜索人员权限失败' }, { status: 500 })
}
}
export async function PUT(request: NextRequest) {
try {
if (!(await requireSuperAdmin())) {
return NextResponse.json({ error: '需要超级管理员权限' }, { status: 403 })
}
const body = await request.json()
const action = String(body?.action || '').trim()
const userId = String(body?.userId || '').trim()
const slug = String(body?.slug || '').trim()
if (!userId) return NextResponse.json({ error: '请选择人员' }, { status: 400 })
if (!slug) return NextResponse.json({ error: '缺少权限组' }, { status: 400 })
const [user, pack] = await Promise.all([
prisma.user.findUnique({
where: { id: userId },
select: { id: true, dingtalkUserId: true },
}),
prisma.materialPermissionPack.findUnique({
where: { slug },
select: { id: true },
}),
])
if (!user) return NextResponse.json({ error: '没有这个账号' }, { status: 400 })
if (!user.dingtalkUserId) {
return NextResponse.json({ error: '这个账号没有钉钉绑定,不能进权限组' }, { status: 400 })
}
if (!pack) return NextResponse.json({ error: '没有这个权限组' }, { status: 400 })
if (action === 'remove-member') {
await prisma.userPermissionInherit.deleteMany({
where: { userId, packId: pack.id },
})
return NextResponse.json({ ok: true })
}
if (action === 'add-member') {
await prisma.userPermissionInherit.upsert({
where: { userId_packId: { userId, packId: pack.id } },
update: {},
create: { userId, packId: pack.id },
})
return NextResponse.json({ ok: true })
}
return NextResponse.json({ error: '不认识这个操作' }, { status: 400 })
} catch (error) {
console.error('[user-permissions] PUT failed', error)
return NextResponse.json({ error: '保存人员权限失败' }, { status: 500 })
}
}