161 lines
5.4 KiB
TypeScript
161 lines
5.4 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import { getServerSession } from 'next-auth';
|
||
import { authOptions } from '@/lib/auth';
|
||
import { prisma } from '@/lib/prisma';
|
||
import { isSystemAdmin } from '@/lib/department-permission';
|
||
|
||
// GET /api/admin/users - 获取用户列表
|
||
export async function GET() {
|
||
try {
|
||
const session = await getServerSession(authOptions);
|
||
if (!session || !session.user || !isSystemAdmin((session.user as any).role)) {
|
||
return NextResponse.json({ error: '无权限' }, { status: 403 });
|
||
}
|
||
|
||
const users = await prisma.user.findMany({
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
username: true,
|
||
realName: true, // 真实姓名(用于搜索)
|
||
role: true,
|
||
isActive: true, // 保留,向后兼容
|
||
status: true, // 新增:用户状态(阶段一)
|
||
canDownload: true,
|
||
department: true, // 保留,向后兼容
|
||
departmentId: true, // 新增:部门ID(阶段二)
|
||
managedDepartment: true, // 管理员负责的部门
|
||
createdAt: true,
|
||
lastLoginAt: true, // 新增:最后登录时间(阶段一)
|
||
loginCount: true, // 新增:登录次数(阶段一)
|
||
archivedAt: true, // 新增:归档时间(阶段一)
|
||
departmentRef: { // 新增:部门关联信息(阶段二)
|
||
select: {
|
||
id: true,
|
||
name: true,
|
||
}
|
||
},
|
||
_count: {
|
||
select: {
|
||
materials: {
|
||
where: {
|
||
deletedAt: null,
|
||
} as any,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
orderBy: {
|
||
createdAt: 'desc',
|
||
},
|
||
});
|
||
|
||
// 批量查询活跃绑定,附加权威部门路径(避免 N+1)
|
||
// 权威人员数据:UserEmployeeBinding -> PerformanceSurveyEmployeeProfile.departmentPath
|
||
const activeBindings = await prisma.userEmployeeBinding.findMany({
|
||
where: { status: 'ACTIVE' },
|
||
select: {
|
||
userId: true,
|
||
employeeProfile: {
|
||
select: {
|
||
departmentPath: true,
|
||
departmentName: true,
|
||
displayName: true,
|
||
positionLevel: true,
|
||
levelType: true,
|
||
isLeadership: true,
|
||
employmentState: true,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
const bindingMap = new Map(
|
||
activeBindings
|
||
.filter((b) => b.employeeProfile)
|
||
.map((b) => [
|
||
b.userId,
|
||
{
|
||
departmentPath: b.employeeProfile!.departmentPath,
|
||
departmentName: b.employeeProfile!.departmentName,
|
||
displayName: b.employeeProfile!.displayName,
|
||
positionLevel: b.employeeProfile!.positionLevel,
|
||
levelType: b.employeeProfile!.levelType,
|
||
isLeadership: b.employeeProfile!.isLeadership,
|
||
employmentState: b.employeeProfile!.employmentState,
|
||
},
|
||
]),
|
||
);
|
||
|
||
// 附加权威部门字段到每个用户
|
||
const enriched = users.map((u) => {
|
||
const binding = bindingMap.get(u.id);
|
||
return {
|
||
...u,
|
||
// 权威部门路径(新组织树),如「江苏简尚品牌管理有限公司/运营中心G/运营一部(CZ)」
|
||
employeeDeptPath: binding?.departmentPath ?? null,
|
||
// 权威部门名(最后一段)
|
||
employeeDeptName: binding?.departmentPath ? binding.departmentPath.split('/').pop() ?? null : null,
|
||
employeeDisplayName: binding?.displayName ?? null,
|
||
employeePositionLevel: binding?.positionLevel ?? null,
|
||
employeeLevelType: binding?.levelType ?? null,
|
||
employeeIsLeadership: binding?.isLeadership ?? false,
|
||
employeeEmploymentState: binding?.employmentState ?? null,
|
||
// 是否有权威绑定(无绑定 = 待人工确认)
|
||
hasEmployeeBinding: binding != null,
|
||
};
|
||
});
|
||
|
||
return NextResponse.json(enriched);
|
||
} catch (error) {
|
||
console.error('Error fetching users:', error);
|
||
return NextResponse.json({ error: '获取用户列表失败' }, { status: 500 });
|
||
}
|
||
}
|
||
|
||
// DELETE /api/admin/users - 归档用户(保留其素材和历史记录)
|
||
export async function DELETE(request: NextRequest) {
|
||
try {
|
||
const session = await getServerSession(authOptions);
|
||
if (!session || !session.user || !isSystemAdmin((session.user as any).role)) {
|
||
return NextResponse.json({ error: '无权限' }, { status: 403 });
|
||
}
|
||
|
||
const { searchParams } = new URL(request.url);
|
||
const id = searchParams.get('id');
|
||
|
||
if (!id) {
|
||
return NextResponse.json({ error: '缺少用户ID' }, { status: 400 });
|
||
}
|
||
|
||
// 不能删除自己
|
||
if (id === (session.user as any).id) {
|
||
return NextResponse.json({ error: '不能删除自己' }, { status: 400 });
|
||
}
|
||
|
||
const existingUser = await prisma.user.findUnique({
|
||
where: { id },
|
||
select: { id: true, status: true },
|
||
});
|
||
|
||
if (!existingUser) {
|
||
return NextResponse.json({ error: '用户不存在' }, { status: 404 });
|
||
}
|
||
|
||
await prisma.user.update({
|
||
where: { id },
|
||
data: {
|
||
status: 'archived',
|
||
isActive: false,
|
||
archivedAt: new Date(),
|
||
statusChangedAt: new Date(),
|
||
statusChangedBy: (session.user as any).id,
|
||
},
|
||
});
|
||
|
||
return NextResponse.json({ message: '用户已归档,素材和历史记录已保留' });
|
||
} catch (error) {
|
||
console.error('Error deleting user:', error);
|
||
return NextResponse.json({ error: '归档失败' }, { status: 500 });
|
||
}
|
||
}
|