const bcrypt = require('bcryptjs'); const { Op } = require('sequelize'); const User = require('../models/User'); const Role = require('../models/Role'); const Tenant = require('../models/Tenant'); const { logOperation } = require('../utils/logger'); const response = require('../utils/response'); // 格式化日期时间(年月日时分秒) const formatDateTime = (date) => { if (!date) return null; const dateObj = date instanceof Date ? date : new Date(date); if (isNaN(dateObj.getTime())) return null; const beijingDate = new Date(dateObj.getTime() + 8 * 60 * 60 * 1000); return beijingDate.toISOString().replace('T', ' ').slice(0, 19); }; // 格式化用户数据 const formatUserData = (user) => { const data = user.toJSON ? user.toJSON() : user; return { ...data, createTime: formatDateTime(data.createTime), updateTime: formatDateTime(data.updateTime) }; }; // 获取用户列表 exports.getUserList = async (req, res) => { try { const { page = 1, pageSize = 10, username, roleId, status } = req.query; // 构建查询条件 - 只查询普通用户(不包括系统管理员) const where = { tenantId: req.user.tenantId, isDeleted: 0, userType: 'user' }; if (username) { where.username = { [Op.like]: `%${username}%` }; } if (roleId) { where.roleId = roleId; } if (status) { where.status = status; } // 查询用户列表 const { count, rows } = await User.findAndCountAll({ where, attributes: ['id', 'username', 'nickname', 'roleId', 'status', 'createTime', 'updateTime'], include: [{ model: Role, as: 'role', attributes: ['id', 'name', 'code'] }], order: [['createTime', 'DESC']], offset: (page - 1) * pageSize, limit: parseInt(pageSize) }); response.success(res, '获取成功', { list: rows.map(formatUserData), total: count, page: parseInt(page), pageSize: parseInt(pageSize) }); } catch (error) { console.error('获取用户列表错误:', error); response.serverError(res, '获取用户列表失败', error); } }; // 获取用户详情 exports.getUserById = async (req, res) => { try { const { id } = req.params; const user = await User.findOne({ where: { id, tenantId: req.user.tenantId, isDeleted: 0 }, attributes: ['id', 'username', 'nickname', 'roleId', 'status', 'createTime', 'updateTime'], include: [{ model: Role, as: 'role', attributes: ['id', 'name', 'code'] }] }); if (!user) { return response.notFound(res, '用户不存在'); } response.success(res, '获取成功', formatUserData(user)); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '用户管理', action: '查询', description: `获取用户详情,ID: ${id}`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } catch (error) { console.error('获取用户详情错误:', error); response.serverError(res, '获取用户详情失败', error); } }; // 创建用户 exports.createUser = async (req, res) => { try { const { username, password, nickname, roleId } = req.body; // 参数验证 if (!username || !password) { return response.badRequest(res, '用户名和密码不能为空'); } if (username.length < 3 || username.length > 20) { return response.badRequest(res, '用户名长度应在3-20个字符之间'); } if (password.length < 6 || password.length > 20) { return response.badRequest(res, '密码长度应在6-20个字符之间'); } if (!roleId) { return response.badRequest(res, '角色不能为空'); } // 检查租户资源使用情况(仅记录日志,不阻止创建) const tenant = await Tenant.findByPk(req.user.tenantId); let currentUserCount = 0; let isOverage = false; if (tenant) { // 获取当前用户数量 currentUserCount = await User.count({ where: { tenantId: req.user.tenantId, isDeleted: 0 } }); // 检查是否超出限制 if (currentUserCount >= tenant.maxUsers) { isOverage = true; // 记录超额使用日志 await logOperation({ userId: req.user.id, username: req.user.username, tenantId: req.user.tenantId, module: '用户管理', action: '超额创建', description: `创建用户"${username}",当前已使用 ${currentUserCount + 1}/${tenant.maxUsers} 人(超出限制)`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } } // 检查角色是否存在 const role = await Role.findByPk(roleId, { where: { isDeleted: 0, status: 'active' } }); if (!role) { return response.badRequest(res, '角色不存在或已禁用'); } // 检查用户名是否已存在(同一租户下) const existingUser = await User.findOne({ where: { username, tenantId: req.user.tenantId, isDeleted: 0 } }); if (existingUser) { return response.badRequest(res, '用户名已存在'); } // 加密密码 const hashedPassword = await bcrypt.hash(password, 10); // 创建用户 const user = await User.create({ username, password: hashedPassword, nickname: nickname || null, roleId, tenantId: req.user.tenantId, createBy: req.user.id, updateBy: req.user.id }); response.success(res, '创建成功', { id: user.id, username: user.username, nickname: user.nickname, roleId: user.roleId, createTime: formatDateTime(user.createTime), warning: isOverage && tenant ? `当前已超出套餐限制(${tenant.maxUsers}人),续费时将收取超额费用` : null }); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '用户管理', action: '创建', description: `创建用户: ${username}`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } catch (error) { console.error('创建用户错误:', error); response.serverError(res, '创建用户失败', error); } }; // 更新用户 exports.updateUser = async (req, res) => { try { const { id } = req.params; const { nickname, roleId, status } = req.body; // 查找用户 const user = await User.findOne({ where: { id, tenantId: req.user.tenantId, isDeleted: 0 } }); if (!user) { return response.notFound(res, '用户不存在'); } // 检查角色是否存在 if (roleId) { const role = await Role.findByPk(roleId, { where: { isDeleted: 0, status: 'active' } }); if (!role) { return response.badRequest(res, '角色不存在或已禁用'); } } // 构建更新数据 const updateData = { updateBy: req.user.id }; if (nickname !== undefined) updateData.nickname = nickname || null; if (roleId !== undefined) updateData.roleId = roleId; if (status !== undefined) updateData.status = status; // 更新用户 await user.update(updateData); response.success(res, '更新成功', { id: user.id, username: user.username, nickname: user.nickname, roleId: user.roleId, status: user.status, updateTime: formatDateTime(user.updateTime) }); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '用户管理', action: '更新', description: `更新用户: ${user.username}`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } catch (error) { console.error('更新用户错误:', error); response.serverError(res, '更新用户失败', error); } }; // 删除用户 exports.deleteUser = async (req, res) => { try { const { id } = req.params; // 不能删除自己 if (parseInt(id) === req.user.id) { return response.badRequest(res, '不能删除自己的账号'); } // 查找用户 const user = await User.findOne({ where: { id, tenantId: req.user.tenantId, isDeleted: 0 } }); if (!user) { return response.notFound(res, '用户不存在'); } // 软删除 await user.update({ isDeleted: 1, updateBy: req.user.id }); response.success(res, '删除成功'); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '用户管理', action: '删除', description: `删除用户: ${user.username}`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } catch (error) { console.error('删除用户错误:', error); response.serverError(res, '删除用户失败', error); } }; // 重置用户密码 exports.resetUserPassword = async (req, res) => { try { const { id } = req.params; const defaultPassword = '123456'; // 查找用户 const user = await User.findOne({ where: { id, tenantId: req.user.tenantId, isDeleted: 0 } }); if (!user) { return response.notFound(res, '用户不存在'); } // 加密默认密码 const hashedPassword = await bcrypt.hash(defaultPassword, 10); // 更新密码 await user.update({ password: hashedPassword, updateBy: req.user.id }); response.success(res, '密码重置成功', { defaultPassword }); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '用户管理', action: '重置密码', description: `重置用户密码: ${user.username}`, method: req.method, path: req.path, ip: req.ip, status: 'success' }); } catch (error) { console.error('重置密码错误:', error); response.serverError(res, '重置密码失败', error); } }; // 获取用户列表(用于下拉选择) exports.getAllUsers = async (req, res) => { try { const users = await User.findAll({ where: { tenantId: req.user.tenantId, isDeleted: 0, status: 'active' }, attributes: ['id', 'username', 'nickname'] }); response.success(res, '获取用户列表成功', users.map(formatUserData)); } catch (error) { console.error('获取用户列表错误:', error); response.serverError(res, '获取用户列表失败', error); } }; // 获取当前用户信息 exports.getCurrentUserInfo = async (req, res) => { try { const user = await User.findByPk(req.user.id, { attributes: ['id', 'username', 'nickname', 'status', 'createTime', 'updateTime'], include: [{ model: Role, as: 'role', attributes: ['id', 'name', 'code'] }] }); if (!user) { return response.notFound(res, '用户不存在'); } response.success(res, '获取用户信息成功', formatUserData(user)); } catch (error) { console.error('获取用户信息失败:', error); response.serverError(res, '获取用户信息失败', error); } }; // 更新个人资料 exports.updateUserProfile = async (req, res) => { try { const { nickname } = req.body; // 验证昵称 if (!nickname || nickname.trim().length === 0) { return response.badRequest(res, '昵称不能为空'); } // 更新用户信息 const user = await User.findByPk(req.user.id); if (!user) { return response.notFound(res, '用户不存在'); } await user.update({ nickname, updateBy: req.user.id }); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '个人中心', action: '更新资料', description: '更新个人资料', method: req.method, path: req.path, ip: req.ip, status: 'success' }); response.success(res, '个人资料更新成功', { id: user.id, username: user.username, nickname: user.nickname, status: user.status, createTime: formatDateTime(user.createTime), updateTime: formatDateTime(user.updateTime) }); } catch (error) { console.error('更新个人资料失败:', error); response.serverError(res, '更新个人资料失败', error); } }; // 修改密码 exports.changePassword = async (req, res) => { try { const { oldPassword, newPassword } = req.body; // 验证参数 if (!oldPassword || !newPassword) { return response.badRequest(res, '旧密码和新密码不能为空'); } if (newPassword.length < 6) { return response.badRequest(res, '新密码长度至少6位'); } // 验证旧密码 const user = await User.findByPk(req.user.id); if (!user) { return response.notFound(res, '用户不存在'); } const isPasswordValid = await bcrypt.compare(oldPassword, user.password); if (!isPasswordValid) { return response.badRequest(res, '旧密码错误'); } // 更新密码 const hashedPassword = await bcrypt.hash(newPassword, 10); await user.update({ password: hashedPassword, updateBy: req.user.id }); // 记录操作日志 await logOperation({ userId: req.user.id, username: req.user.username, module: '个人中心', action: '修改密码', description: '修改个人密码', method: req.method, path: req.path, ip: req.ip, status: 'success' }); response.success(res, '密码修改成功'); } catch (error) { console.error('修改密码失败:', error); response.serverError(res, '修改密码失败', error); } };