更新后端API端口和认证系统,优化数据库配置和环境变量管理

This commit is contained in:
2025-09-18 16:47:08 +08:00
parent 31bc064018
commit 4e852bd3cb
10 changed files with 534 additions and 15 deletions

View File

@@ -10,7 +10,7 @@ JWT_SECRET=niumall_jwt_secret_key_2024
JWT_EXPIRES_IN=24h
# 应用配置
NODE_ENV=production
NODE_ENV=development
PORT=4330
API_PREFIX=/api

92
backend/add_demo_users.js Normal file
View File

@@ -0,0 +1,92 @@
// 添加演示账号到数据库的脚本
const { sequelize, ApiUser } = require('./models');
const bcrypt = require('bcryptjs');
// 演示账号数据
const demoUsers = [
{
username: 'admin',
password: 'admin123',
phone: '13800138001',
email: 'admin@niumall.com',
user_type: 'admin',
status: 'active'
},
{
username: 'buyer',
password: 'buyer123',
phone: '13800138002',
email: 'buyer@niumall.com',
user_type: 'client',
status: 'active'
},
{
username: 'trader',
password: 'trader123',
phone: '13800138003',
email: 'trader@niumall.com',
user_type: 'supplier',
status: 'active'
}
];
// 添加或更新演示账号
const setupDemoUsers = async () => {
try {
// 测试数据库连接
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 为每个演示账号创建或更新记录
for (const userData of demoUsers) {
// 尝试通过用户名查找用户
let user = await ApiUser.findOne({
where: { username: userData.username }
});
// 加密密码
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(userData.password, salt);
try {
if (user) {
// 用户存在,更新信息,但保留现有手机号(避免唯一约束冲突)
await user.update({
password_hash: passwordHash,
email: userData.email,
user_type: userData.user_type,
status: userData.status
// 不更新phone字段避免唯一约束冲突
});
console.log(`✅ 成功更新用户: ${userData.username} (${userData.user_type})`);
} else {
// 用户不存在,创建新用户
await ApiUser.create({
...userData,
password_hash: passwordHash
});
console.log(`✅ 成功创建用户: ${userData.username} (${userData.user_type})`);
}
} catch (error) {
// 处理可能的唯一约束冲突或其他错误
if (error.name === 'SequelizeUniqueConstraintError') {
console.log(`⚠️ 无法创建用户 ${userData.username}: 用户名或手机号已被使用`);
} else {
console.error(`❌ 处理用户 ${userData.username} 时出错:`, error.message);
}
}
}
console.log('✅ 所有演示账号设置完成');
} catch (error) {
console.error('❌ 设置演示账号失败:', error.message);
// 输出完整错误信息便于调试
console.error(error);
} finally {
// 关闭数据库连接
await sequelize.close();
}
};
// 执行脚本
setupDemoUsers();

262
backend/docs/openapi.json Normal file
View File

@@ -0,0 +1,262 @@
{
"openapi": "3.0.0",
"info": {
"title": "NiuMall 管理系统 API",
"description": "NiuMall 管理系统后端API文档",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:4330/api",
"description": "本地开发环境"
}
],
"paths": {
"/auth/login": {
"post": {
"summary": "用户登录",
"description": "管理员用户登录认证接口,支持通过用户名或邮箱登录",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["username", "password"],
"properties": {
"username": {
"type": "string",
"description": "用户名或邮箱"
},
"password": {
"type": "string",
"description": "用户密码"
}
},
"example": {
"username": "admin",
"password": "123456"
}
}
}
}
},
"responses": {
"200": {
"description": "登录成功",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"message": {
"type": "string",
"example": "登录成功"
},
"access_token": {
"type": "string",
"description": "JWT访问令牌"
},
"token_type": {
"type": "string",
"example": "Bearer"
},
"expires_in": {
"type": "integer",
"description": "令牌过期时间(分钟)",
"example": 1440
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"username": {
"type": "string",
"example": "admin"
},
"email": {
"type": "string",
"example": "admin@niumall.com"
},
"phone": {
"type": "string",
"example": "13800138000"
},
"role": {
"type": "string",
"example": "admin"
},
"status": {
"type": "string",
"example": "active"
}
}
}
}
}
}
}
},
"400": {
"description": "参数错误",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"message": {
"type": "string",
"example": "用户名和密码不能为空"
}
}
}
}
}
},
"401": {
"description": "认证失败",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"message": {
"type": "string",
"example": "用户名或密码错误"
}
}
}
}
}
},
"500": {
"description": "服务器错误",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"message": {
"type": "string",
"example": "登录失败,请稍后再试"
}
}
}
}
}
}
}
}
},
"/auth/current": {
"get": {
"summary": "获取当前用户信息",
"description": "获取当前登录用户的详细信息需要JWT认证",
"security": [
{
"bearerAuth": []
}
],
"responses": {
"200": {
"description": "获取成功",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"message": {
"type": "string",
"example": "获取用户信息成功"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"example": 1
},
"username": {
"type": "string",
"example": "admin"
},
"email": {
"type": "string",
"example": "admin@niumall.com"
},
"phone": {
"type": "string",
"example": "13800138000"
},
"user_type": {
"type": "string",
"example": "admin"
},
"status": {
"type": "string",
"example": "active"
}
}
}
}
}
}
}
},
"401": {
"description": "未认证",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"message": {
"type": "string",
"example": "未授权,请登录"
}
}
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}

View File

@@ -1,11 +1,11 @@
// 数据库配置
const dbConfig = {
development: {
username: 'root',
password: 'password',
database: 'niumall_dev',
host: '127.0.0.1',
port: 3306,
username: process.env.DB_USERNAME || 'root',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_NAME || 'niumall_dev',
host: process.env.DB_HOST || '127.0.0.1',
port: process.env.DB_PORT || 3306,
dialect: 'mysql'
},
production: {

View File

@@ -3,7 +3,98 @@ const bcrypt = require('bcryptjs');
const { v4: uuidv4 } = require('uuid');
const { successResponse, errorResponse } = require('../utils/response');
const { jwtConfig } = require('../config/config');
const User = require('../models/User');
const { ApiUser } = require('../../models');
const jsonwebtoken = require('jsonwebtoken');
const { Op } = require('sequelize');
// 传统用户登录 - 管理系统使用
const login = async (req, res) => {
try {
const { username, password } = req.body;
// 验证参数
if (!username || !password) {
return res.status(400).json(errorResponse('用户名和密码不能为空', 400));
}
// 查找用户 - 支持通过用户名或邮箱登录
let user = await ApiUser.findOne({
where: {
[Op.or]: [
{ username },
{ email: username }
]
}
});
// 检查用户是否存在
if (!user) {
return res.status(401).json(errorResponse('用户名或密码错误', 401));
}
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password_hash);
if (!isPasswordValid) {
return res.status(401).json(errorResponse('用户名或密码错误', 401));
}
// 检查用户状态
if (user.status !== 'active') {
return res.status(401).json(errorResponse('用户账号已被禁用', 401));
}
// 生成JWT token
const token = jsonwebtoken.sign(
{
id: user.id,
uuid: user.uuid,
username: user.username,
userType: user.user_type,
email: user.email
},
jwtConfig.secret,
{ expiresIn: jwtConfig.expiresIn }
);
// 准备返回的用户信息
const userInfo = {
id: user.id,
username: user.username,
email: user.email,
user_type: user.user_type,
status: user.status,
phone: user.phone
};
// 返回响应 - 包含data字段以匹配前端期望的格式
res.json({
success: true,
message: '登录成功',
data: {
access_token: token,
token_type: 'Bearer',
expires_in: parseInt(jwtConfig.expiresIn) * 60, // 转换为秒
user: {
id: user.id,
username: user.username,
email: user.email,
phone: user.phone,
avatar: user.avatar,
role: user.user_type,
status: user.status,
createdAt: user.created_at,
updatedAt: user.updated_at
}
}
});
} catch (error) {
console.error('用户登录失败:', error);
res.status(500).json({
success: false,
message: '登录失败,请稍后再试'
});
}
};
// 小程序用户登录
const miniProgramLogin = async (req, res) => {
@@ -30,7 +121,7 @@ const miniProgramLogin = async (req, res) => {
}
// 生成JWT token
const token = jwt.sign(
const token = jsonwebtoken.sign(
{
id: user.id,
uuid: user.uuid,
@@ -62,7 +153,7 @@ const miniProgramLogin = async (req, res) => {
// 获取当前用户信息
const getCurrentUser = async (req, res) => {
try {
const user = await User.findByPk(req.user.id);
const user = await ApiUser.findByPk(req.user.id);
if (!user) {
return res.status(404).json(errorResponse('用户不存在', 404));
@@ -86,6 +177,7 @@ const getCurrentUser = async (req, res) => {
};
module.exports = {
login,
miniProgramLogin,
getCurrentUser
};

View File

@@ -1,3 +1,6 @@
// 加载.env文件
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
@@ -5,6 +8,15 @@ const morgan = require('morgan');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
// 打印环境变量用于调试
console.log('Environment variables:');
console.log('DB_HOST:', process.env.DB_HOST);
console.log('DB_PORT:', process.env.DB_PORT);
console.log('DB_USERNAME:', process.env.DB_USERNAME);
console.log('DB_NAME:', process.env.DB_NAME);
console.log('NODE_ENV:', process.env.NODE_ENV);
console.log('PORT:', process.env.PORT);
// 数据库连接
const sequelize = require('./config/database');
@@ -57,10 +69,12 @@ app.use(errorHandler);
// 同步数据库模型
const syncDatabase = async () => {
try {
await sequelize.sync({ alter: true });
// 不修改现有表结构,只创建不存在的表
await sequelize.sync({ force: false });
console.log('数据库模型同步成功');
} catch (error) {
console.error('数据库模型同步失败:', error);
console.log('服务器仍然会继续运行,但某些功能可能受到影响');
}
};

View File

@@ -3,6 +3,9 @@ const router = express.Router();
const AuthController = require('../controllers/AuthController');
const { authenticate } = require('../middleware/auth');
// 传统用户登录 - 管理系统使用
router.post('/login', AuthController.login);
// 小程序用户登录
router.post('/mini-program/login', AuthController.miniProgramLogin);