2025-09-04 09:04:58 +08:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const router = express.Router();
|
|
|
|
|
|
const Joi = require('joi');
|
|
|
|
|
|
const User = require('../models/User');
|
|
|
|
|
|
const { generateToken, refreshToken, authenticateToken } = require('../middleware/auth');
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
// 验证schema
|
2025-09-02 21:59:27 +08:00
|
|
|
|
const loginSchema = Joi.object({
|
|
|
|
|
|
username: Joi.string().min(2).max(50).required(),
|
|
|
|
|
|
password: Joi.string().min(6).max(100).required()
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const passwordResetRequestSchema = Joi.object({
|
|
|
|
|
|
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).required()
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const passwordResetConfirmSchema = Joi.object({
|
|
|
|
|
|
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).required(),
|
|
|
|
|
|
resetCode: Joi.string().required(),
|
|
|
|
|
|
newPassword: Joi.string().min(6).max(100).required()
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const changePasswordSchema = Joi.object({
|
|
|
|
|
|
oldPassword: Joi.string().required(),
|
|
|
|
|
|
newPassword: Joi.string().min(6).max(100).required()
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户登录
|
|
|
|
|
|
router.post('/login', async (req, res) => {
|
|
|
|
|
|
try {
|
2025-09-04 09:04:58 +08:00
|
|
|
|
const { error, value } = loginSchema.validate(req.body);
|
2025-09-02 21:59:27 +08:00
|
|
|
|
if (error) {
|
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '参数验证失败',
|
2025-09-04 09:04:58 +08:00
|
|
|
|
errors: error.details.map(detail => detail.message)
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
const { username, password } = value;
|
|
|
|
|
|
|
2025-09-02 21:59:27 +08:00
|
|
|
|
// 查找用户
|
2025-09-04 09:04:58 +08:00
|
|
|
|
const user = await User.findByLoginIdentifier(username);
|
|
|
|
|
|
|
2025-09-02 21:59:27 +08:00
|
|
|
|
if (!user) {
|
|
|
|
|
|
return res.status(401).json({
|
|
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '用户名或密码错误',
|
|
|
|
|
|
code: 'INVALID_CREDENTIALS'
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证密码
|
2025-09-04 09:04:58 +08:00
|
|
|
|
const isValidPassword = await user.validatePassword(password);
|
|
|
|
|
|
if (!isValidPassword) {
|
2025-09-02 21:59:27 +08:00
|
|
|
|
return res.status(401).json({
|
|
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '用户名或密码错误',
|
|
|
|
|
|
code: 'INVALID_CREDENTIALS'
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (user.status !== 'active') {
|
2025-09-04 09:04:58 +08:00
|
|
|
|
return res.status(401).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '账号已被禁用,请联系管理员',
|
|
|
|
|
|
code: 'ACCOUNT_DISABLED'
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
// 更新登录信息
|
|
|
|
|
|
await user.updateLoginInfo();
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
// 生成JWT token
|
|
|
|
|
|
const tokens = generateToken(user);
|
|
|
|
|
|
|
2025-09-02 21:59:27 +08:00
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '登录成功',
|
|
|
|
|
|
data: {
|
2025-09-04 09:04:58 +08:00
|
|
|
|
...tokens,
|
2025-09-02 21:59:27 +08:00
|
|
|
|
user: {
|
|
|
|
|
|
id: user.id,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
uuid: user.uuid,
|
2025-09-02 21:59:27 +08:00
|
|
|
|
username: user.username,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
phone: user.phone,
|
2025-09-02 21:59:27 +08:00
|
|
|
|
email: user.email,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
real_name: user.real_name,
|
|
|
|
|
|
avatar_url: user.avatar_url,
|
|
|
|
|
|
user_type: user.user_type,
|
|
|
|
|
|
status: user.status,
|
|
|
|
|
|
last_login_at: user.last_login_at,
|
|
|
|
|
|
login_count: user.login_count
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
} catch (error) {
|
2025-09-04 09:04:58 +08:00
|
|
|
|
console.error('登录错误:', error);
|
2025-09-02 21:59:27 +08:00
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '登录失败',
|
|
|
|
|
|
error: error.message
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
// 获取用户信息
|
|
|
|
|
|
router.get('/me', authenticateToken, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// req.user 已经通过中间件注入
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
id: req.user.id,
|
|
|
|
|
|
uuid: req.user.uuid,
|
|
|
|
|
|
username: req.user.username,
|
|
|
|
|
|
phone: req.user.phone,
|
|
|
|
|
|
email: req.user.email,
|
|
|
|
|
|
real_name: req.user.real_name,
|
|
|
|
|
|
avatar_url: req.user.avatar_url,
|
|
|
|
|
|
user_type: req.user.user_type,
|
|
|
|
|
|
status: req.user.status,
|
|
|
|
|
|
last_login_at: req.user.last_login_at,
|
|
|
|
|
|
login_count: req.user.login_count,
|
|
|
|
|
|
created_at: req.user.created_at,
|
|
|
|
|
|
updated_at: req.user.updated_at
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取用户信息错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '获取用户信息失败',
|
|
|
|
|
|
error: error.message
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户登出
|
2025-09-04 09:04:58 +08:00
|
|
|
|
router.post('/logout', authenticateToken, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// TODO: 实际项目中可以将token加入黑名单或Redis
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '退出登录成功'
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('退出登录错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-04 09:04:58 +08:00
|
|
|
|
message: '退出登录失败',
|
|
|
|
|
|
error: error.message
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
// 刷新token
|
|
|
|
|
|
router.post('/refresh', refreshToken);
|
|
|
|
|
|
|
|
|
|
|
|
// 验证token有效性
|
|
|
|
|
|
router.post('/verify', authenticateToken, (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 如果通过认证中间件,说明token有效
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: 'Token有效',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
valid: true,
|
|
|
|
|
|
user: {
|
|
|
|
|
|
id: req.user.id,
|
|
|
|
|
|
username: req.user.username,
|
|
|
|
|
|
user_type: req.user.user_type
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Token验证错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: 'Token验证失败',
|
|
|
|
|
|
error: error.message
|
|
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
});
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
module.exports = router;
|