Files
aijianhua/backend/routes/auth.js

399 lines
11 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 express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const validator = require('validator');
const dbConnector = require('../utils/dbConnector');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
/**
* @swagger
* /api/v1/auth/register:
* post:
* summary: 用户注册
* description: 创建一个新的用户账户
* tags:
* - 认证管理
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - username
* - password
* - phone
* properties:
* username:
* type: string
* example: "user123"
* description: 用户名
* password:
* type: string
* example: "password123"
* description: 密码至少6位
* phone:
* type: string
* example: "13800138000"
* description: 手机号
* email:
* type: string
* example: "user@example.com"
* description: 邮箱地址
* user_type:
* type: string
* example: "farmer"
* description: 用户类型
* responses:
* 201:
* description: 注册成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 201
* message:
* type: string
* example: 注册成功
* data:
* type: object
* properties:
* user_id:
* type: integer
* example: 1
* username:
* type: string
* example: "user123"
* phone:
* type: string
* example: "13800138000"
* email:
* type: string
* example: "user@example.com"
* user_type:
* type: string
* example: "farmer"
* token:
* type: string
* example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
* 400:
* description: 请求参数错误
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 400
* message:
* type: string
* example: 用户名、密码和手机号为必填项
* 409:
* description: 用户已存在
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 409
* message:
* type: string
* example: 用户名、手机号或邮箱已存在
*
* 用户注册
*/
router.post('/register', async (req, res, next) => {
try {
const { username, password, phone, email, user_type = 'farmer' } = req.body;
// 参数验证
if (!username || !password || !phone) {
return res.status(400).json({
code: 400,
message: '用户名、密码和手机号为必填项',
data: null
});
}
if (password.length < 6) {
return res.status(400).json({
code: 400,
message: '密码长度不能少于6位',
data: null
});
}
if (!validator.isMobilePhone(phone, 'zh-CN')) {
return res.status(400).json({
code: 400,
message: '手机号格式不正确',
data: null
});
}
if (email && !validator.isEmail(email)) {
return res.status(400).json({
code: 400,
message: '邮箱格式不正确',
data: null
});
}
// 检查用户是否已存在
const existingUser = await dbConnector.query(
'SELECT id FROM users WHERE username = ? OR phone = ? OR email = ?',
[username, phone, email]
);
if (existingUser.length > 0) {
return res.status(409).json({
code: 409,
message: '用户名、手机号或邮箱已存在',
data: null
});
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 12);
// 创建用户
const result = await dbConnector.query(
'INSERT INTO users (username, password, phone, email, user_type) VALUES (?, ?, ?, ?, ?)',
[username, hashedPassword, phone, email, user_type]
);
// 生成JWT token
const token = jwt.sign(
{ userId: result.insertId, username, user_type },
JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
code: 201,
message: '注册成功',
data: {
user_id: result.insertId,
username,
phone,
email,
user_type,
token
}
});
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/v1/auth/login:
* post:
* summary: 用户登录
* description: 使用用户名和密码进行身份验证并获取访问令牌
* tags:
* - 认证管理
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - username
* - password
* properties:
* username:
* type: string
* example: "user123"
* description: 用户名
* password:
* type: string
* example: "password123"
* description: 密码
* responses:
* 200:
* description: 登录成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* message:
* type: string
* example: 登录成功
* data:
* type: object
* properties:
* user_id:
* type: integer
* example: 1
* username:
* type: string
* example: "user123"
* user_type:
* type: string
* example: "farmer"
* token:
* type: string
* example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
* 400:
* description: 请求参数错误
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 400
* message:
* type: string
* example: 用户名和密码为必填项
* 401:
* description: 用户名或密码错误
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 401
* message:
* type: string
* example: 用户名或密码错误
*
* 用户登录
*/
router.post('/login', async (req, res, next) => {
try {
const { login, password } = req.body;
if (!login || !password) {
return res.status(400).json({
code: 400,
message: '登录账号和密码为必填项',
data: null
});
}
// 查询用户(支持用户名、手机号、邮箱登录)
const user = await dbConnector.query(
'SELECT * FROM users WHERE (username = ? OR phone = ? OR email = ?) AND status = 1',
[login, login, login]
);
if (user.length === 0) {
return res.status(401).json({
code: 401,
message: '用户不存在或已被禁用',
data: null
});
}
const userData = user[0];
// 验证密码
const isValidPassword = await bcrypt.compare(password, userData.password_hash);
if (!isValidPassword) {
return res.status(401).json({
code: 401,
message: '密码不正确',
data: null
});
}
// 更新最后登录时间
await dbConnector.query(
'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?',
[userData.id]
);
// 生成JWT token
const token = jwt.sign(
{ userId: userData.id, username: userData.username, user_type: userData.user_type },
JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
code: 200,
message: '登录成功',
data: {
user_id: userData.id,
username: userData.username,
phone: userData.phone,
email: userData.email,
user_type: userData.user_type,
avatar_url: userData.avatar_url,
token
}
});
} catch (error) {
next(error);
}
});
/**
* 获取当前用户信息
*/
router.get('/me', async (req, res, next) => {
try {
// 从token中获取用户ID
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
code: 401,
message: '未提供认证token',
data: null
});
}
const decoded = jwt.verify(token, JWT_SECRET);
const user = await dbConnector.query(
'SELECT id, username, phone, email, user_type, avatar_url, created_at, last_login FROM users WHERE id = ? AND status = 1',
[decoded.userId]
);
if (user.length === 0) {
return res.status(404).json({
code: 404,
message: '用户不存在',
data: null
});
}
res.json({
code: 200,
message: '获取成功',
data: user[0]
});
} catch (error) {
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({
code: 401,
message: '无效的token',
data: null
});
}
next(error);
}
});
module.exports = router;