rentease-backend/controllers/authController.js

297 lines
6.8 KiB
JavaScript
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.

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