feat: 增加DEFAULT_USER_PASSWORD,作为创建用户时的默认密码,添加p-limit库,添加DB_PARALLEL_LIMIT = 32环境变量作为“数据库批次操作默认并发数” 限制只有超级管理员才能创建超级管理员用户 删除用户时可以级联删除SelectionLog 添加zustand全局状态管理 一对多的院系管理功能 ,支持增删改查院系管理员信息、用户可以在header中切换管理的院系

This commit is contained in:
2025-11-18 20:07:42 +08:00
parent 7f3190a223
commit 2a80a44972
31 changed files with 1651 additions and 96 deletions

View File

@@ -6,6 +6,10 @@ import { transformDataTableQueryParams } from '@/server/utils/data-table-helper'
import bcrypt from 'bcryptjs'
import { z } from 'zod'
import { inferProcedureOutput, TRPCError } from '@trpc/server'
import pLimit from 'p-limit'
// 从环境变量获取并发限制默认为16
const dbParallelLimit = pLimit(parseInt(process.env.DB_PARALLEL_LIMIT || '16', 10))
export const usersRouter = createTRPCRouter({
list: permissionRequiredProcedure(Permissions.USER_MANAGE)
@@ -186,14 +190,16 @@ export const usersRouter = createTRPCRouter({
await Promise.all(
batch.map(user =>
ctx.db.user.update({
where: { id: user.id },
data: {
roles: action === 'grant'
? { connect: { id: roleId } }
: { disconnect: { id: roleId } }
}
})
dbParallelLimit(() =>
ctx.db.user.update({
where: { id: user.id },
data: {
roles: action === 'grant'
? { connect: { id: roleId } }
: { disconnect: { id: roleId } }
}
})
)
)
)
@@ -206,6 +212,11 @@ export const usersRouter = createTRPCRouter({
create: permissionRequiredProcedure(Permissions.USER_MANAGE).input(createUserSchema).mutation(async ({ ctx, input }) => {
const { id, name, status, deptCode, password, roleIds, isSuperAdmin } = input
// 检查是否尝试创建超级管理员,只有超级管理员才能创建超级管理员
if (isSuperAdmin && !ctx.session?.user?.isSuperAdmin) {
throw new TRPCError({ code: 'FORBIDDEN', message: '只有超级管理员才能创建超级管理员用户' })
}
const existingUser = await ctx.db.user.findUnique({ where: { id } })
if (existingUser) throw new TRPCError({ code: 'BAD_REQUEST', message: '用户ID已存在' })
@@ -235,6 +246,11 @@ export const usersRouter = createTRPCRouter({
const existingUser = await ctx.db.user.findUnique({ where: { id } })
if (!existingUser) throw new TRPCError({ code: 'NOT_FOUND', message: '用户不存在' })
// 检查是否尝试修改 isSuperAdmin 字段,只有超级管理员才能操作
if (isSuperAdmin !== existingUser.isSuperAdmin && !ctx.session?.user?.isSuperAdmin) {
throw new TRPCError({ code: 'FORBIDDEN', message: '只有超级管理员才能修改超级管理员权限' })
}
// 准备更新数据
const updateData: any = {
name: name?.trim() || '',
@@ -372,6 +388,102 @@ export const usersRouter = createTRPCRouter({
))
).sort((a, b) => a.id - b.id),
}
}),
// 获取当前用户可管理的院系列表和正在管理的院系
getManagedDepts: permissionRequiredProcedure('')
.query(async ({ ctx }) => {
const userId = ctx.session!.user.id
// 获取用户当前信息
const currentUser = await ctx.db.user.findUnique({
where: { id: userId },
select: { currentManagedDept: true }
})
let depts: Array<{ code: string; name: string; fullName: string }>
// 超级管理员可以管理所有院系
if (ctx.session?.user?.isSuperAdmin) {
depts = await ctx.db.dept.findMany({
orderBy: { code: 'asc' }
})
} else {
// 普通用户只能管理自己被授权的院系
const deptAdmins = await ctx.db.deptAdmin.findMany({
where: { uid: userId },
include: { dept: true },
orderBy: { deptCode: 'asc' }
})
depts = deptAdmins.map(da => da.dept)
}
// 如果用户当前没有管理院系,但有可管理的院系,自动设置为第一个
let currentDept = currentUser?.currentManagedDept
if (!currentDept && depts.length > 0) {
currentDept = depts[0].code
await ctx.db.user.update({
where: { id: userId },
data: { currentManagedDept: currentDept }
})
}
return {
currentDept,
depts
}
}),
// 切换当前管理的院系
switchManagedDept: permissionRequiredProcedure('')
.input(z.object({
deptCode: z.string().nullable()
}))
.mutation(async ({ ctx, input }) => {
const { deptCode } = input
// 如果要切换到某个院系,需要验证权限
if (deptCode) {
// 超级管理员可以切换到任意院系
if (!ctx.session?.user?.isSuperAdmin) {
// 普通用户需要验证是否有该院系的管理权限
const deptAdmin = await ctx.db.deptAdmin.findUnique({
where: {
uidx_uid_dept_code: {
uid: ctx.session!.user.id,
deptCode: deptCode
}
}
})
if (!deptAdmin) {
throw new TRPCError({
code: 'FORBIDDEN',
message: '您没有该院系的管理权限'
})
}
}
// 验证院系是否存在
const dept = await ctx.db.dept.findUnique({
where: { code: deptCode }
})
if (!dept) {
throw new TRPCError({
code: 'NOT_FOUND',
message: '院系不存在'
})
}
}
// 更新用户的当前管理院系
await ctx.db.user.update({
where: { id: ctx.session!.user.id },
data: { currentManagedDept: deptCode }
})
return { success: true, deptCode }
})
})