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;