161 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 });
}
}