import { Request, Response } from 'express'; import { UserModel } from '../models/user'; import { QuizModel } from '../models/quiz'; import { UserGroupModel } from '../models/userGroup'; export class AdminUserController { static async getUsers(req: Request, res: Response) { try { const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 10; const keyword = req.query.keyword as string; // TODO: Implement search in UserModel if needed, currently filtering in memory or ignored // For now assuming findAll supports basic pagination. // If keyword is needed, we should add findByKeyword to UserModel. // But based on existing code, it seems it wasn't implemented there. // Let's stick to what was there or improve if I see it. // The previous code used findAll(limit, offset). const result = await UserModel.findAll(limit, (page - 1) * limit); // Filter by keyword if provided (naive implementation since DB doesn't support it yet via API) let users = result.users; if (keyword) { users = users.filter(u => u.name.includes(keyword) || u.phone.includes(keyword)); } // 获取每个用户的用户组信息 const usersWithGroups = await Promise.all(users.map(async (u) => { const groups = await UserGroupModel.getUserGroups(u.id); return { ...u, password: u.password ?? '', groups }; })); res.json({ success: true, data: usersWithGroups, pagination: { page, limit, total: result.total, pages: Math.ceil(result.total / limit) } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取用户列表失败' }); } } // 创建用户 static async createUser(req: Request, res: Response) { try { const { name, phone, password, groupIds } = req.body; if (!name || !phone) { return res.status(400).json({ success: false, message: '姓名和手机号不能为空' }); } const errors = UserModel.validateUserData({ name, phone, password }); if (errors.length > 0) { return res.status(400).json({ success: false, message: '数据验证失败: ' + errors.join(', ') }); } const user = await UserModel.create({ name, phone, password }); // 自动加入"全体用户"组 const allUsersGroup = await UserGroupModel.getSystemGroup(); if (allUsersGroup) { await UserGroupModel.addMember(allUsersGroup.id, user.id); } // 添加到指定用户组 if (groupIds && Array.isArray(groupIds)) { await UserGroupModel.updateUserGroups(user.id, groupIds); } res.json({ success: true, data: user }); } catch (error: any) { // 处理手机号已存在的错误 if (error.message === '手机号已存在' || error.code === 'SQLITE_CONSTRAINT_UNIQUE') { return res.status(400).json({ success: false, message: '手机号已存在' }); } res.status(500).json({ success: false, message: error.message || '创建用户失败' }); } } static async deleteUser(req: Request, res: Response) { try { const { userId } = req.body; if (!userId) { return res.status(400).json({ success: false, message: 'userId不能为空' }); } const user = await UserModel.findById(userId); if (!user) { return res.status(404).json({ success: false, message: '用户不存在' }); } await UserModel.delete(userId); res.json({ success: true, message: '删除成功' }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '删除用户失败' }); } } static async exportUsers(req: Request, res: Response) { try { const result = await UserModel.findAll(10000, 0); const data = result.users.map((u) => ({ ID: u.id, 姓名: u.name, 手机号: u.phone, 密码: u.password ?? '', 注册时间: u.createdAt })); res.json({ success: true, data }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '导出用户失败' }); } } // 更新用户信息 static async updateUser(req: Request, res: Response) { try { const { id } = req.params; const { name, phone, password, groupIds } = req.body; const user = await UserModel.findById(id); if (!user) { return res.status(404).json({ success: false, message: '用户不存在' }); } // 准备更新数据 const updateData: Partial<{ name: string; phone: string; password: string }> = {}; if (name !== undefined) updateData.name = name; if (phone !== undefined) updateData.phone = phone; if (password !== undefined) updateData.password = password; // 更新用户 const updatedUser = await UserModel.update(id, updateData); // 更新用户组 if (groupIds && Array.isArray(groupIds)) { await UserGroupModel.updateUserGroups(id, groupIds); } // 获取最新用户组信息 const groups = await UserGroupModel.getUserGroups(id); res.json({ success: true, data: { ...updatedUser, groups } }); } catch (error: any) { // 处理手机号已存在的错误 if (error.message === '手机号已存在') { return res.status(400).json({ success: false, message: '手机号已存在' }); } // 处理SQLITE_CONSTRAINT_UNIQUE错误 if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') { return res.status(400).json({ success: false, message: '手机号已存在' }); } // 处理数据验证错误 if (error.message.includes('姓名长度必须在2-20个字符之间') || error.message.includes('手机号格式不正确')) { return res.status(400).json({ success: false, message: error.message }); } // 处理其他错误 res.status(500).json({ success: false, message: error.message || '更新用户失败' }); } } static async importUsers(req: Request, res: Response) { try { const file = (req as any).file; if (!file) { return res.status(400).json({ success: false, message: '请上传Excel文件' }); } const XLSX = await import('xlsx'); const workbook = XLSX.read(file.buffer, { type: 'buffer' }); const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; const rows = XLSX.utils.sheet_to_json(worksheet) as any[]; const errors: string[] = []; let imported = 0; for (let i = 0; i < rows.length; i++) { const row = rows[i]; try { const name = row['姓名'] || row['name']; const phone = row['手机号'] || row['phone']; const password = row['密码'] || row['password'] || ''; if (!name || !phone) { errors.push(`第${i + 2}行:姓名或手机号缺失`); continue; } await UserModel.create({ name, phone, password }); imported++; } catch (error: any) { if (error.message === '手机号已存在') { errors.push(`第${i + 2}行:手机号重复`); } else { errors.push(`第${i + 2}行:${error.message}`); } } } res.json({ success: true, data: { imported, total: rows.length, errors } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '导入用户失败' }); } } static async getUserRecords(req: Request, res: Response) { try { const { userId } = req.params; const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 10; const result = await QuizModel.findRecordsByUserId(userId, limit, (page - 1) * limit); res.json({ success: true, data: result.records, pagination: { page, limit, total: result.total, pages: Math.ceil(result.total / limit) } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取用户答题记录失败' }); } } static async getRecordDetail(req: Request, res: Response) { try { const { recordId } = req.params; const record = await QuizModel.findRecordById(recordId); if (!record) { return res.status(404).json({ success: false, message: '答题记录不存在' }); } const answers = await QuizModel.findAnswersByRecordId(recordId); res.json({ success: true, data: { record, answers } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取答题记录详情失败' }); } } }