Files
nxxmdata/bank-backend/controllers/userController.js

723 lines
16 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.

/**
* 用户控制器
* @file userController.js
* @description 处理用户相关的请求
*/
const { User, Role, Account } = require('../models');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { validationResult } = require('express-validator');
const { Op } = require('sequelize');
/**
* 用户注册
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.register = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { username, email, password, phone, real_name, id_card } = req.body;
// 检查用户名是否已存在
const existingUser = await User.findOne({
where: { username }
});
if (existingUser) {
return res.status(400).json({
success: false,
message: '用户名已存在'
});
}
// 检查邮箱是否已存在
const existingEmail = await User.findOne({
where: { email }
});
if (existingEmail) {
return res.status(400).json({
success: false,
message: '邮箱已被注册'
});
}
// 检查身份证是否已存在
const existingIdCard = await User.findOne({
where: { id_card }
});
if (existingIdCard) {
return res.status(400).json({
success: false,
message: '身份证号已被注册'
});
}
// 创建新用户
const user = await User.create({
username,
email,
password,
phone,
real_name,
id_card,
role_id: 2 // 默认为普通用户
});
// 生成JWT令牌
const token = jwt.sign(
{ id: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
);
res.status(201).json({
success: true,
message: '注册成功',
data: {
user: user.getSafeInfo(),
token
}
});
} catch (error) {
console.error('用户注册错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 用户登录
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.login = async (req, res) => {
try {
const { username, password } = req.body;
// 查找用户
const user = await User.findOne({
where: { username },
include: [{
model: Role,
as: 'role'
}]
});
if (!user) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
// 检查账户状态
if (user.status !== 'active') {
return res.status(401).json({
success: false,
message: '账户已被禁用'
});
}
// 检查账户是否被锁定
if (user.locked_until && user.locked_until > new Date()) {
return res.status(401).json({
success: false,
message: '账户已被锁定,请稍后再试'
});
}
// 验证密码
const isValidPassword = await user.validPassword(password);
if (!isValidPassword) {
// 增加登录失败次数
user.login_attempts += 1;
// 如果失败次数达到5次锁定账户1小时
if (user.login_attempts >= 5) {
user.locked_until = new Date(Date.now() + 60 * 60 * 1000); // 1小时后解锁
}
await user.save();
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
// 登录成功,重置登录失败次数
user.login_attempts = 0;
user.locked_until = null;
user.last_login = new Date();
await user.save();
// 生成JWT令牌
const token = jwt.sign(
{ id: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
);
res.json({
success: true,
message: '登录成功',
data: {
user: user.getSafeInfo(),
token
}
});
} catch (error) {
console.error('用户登录错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 获取用户信息
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getProfile = async (req, res) => {
try {
const user = await User.findByPk(req.user.id, {
include: [{
model: Role,
as: 'role'
}]
});
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
data: user.getSafeInfo()
});
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 更新用户信息
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.updateProfile = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { phone, real_name, avatar } = req.body;
const user = await User.findByPk(req.user.id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 更新用户信息
await user.update({
phone: phone || user.phone,
real_name: real_name || user.real_name,
avatar: avatar || user.avatar
});
res.json({
success: true,
message: '用户信息更新成功',
data: user.getSafeInfo()
});
} catch (error) {
console.error('更新用户信息错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 修改密码
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.changePassword = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { old_password, new_password } = req.body;
const user = await User.findByPk(req.user.id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 验证旧密码
const isValidPassword = await user.validPassword(old_password);
if (!isValidPassword) {
return res.status(400).json({
success: false,
message: '原密码错误'
});
}
// 更新密码
user.password = new_password;
await user.save();
res.json({
success: true,
message: '密码修改成功'
});
} catch (error) {
console.error('修改密码错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 获取用户列表(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getUsers = async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const search = req.query.search || '';
const whereClause = {};
if (search) {
whereClause[Op.or] = [
{ username: { [Op.like]: `%${search}%` } },
{ email: { [Op.like]: `%${search}%` } },
{ real_name: { [Op.like]: `%${search}%` } }
];
}
const { count, rows } = await User.findAndCountAll({
where: whereClause,
include: [{
model: Role,
as: 'role'
}],
limit,
offset,
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: {
users: rows.map(user => user.getSafeInfo()),
pagination: {
page,
limit,
total: count,
pages: Math.ceil(count / limit)
}
}
});
} catch (error) {
console.error('获取用户列表错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 更新用户状态(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.updateUserStatus = async (req, res) => {
try {
const { userId } = req.params;
const { status } = req.body;
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
await user.update({ status });
res.json({
success: true,
message: '用户状态更新成功',
data: user.getSafeInfo()
});
} catch (error) {
console.error('更新用户状态错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 获取用户账户列表
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getUserAccounts = async (req, res) => {
try {
const userId = req.params.userId || req.user.id;
// 检查权限:用户只能查看自己的账户,管理员可以查看所有账户
if (userId !== req.user.id.toString() && req.user.role.name !== 'admin') {
return res.status(403).json({
success: false,
message: '无权访问该用户的账户信息'
});
}
const accounts = await Account.findAll({
where: { user_id: userId },
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: accounts.map(account => ({
...account.getSafeInfo(),
balance_formatted: account.getBalanceFormatted(),
available_balance_formatted: account.getAvailableBalanceFormatted(),
frozen_amount_formatted: account.getFrozenAmountFormatted()
}))
});
} catch (error) {
console.error('获取用户账户列表错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 创建用户(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.createUser = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { username, email, password, phone, real_name, id_card, role_id } = req.body;
// 检查用户名是否已存在
const existingUser = await User.findOne({
where: { username }
});
if (existingUser) {
return res.status(400).json({
success: false,
message: '用户名已存在'
});
}
// 检查邮箱是否已存在
const existingEmail = await User.findOne({
where: { email }
});
if (existingEmail) {
return res.status(400).json({
success: false,
message: '邮箱已被注册'
});
}
// 创建新用户
const user = await User.create({
username,
email,
password,
phone,
real_name,
id_card,
role_id: role_id || 2 // 默认为普通用户
});
// 获取用户信息(包含角色)
const userWithRole = await User.findByPk(user.id, {
include: [{
model: Role,
as: 'role'
}]
});
res.status(201).json({
success: true,
message: '用户创建成功',
data: userWithRole.getSafeInfo()
});
} catch (error) {
console.error('创建用户错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 更新用户信息(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.updateUser = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { userId } = req.params;
const { username, email, phone, real_name, id_card, role_id, status } = req.body;
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 检查用户名是否被其他用户使用
if (username && username !== user.username) {
const existingUser = await User.findOne({
where: { username, id: { [Op.ne]: userId } }
});
if (existingUser) {
return res.status(400).json({
success: false,
message: '用户名已被其他用户使用'
});
}
}
// 检查邮箱是否被其他用户使用
if (email && email !== user.email) {
const existingEmail = await User.findOne({
where: { email, id: { [Op.ne]: userId } }
});
if (existingEmail) {
return res.status(400).json({
success: false,
message: '邮箱已被其他用户使用'
});
}
}
// 更新用户信息
await user.update({
username: username || user.username,
email: email || user.email,
phone: phone || user.phone,
real_name: real_name || user.real_name,
id_card: id_card || user.id_card,
role_id: role_id || user.role_id,
status: status || user.status
});
// 获取更新后的用户信息(包含角色)
const updatedUser = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role'
}]
});
res.json({
success: true,
message: '用户信息更新成功',
data: updatedUser.getSafeInfo()
});
} catch (error) {
console.error('更新用户信息错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 删除用户(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.deleteUser = async (req, res) => {
try {
const { userId } = req.params;
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 检查是否是当前用户
if (userId === req.user.userId.toString()) {
return res.status(400).json({
success: false,
message: '不能删除自己的账户'
});
}
// 软删除用户更新状态为inactive
await user.update({ status: 'inactive' });
res.json({
success: true,
message: '用户删除成功'
});
} catch (error) {
console.error('删除用户错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 重置用户密码(管理员)
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.resetPassword = async (req, res) => {
try {
const { userId } = req.params;
const { newPassword } = req.body;
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 更新密码
user.password = newPassword;
await user.save();
res.json({
success: true,
message: '密码重置成功'
});
} catch (error) {
console.error('重置密码错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 获取用户详情
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getUserById = async (req, res) => {
try {
const { userId } = req.params;
const user = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role'
}]
});
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
message: '获取用户详情成功',
data: user.getSafeInfo()
});
} catch (error) {
console.error('获取用户详情错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};