Files
2025-09-16 17:39:51 +08:00

345 lines
12 KiB
JavaScript
Raw Permalink 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 {getDB} = require('../database');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
router.post('/register', async (req, res) => {
try {
const db = getDB();
await db.query('START TRANSACTION');
const {
username,
phone,
password,
city,
district_id: district,
province,
inviter = null,
captchaId,
captchaText,
smsCode, // 短信验证码
role = 'user'
} = req.body;
if (!username || !phone || !password || !city || !district || !province) {
return res.status(400).json({success: false, message: '用户名、手机号、密码、城市和区域不能为空'});
}
if (!captchaId || !captchaText) {
return res.status(400).json({success: false, message: '图形验证码不能为空'});
}
const storedCaptcha = global.captchaStore.get(captchaId);
console.log(storedCaptcha);
if (!storedCaptcha) {
return res.status(400).json({
success: false,
message: '验证码不存在或已过期'
});
}
// 检查是否过期
if (Date.now() > storedCaptcha.expires) {
global.captchaStore.delete(captchaId);
return res.status(400).json({
success: false,
message: '验证码已过期'
});
}
// 验证验证码(不区分大小写)
const isValid = storedCaptcha.text === captchaText.toLowerCase();
// 删除已验证的验证码
global.captchaStore.delete(captchaId);
if (!isValid) {
return res.status(400).json({
success: false,
message: '验证码错误'
});
}
if (!smsCode) {
return res.status(400).json({success: false, message: '短信验证码不能为空'});
}
// 验证短信验证码
const smsAPI = require('./sms');
const smsValid = smsAPI.verifySMSCode(phone, smsCode);
if (!smsValid) {
return res.status(400).json({success: false, message: '短信验证码错误或已过期'});
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
return res.status(400).json({success: false, message: '手机号格式不正确'});
}
// 检查用户是否已存在
const [existingUsers] = await db.execute(
'SELECT id, payment_status FROM users WHERE username = ? OR phone = ?',
[username, phone]
);
if (existingUsers.length > 0) {
return res.status(400).json({success: false, message: '用户名或手机号已存在'});
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建用户(初始状态为未支付)
const [result] = await db.execute(
'INSERT INTO users (username, phone, password, role, points, audit_status, city, district_id, payment_status, province, inviter) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "unpaid", ?, ?)',
[username, phone, hashedPassword, role, 0, 'pending', city, district, province, inviter]
);
const userId = result.insertId;
await db.query('COMMIT');
// 生成JWT token用于支付流程
const token = jwt.sign(
{userId: userId, username, role},
JWT_SECRET,
{expiresIn: '24h'}
);
res.status(201).json({
success: true,
message: '用户信息创建成功,请完成支付以激活账户',
token,
user: {
id: userId,
username,
phone,
role,
points: 0,
audit_status: 'pending',
city,
district,
paymentStatus: 'unpaid'
},
needPayment: true
});
} catch (error) {
try {
// await getDB().query('ROLLBACK');
} catch (rollbackError) {
console.error('回滚错误:', rollbackError);
}
console.error('注册错误详情:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json({
success: false,
message: '注册失败',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
});
router.post('/login', async (req, res) => {
try {
const db = getDB();
const {username, password, captchaId, captchaText} = req.body;
if (!username || !password) {
return res.status(400).json({success: false, message: '用户名和密码不能为空'});
}
if (!captchaId || !captchaText) {
return res.status(400).json({success: false, message: '验证码不能为空'});
}
// 获取存储的验证码
const storedCaptcha = global.captchaStore.get(captchaId);
console.log(storedCaptcha);
if (!storedCaptcha) {
return res.status(400).json({
success: false,
message: '验证码不存在或已过期'
});
}
// 检查是否过期
if (Date.now() > storedCaptcha.expires) {
global.captchaStore.delete(captchaId);
return res.status(400).json({
success: false,
message: '验证码已过期'
});
}
// 验证验证码(不区分大小写)
const isValid = storedCaptcha.text === captchaText.toLowerCase();
// 删除已验证的验证码
global.captchaStore.delete(captchaId);
if (!isValid) {
return res.status(400).json({
success: false,
message: '验证码错误'
});
}
// 注意:验证码已在前端通过 /captcha/verify 接口验证过,这里不再重复验证
// 查找用户(包含支付状态)
console.log('登录尝试 - 用户名:', username);
const [users] = await db.execute(
'SELECT * FROM users WHERE username = ?',
[username]
);
console.log('查找到的用户数量:', users.length);
if (users.length === 0) {
console.log('用户不存在:', username);
return res.status(401).json({success: false, message: '用户名或密码错误'});
}
const user = users[0];
console.log('找到用户:', user.username, '密码长度:', user.password ? user.password.length : 'null');
// 验证密码
console.log('验证密码 - 输入密码:', password, '数据库密码前10位:', user.password ? user.password.substring(0, 10) : 'null');
const isValidPassword = await bcrypt.compare(password, user.password);
console.log('密码验证结果:', isValidPassword);
if (!isValidPassword) {
console.log('密码验证失败');
return res.status(401).json({success: false, message: '用户名或密码错误'});
}
// 检查支付状态(管理员除外)
if (user.role !== 'admin' && user.payment_status === 'unpaid') {
const token = jwt.sign(
{userId: user.id, username: user.username, role: user.role},
JWT_SECRET,
{expiresIn: '5m'}
);
return res.status(200).json({
success: false,
message: '您的账户尚未激活,请完成支付后再登录',
needPayment: true,
user: user[0],
token
});
}
// 检查用户审核状态(管理员除外,只阻止被拒绝的用户)
if (user.role !== 'admin' && user.audit_status === 'rejected') {
return res.status(403).json({success: false, message: '您的账户审核未通过,请联系管理员'});
}
// 待审核用户可以正常登录使用系统,但匹配功能会有限制
// 生成JWT token
const token = jwt.sign(
{userId: user.id, username: user.username, role: user.role},
JWT_SECRET,
{expiresIn: '24h'}
);
const [is_distribution] = await db.execute(`
SELECT *
FROM distribution
WHERE user_id = ?`, [user.id]);
user.distribution = is_distribution.length > 0 ? true : false;
res.json({
success: true,
message: '登录成功',
token,
user
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({success: false, message: '登录失败'});
}
});
// 验证token中间件
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({success: false, message: '访问令牌缺失'});
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({success: false, message: '访问令牌无效'});
}
req.user = user;
next();
});
};
// 获取当前用户信息
router.get('/me', authenticateToken, async (req, res) => {
try {
const db = getDB();
const [users] = await db.execute(
'SELECT id, username, role, avatar, points, created_at FROM users WHERE id = ?',
[req.user.userId]
);
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
res.json({success: true, user: users[0]});
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({success: false, message: '获取用户信息失败'});
}
});
// 修改密码
router.put('/change-password', authenticateToken, async (req, res) => {
try {
const db = getDB();
const {currentPassword, newPassword} = req.body;
if (!currentPassword || !newPassword) {
return res.status(400).json({success: false, message: '旧密码和新密码不能为空'});
}
// 获取用户当前密码
const [users] = await db.execute(
'SELECT password FROM users WHERE id = ?',
[req.user.userId]
);
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
// 验证旧密码
const isValidPassword = await bcrypt.compare(currentPassword, users[0].password);
if (!isValidPassword) {
return res.status(400).json({success: false, message: '旧密码错误'});
}
// 加密新密码
const hashedNewPassword = await bcrypt.hash(newPassword, 10);
// 更新密码
await db.execute(
'UPDATE users SET password = ? WHERE id = ?',
[hashedNewPassword, req.user.userId]
);
res.json({success: true, message: '密码修改成功'});
} catch (error) {
console.error('修改密码错误:', error);
res.status(500).json({success: false, message: '修改密码失败'});
}
});
module.exports = router;
module.exports.authenticateToken = authenticateToken;