rentease-backend/controllers/authController.js

297 lines
6.8 KiB
JavaScript
Raw Permalink Normal View History

2026-03-08 16:28:33 +00:00
const bcrypt = require('bcryptjs');
const User = require('../models/User');
const Role = require('../models/Role');
const Menu = require('../models/Menu');
const { generateToken } = require('../middleware/auth');
const { logLogin, getClientIp } = require('../utils/logger');
// 登录
exports.login = async (req, res) => {
const clientIp = getClientIp(req);
const userAgent = req.headers['user-agent'];
let username = req.body.username;
try {
const { password } = req.body;
// 参数验证
if (!username || !password) {
await logLogin({
username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'fail',
message: '用户名和密码不能为空'
});
return res.status(400).json({
code: 400,
message: '用户名和密码不能为空'
});
}
// 查找用户
const user = await User.findOne({
where: { username },
include: [{
model: Role,
as: 'role'
}]
});
if (!user) {
await logLogin({
username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'fail',
message: '用户名或密码错误'
});
return res.status(401).json({
code: 401,
message: '用户名或密码错误'
});
}
// 检查用户状态
if (user.status === 'disabled') {
await logLogin({
userId: user.id,
username: user.username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'fail',
message: '账号已被禁用'
});
return res.status(401).json({
code: 401,
message: '账号已被禁用'
});
}
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
await logLogin({
userId: user.id,
username: user.username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'fail',
message: '用户名或密码错误'
});
return res.status(401).json({
code: 401,
message: '用户名或密码错误'
});
}
// 生成 Token
const token = generateToken(user);
// 获取用户菜单权限
let menus = [];
// 超级管理员isSuperAdmin为1或角色code为admin返回所有菜单
if (user.isSuperAdmin === 1 || (user.role && user.role.code === 'admin')) {
// 超级管理员返回所有菜单
const allMenus = await Menu.findAll({
where: {
isDeleted: false,
status: 'active',
visible: 'show'
},
order: [['sort', 'ASC']]
});
menus = buildMenuTree(allMenus);
} else if (user.roleId) {
// 普通用户返回角色分配的菜单
const roleData = await Role.findByPk(user.roleId, {
include: [{
model: Menu,
as: 'menus',
where: {
isDeleted: false,
status: 'active',
visible: 'show'
},
through: { attributes: [] }
}]
});
if (roleData && roleData.menus) {
const sortedMenus = roleData.menus.sort((a, b) => a.sort - b.sort);
menus = buildMenuTree(sortedMenus);
}
}
// 记录登录成功日志
await logLogin({
userId: user.id,
username: user.username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'success',
message: '登录成功'
});
// 返回用户信息和 Token
res.json({
code: 200,
message: '登录成功',
data: {
token,
userInfo: {
id: user.id,
username: user.username,
nickname: user.nickname,
role: user.role
},
menus
}
});
} catch (error) {
console.error('登录错误:', error);
await logLogin({
username,
loginType: 'login',
ip: clientIp,
userAgent,
status: 'fail',
message: error.message
});
res.status(500).json({
code: 500,
message: '登录失败',
error: error.message
});
}
};
// 登出
exports.logout = async (req, res) => {
try {
// 记录登出日志
const user = req.user || {};
await logLogin({
userId: user.id,
username: user.username,
loginType: 'logout',
ip: getClientIp(req),
userAgent: req.headers['user-agent'],
status: 'success',
message: '登出成功'
});
res.json({
code: 200,
message: '登出成功'
});
} catch (error) {
console.error('登出错误:', error);
res.json({
code: 200,
message: '登出成功'
});
}
};
// 构建菜单树
function buildMenuTree(menus, parentId = null) {
return menus
.filter(menu => menu.parentId === parentId)
.map(menu => ({
id: menu.id,
name: menu.name,
code: menu.code,
type: menu.type,
path: menu.path,
component: menu.component,
icon: menu.icon,
sort: menu.sort,
visible: menu.visible,
children: buildMenuTree(menus, menu.id)
}));
}
// 获取当前用户信息
exports.getCurrentUser = async (req, res) => {
try {
// req.user 由 authMiddleware 附加
res.json({
code: 200,
message: '获取成功',
data: req.user
});
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({
code: 500,
message: '获取用户信息失败',
error: error.message
});
}
};
// 修改密码
exports.changePassword = async (req, res) => {
try {
const { oldPassword, newPassword } = req.body;
const userId = req.user.id;
// 参数验证
if (!oldPassword || !newPassword) {
return res.status(400).json({
code: 400,
message: '原密码和新密码不能为空'
});
}
if (newPassword.length < 6) {
return res.status(400).json({
code: 400,
message: '新密码长度不能少于6位'
});
}
// 查找用户
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
code: 404,
message: '用户不存在'
});
}
// 验证原密码
const isPasswordValid = await bcrypt.compare(oldPassword, user.password);
if (!isPasswordValid) {
return res.status(400).json({
code: 400,
message: '原密码错误'
});
}
// 加密新密码
const hashedPassword = await bcrypt.hash(newPassword, 10);
// 更新密码
await user.update({ password: hashedPassword });
res.json({
code: 200,
message: '密码修改成功'
});
} catch (error) {
console.error('修改密码错误:', error);
res.status(500).json({
code: 500,
message: '修改密码失败',
error: error.message
});
}
};