2025-09-05 01:18:40 +08:00
|
|
|
|
const express = require('express')
|
|
|
|
|
|
const bcrypt = require('bcryptjs')
|
|
|
|
|
|
const jwt = require('jsonwebtoken')
|
|
|
|
|
|
const Joi = require('joi')
|
|
|
|
|
|
const router = express.Router()
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
// 引入数据库模型
|
|
|
|
|
|
const { ApiUser } = require('../models')
|
|
|
|
|
|
|
|
|
|
|
|
// 登录参数验证
|
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-05 01:18:40 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 生成JWT token
|
|
|
|
|
|
const generateToken = (user) => {
|
|
|
|
|
|
return jwt.sign(
|
|
|
|
|
|
{
|
|
|
|
|
|
id: user.id,
|
|
|
|
|
|
username: user.username,
|
|
|
|
|
|
role: user.user_type
|
|
|
|
|
|
},
|
|
|
|
|
|
process.env.JWT_SECRET || 'niumall-secret-key',
|
|
|
|
|
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户登录
|
|
|
|
|
|
router.post('/login', async (req, res) => {
|
|
|
|
|
|
try {
|
2025-09-05 01:18:40 +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-05 01:18:40 +08:00
|
|
|
|
details: error.details[0].message
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
const { username, password } = value
|
2025-09-04 09:04:58 +08:00
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
// 查找用户
|
|
|
|
|
|
const user = await ApiUser.findOne({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
[require('sequelize').Op.or]: [
|
|
|
|
|
|
{ username: username },
|
|
|
|
|
|
{ phone: username },
|
|
|
|
|
|
{ email: username }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-02 21:59:27 +08:00
|
|
|
|
if (!user) {
|
|
|
|
|
|
return res.status(401).json({
|
|
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '用户名或密码错误'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证密码
|
2025-09-05 01:18:40 +08:00
|
|
|
|
const isPasswordValid = await bcrypt.compare(password, user.password_hash)
|
|
|
|
|
|
if (!isPasswordValid) {
|
2025-09-02 21:59:27 +08:00
|
|
|
|
return res.status(401).json({
|
|
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '用户名或密码错误'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
// 检查用户状态
|
2025-09-02 21:59:27 +08:00
|
|
|
|
if (user.status !== 'active') {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
return res.status(403).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '账户已被禁用,请联系管理员'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
// 生成token
|
|
|
|
|
|
const token = generateToken(user)
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '登录成功',
|
|
|
|
|
|
data: {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
access_token: token,
|
|
|
|
|
|
token_type: 'Bearer',
|
|
|
|
|
|
expires_in: 86400, // 24小时
|
2025-09-02 21:59:27 +08:00
|
|
|
|
user: {
|
|
|
|
|
|
id: user.id,
|
|
|
|
|
|
username: user.username,
|
|
|
|
|
|
email: user.email,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
role: user.user_type,
|
|
|
|
|
|
status: user.status
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-05 01:18:40 +08:00
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
} catch (error) {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
console.error('登录失败:', error)
|
2025-09-02 21:59:27 +08:00
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '登录失败,请稍后重试'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-05 01:18:40 +08:00
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
// 获取当前用户信息
|
2025-09-04 09:04:58 +08:00
|
|
|
|
router.get('/me', authenticateToken, async (req, res) => {
|
|
|
|
|
|
try {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
const user = await ApiUser.findByPk(req.user.id)
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '用户不存在'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 09:04:58 +08:00
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
user: {
|
|
|
|
|
|
id: user.id,
|
|
|
|
|
|
username: user.username,
|
|
|
|
|
|
email: user.email,
|
|
|
|
|
|
role: user.user_type,
|
|
|
|
|
|
status: user.status
|
|
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
}
|
2025-09-05 01:18:40 +08:00
|
|
|
|
})
|
2025-09-04 09:04:58 +08:00
|
|
|
|
} catch (error) {
|
2025-09-05 01:18:40 +08:00
|
|
|
|
console.error('获取用户信息失败:', error)
|
2025-09-04 09:04:58 +08:00
|
|
|
|
res.status(500).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '获取用户信息失败'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-05 01:18:40 +08:00
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户登出
|
2025-09-05 01:18:40 +08:00
|
|
|
|
router.post('/logout', authenticateToken, (req, res) => {
|
|
|
|
|
|
// 在实际项目中,可以将token加入黑名单
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '登出成功'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// JWT token验证中间件
|
|
|
|
|
|
function authenticateToken(req, res, next) {
|
|
|
|
|
|
const authHeader = req.headers['authorization']
|
|
|
|
|
|
const token = authHeader && authHeader.split(' ')[1]
|
|
|
|
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
return res.status(401).json({
|
2025-09-02 21:59:27 +08:00
|
|
|
|
success: false,
|
2025-09-05 01:18:40 +08:00
|
|
|
|
message: '访问令牌缺失'
|
|
|
|
|
|
})
|
2025-09-02 21:59:27 +08:00
|
|
|
|
}
|
2025-09-04 09:04:58 +08:00
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
jwt.verify(token, process.env.JWT_SECRET || 'niumall-secret-key', (err, user) => {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
return res.status(403).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '访问令牌无效或已过期'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
req.user = user
|
|
|
|
|
|
next()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-02 21:59:27 +08:00
|
|
|
|
|
2025-09-05 01:18:40 +08:00
|
|
|
|
module.exports = router
|