
教育平台API设计与实现(Node.js + MySQL)
下面我将为您设计一个完整的教育平台API系统,包含用户认证、课程管理、文章管理等功能。
系统架构设计
技术栈
-
后端: Node.js + Express.js
-
数据库: MySQL
-
身份验证: JWT (JSON Web Tokens)
-
密码加密: bcryptjs
-
其他: cors, dotenv, nodemon
数据库设计
首先创建数据库表结构:
sql
CREATE DATABASE education_platform;
USE education_platform;
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('student', 'teacher', 'admin') DEFAULT 'student',
avatar_url VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 课程表
CREATE TABLE courses (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
instructor_id INT NOT NULL,
price DECIMAL(10, 2) DEFAULT 0,
thumbnail_url VARCHAR(255),
category VARCHAR(100),
level ENUM('beginner', 'intermediate', 'advanced') DEFAULT 'beginner',
duration INT, -- 课程时长(小时)
is_published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (instructor_id) REFERENCES users(id)
);
-- 文章表
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content LONGTEXT NOT NULL,
author_id INT NOT NULL,
course_id INT,
is_public BOOLEAN DEFAULT TRUE,
tags JSON,
view_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (author_id) REFERENCES users(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
-- 用户课程关联表(报名记录)
CREATE TABLE user_courses (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
course_id INT NOT NULL,
enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
progress INT DEFAULT 0, -- 学习进度百分比
completed BOOLEAN DEFAULT FALSE,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (course_id) REFERENCES courses(id),
UNIQUE KEY unique_enrollment (user_id, course_id)
);
-- API表
CREATE TABLE apis (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
endpoint VARCHAR(255) NOT NULL,
method ENUM('GET', 'POST', 'PUT', 'DELETE', 'PATCH') DEFAULT 'GET',
description TEXT,
parameters JSON,
response_example JSON,
requires_auth BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
API实现代码
1. 项目初始化
创建项目并安装依赖:
bash
mkdir education-platform-api
cd education-platform-api
npm init -y
npm install express mysql2 bcryptjs jsonwebtoken cors dotenv
npm install -D nodemon
2. 项目结构
text
education-platform-api/
├── config/
│ └── database.js
├── controllers/
│ ├── authController.js
│ ├── userController.js
│ ├── courseController.js
│ ├── articleController.js
│ └── apiController.js
├── middleware/
│ ├── auth.js
│ └── validation.js
├── routes/
│ ├── auth.js
│ ├── users.js
│ ├── courses.js
│ ├── articles.js
│ └── apis.js
├── .env
├── app.js
└── package.json
3. 环境配置 (.env)
env
PORT=3000
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=education_platform
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
4. 数据库连接配置 (config/database.js)
javascript
const mysql = require('mysql2');
require('dotenv').config();
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
connection.connect((err) => {
if (err) {
console.error('数据库连接失败: ' + err.stack);
return;
}
console.log('数据库连接成功,连接ID: ' + connection.threadId);
});
module.exports = connection;
5. 认证中间件 (middleware/auth.js)
javascript
const jwt = require('jsonwebtoken');
require('dotenv').config();
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, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({
success: false,
message: '访问令牌无效'
});
}
req.user = user;
next();
});
};
const authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: '没有权限执行此操作'
});
}
next();
};
};
module.exports = { authenticateToken, authorize };
6. 认证控制器 (controllers/authController.js)
javascript
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const db = require('../config/database');
require('dotenv').config();
// 用户注册
const register = (req, res) => {
const { username, email, password, role } = req.body;
// 验证必填字段
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: '用户名、邮箱和密码是必填项'
});
}
// 检查用户是否已存在
db.query(
'SELECT id FROM users WHERE email = ? OR username = ?',
[email, username],
async (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length > 0) {
return res.status(409).json({
success: false,
message: '用户名或邮箱已存在'
});
}
try {
// 加密密码
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 创建用户
db.query(
'INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)',
[username, email, hashedPassword, role || 'student'],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '用户创建失败'
});
}
// 生成JWT令牌
const token = jwt.sign(
{
id: results.insertId,
username,
email,
role: role || 'student'
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN }
);
res.status(201).json({
success: true,
message: '用户注册成功',
data: {
token,
user: {
id: results.insertId,
username,
email,
role: role || 'student'
}
}
});
}
);
} catch (error) {
res.status(500).json({
success: false,
message: '服务器错误'
});
}
}
);
};
// 用户登录
const login = (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
success: false,
message: '邮箱和密码是必填项'
});
}
// 查找用户
db.query(
'SELECT * FROM users WHERE email = ?',
[email],
async (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(401).json({
success: false,
message: '邮箱或密码错误'
});
}
const user = results[0];
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({
success: false,
message: '邮箱或密码错误'
});
}
// 生成JWT令牌
const token = jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN }
);
res.json({
success: true,
message: '登录成功',
data: {
token,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
avatar_url: user.avatar_url
}
}
});
}
);
};
// 获取当前用户信息
const getCurrentUser = (req, res) => {
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users WHERE id = ?',
[req.user.id],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
module.exports = { register, login, getCurrentUser };
7. 用户控制器 (controllers/userController.js)
javascript
const db = require('../config/database');
// 获取所有用户
const getAllUsers = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
// 获取用户总数
db.query('SELECT COUNT(*) as total FROM users', (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页用户数据
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users LIMIT ? OFFSET ?',
[limit, offset],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
users: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
}
);
});
};
// 获取单个用户
const getUserById = (req, res) => {
const userId = req.params.id;
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users WHERE id = ?',
[userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 更新用户信息
const updateUser = (req, res) => {
const userId = req.params.id;
const { username, avatar_url } = req.body;
// 检查是否有权限更新(只能更新自己的信息或管理员)
if (req.user.id != userId && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此用户信息'
});
}
db.query(
'UPDATE users SET username = ?, avatar_url = ? WHERE id = ?',
[username, avatar_url, userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库更新错误'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
message: '用户信息更新成功'
});
}
);
};
// 删除用户
const deleteUser = (req, res) => {
const userId = req.params.id;
// 只有管理员可以删除用户
if (req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除用户'
});
}
db.query(
'DELETE FROM users WHERE id = ?',
[userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库删除错误'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
message: '用户删除成功'
});
}
);
};
module.exports = { getAllUsers, getUserById, updateUser, deleteUser };
8. 课程控制器 (controllers/courseController.js)
javascript
const db = require('../config/database');
// 获取所有课程
const getAllCourses = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const { category, level, is_published } = req.query;
let query = `
SELECT c.*, u.username as instructor_name
FROM courses c
JOIN users u ON c.instructor_id = u.id
`;
let countQuery = 'SELECT COUNT(*) as total FROM courses c';
let queryParams = [];
let countParams = [];
let conditions = [];
// 添加筛选条件
if (category) {
conditions.push('c.category = ?');
queryParams.push(category);
countParams.push(category);
}
if (level) {
conditions.push('c.level = ?');
queryParams.push(level);
countParams.push(level);
}
if (is_published !== undefined) {
conditions.push('c.is_published = ?');
queryParams.push(is_published === 'true');
countParams.push(is_published === 'true');
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
countQuery += ' WHERE ' + conditions.join(' AND ');
}
query += ' LIMIT ? OFFSET ?';
queryParams.push(limit, offset);
// 获取课程总数
db.query(countQuery, countParams, (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页课程数据
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
courses: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
});
});
};
// 获取单个课程
const getCourseById = (req, res) => {
const courseId = req.params.id;
db.query(
`SELECT c.*, u.username as instructor_name
FROM courses c
JOIN users u ON c.instructor_id = u.id
WHERE c.id = ?`,
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 创建课程
const createCourse = (req, res) => {
const { title, description, price, thumbnail_url, category, level, duration } = req.body;
const instructorId = req.user.id;
if (!title || !description) {
return res.status(400).json({
success: false,
message: '课程标题和描述是必填项'
});
}
db.query(
'INSERT INTO courses (title, description, instructor_id, price, thumbnail_url, category, level, duration) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[title, description, instructorId, price, thumbnail_url, category, level, duration],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程创建失败'
});
}
res.status(201).json({
success: true,
message: '课程创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新课程
const updateCourse = (req, res) => {
const courseId = req.params.id;
const { title, description, price, thumbnail_url, category, level, duration, is_published } = req.body;
// 检查是否是课程创建者或管理员
db.query(
'SELECT instructor_id FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
if (results[0].instructor_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此课程'
});
}
db.query(
'UPDATE courses SET title = ?, description = ?, price = ?, thumbnail_url = ?, category = ?, level = ?, duration = ?, is_published = ? WHERE id = ?',
[title, description, price, thumbnail_url, category, level, duration, is_published, courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程更新失败'
});
}
res.json({
success: true,
message: '课程更新成功'
});
}
);
}
);
};
// 删除课程
const deleteCourse = (req, res) => {
const courseId = req.params.id;
// 检查是否是课程创建者或管理员
db.query(
'SELECT instructor_id FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
if (results[0].instructor_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除此课程'
});
}
db.query(
'DELETE FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程删除失败'
});
}
res.json({
success: true,
message: '课程删除成功'
});
}
);
}
);
};
// 报名课程
const enrollCourse = (req, res) => {
const courseId = req.params.id;
const userId = req.user.id;
// 检查课程是否存在
db.query(
'SELECT id FROM courses WHERE id = ? AND is_published = TRUE',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在或未发布'
});
}
// 检查是否已报名
db.query(
'SELECT id FROM user_courses WHERE user_id = ? AND course_id = ?',
[userId, courseId],
(err, enrollmentResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (enrollmentResults.length > 0) {
return res.status(409).json({
success: false,
message: '您已经报名此课程'
});
}
// 报名课程
db.query(
'INSERT INTO user_courses (user_id, course_id) VALUES (?, ?)',
[userId, courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '报名失败'
});
}
res.status(201).json({
success: true,
message: '报名成功'
});
}
);
}
);
}
);
};
module.exports = {
getAllCourses,
getCourseById,
createCourse,
updateCourse,
deleteCourse,
enrollCourse
};
9. 文章控制器 (controllers/articleController.js)
javascript
const db = require('../config/database');
// 获取所有文章
const getAllArticles = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const { course_id, is_public, author_id } = req.query;
let query = `
SELECT a.*, u.username as author_name, c.title as course_title
FROM articles a
LEFT JOIN users u ON a.author_id = u.id
LEFT JOIN courses c ON a.course_id = c.id
`;
let countQuery = 'SELECT COUNT(*) as total FROM articles a';
let queryParams = [];
let countParams = [];
let conditions = [];
// 添加筛选条件
if (course_id) {
conditions.push('a.course_id = ?');
queryParams.push(course_id);
countParams.push(course_id);
}
if (is_public !== undefined) {
conditions.push('a.is_public = ?');
queryParams.push(is_public === 'true');
countParams.push(is_public === 'true');
}
if (author_id) {
conditions.push('a.author_id = ?');
queryParams.push(author_id);
countParams.push(author_id);
}
// 非管理员只能查看公开文章或自己的文章
if (req.user.role !== 'admin') {
conditions.push('(a.is_public = TRUE OR a.author_id = ?)');
queryParams.push(req.user.id);
countParams.push(req.user.id);
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
countQuery += ' WHERE ' + conditions.join(' AND ');
}
query += ' ORDER BY a.created_at DESC LIMIT ? OFFSET ?';
queryParams.push(limit, offset);
// 获取文章总数
db.query(countQuery, countParams, (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页文章数据
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
articles: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
});
});
};
// 获取单个文章
const getArticleById = (req, res) => {
const articleId = req.params.id;
let query = `
SELECT a.*, u.username as author_name, c.title as course_title
FROM articles a
LEFT JOIN users u ON a.author_id = u.id
LEFT JOIN courses c ON a.course_id = c.id
WHERE a.id = ?
`;
let queryParams = [articleId];
// 非管理员只能查看公开文章或自己的文章
if (req.user.role !== 'admin') {
query += ' AND (a.is_public = TRUE OR a.author_id = ?)';
queryParams.push(req.user.id);
}
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在或没有访问权限'
});
}
// 增加浏览次数
if (results[0].is_public || results[0].author_id === req.user.id) {
db.query(
'UPDATE articles SET view_count = view_count + 1 WHERE id = ?',
[articleId]
);
}
res.json({
success: true,
data: results[0]
});
});
};
// 创建文章
const createArticle = (req, res) => {
const { title, content, course_id, is_public, tags } = req.body;
const authorId = req.user.id;
if (!title || !content) {
return res.status(400).json({
success: false,
message: '文章标题和内容是必填项'
});
}
db.query(
'INSERT INTO articles (title, content, author_id, course_id, is_public, tags) VALUES (?, ?, ?, ?, ?, ?)',
[title, content, authorId, course_id, is_public || true, JSON.stringify(tags)],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章创建失败'
});
}
res.status(201).json({
success: true,
message: '文章创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新文章
const updateArticle = (req, res) => {
const articleId = req.params.id;
const { title, content, course_id, is_public, tags } = req.body;
// 检查是否是文章作者或管理员
db.query(
'SELECT author_id FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在'
});
}
if (results[0].author_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此文章'
});
}
db.query(
'UPDATE articles SET title = ?, content = ?, course_id = ?, is_public = ?, tags = ? WHERE id = ?',
[title, content, course_id, is_public, JSON.stringify(tags), articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章更新失败'
});
}
res.json({
success: true,
message: '文章更新成功'
});
}
);
}
);
};
// 删除文章
const deleteArticle = (req, res) => {
const articleId = req.params.id;
// 检查是否是文章作者或管理员
db.query(
'SELECT author_id FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在'
});
}
if (results[0].author_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除此文章'
});
}
db.query(
'DELETE FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章删除失败'
});
}
res.json({
success: true,
message: '文章删除成功'
});
}
);
}
);
};
module.exports = {
getAllArticles,
getArticleById,
createArticle,
updateArticle,
deleteArticle
};
10. API控制器 (controllers/apiController.js)
javascript
const db = require('../config/database');
// 获取所有API
const getAllAPIs = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
// 获取API总数
db.query('SELECT COUNT(*) as total FROM apis', (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页API数据
db.query(
'SELECT * FROM apis LIMIT ? OFFSET ?',
[limit, offset],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
apis: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
}
);
});
};
// 获取单个API
const getAPIById = (req, res) => {
const apiId = req.params.id;
db.query(
'SELECT * FROM apis WHERE id = ?',
[apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 创建API
const createAPI = (req, res) => {
const { name, endpoint, method, description, parameters, response_example, requires_auth } = req.body;
if (!name || !endpoint) {
return res.status(400).json({
success: false,
message: 'API名称和端点是必填项'
});
}
db.query(
'INSERT INTO apis (name, endpoint, method, description, parameters, response_example, requires_auth) VALUES (?, ?, ?, ?, ?, ?, ?)',
[name, endpoint, method, description, JSON.stringify(parameters), JSON.stringify(response_example), requires_auth],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API创建失败'
});
}
res.status(201).json({
success: true,
message: 'API创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新API
const updateAPI = (req, res) => {
const apiId = req.params.id;
const { name, endpoint, method, description, parameters, response_example, requires_auth } = req.body;
db.query(
'UPDATE apis SET name = ?, endpoint = ?, method = ?, description = ?, parameters = ?, response_example = ?, requires_auth = ? WHERE id = ?',
[name, endpoint, method, description, JSON.stringify(parameters), JSON.stringify(response_example), requires_auth, apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API更新失败'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
message: 'API更新成功'
});
}
);
};
// 删除API
const deleteAPI = (req, res) => {
const apiId = req.params.id;
db.query(
'DELETE FROM apis WHERE id = ?',
[apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API删除失败'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
message: 'API删除成功'
});
}
);
};
module.exports = {
getAllAPIs,
getAPIById,
createAPI,
updateAPI,
deleteAPI
};
11. 路由配置
创建各个模块的路由文件:
认证路由 (routes/auth.js)
javascript
const express = require('express');
const router = express.Router();
const { register, login, getCurrentUser } = require('../controllers/authController');
const { authenticateToken } = require('../middleware/auth');
router.post('/register', register);
router.post('/login', login);
router.get('/me', authenticateToken, getCurrentUser);
module.exports = router;
用户路由 (routes/users.js)
javascript
const express = require('express');
const router = express.Router();
const { getAllUsers, getUserById, updateUser, deleteUser } = require('../controllers/userController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', authenticateToken, authorize('admin'), getAllUsers);
router.get('/:id', authenticateToken, getUserById);
router.put('/:id', authenticateToken, updateUser);
router.delete('/:id', authenticateToken, authorize('admin'), deleteUser);
module.exports = router;
课程路由 (routes/courses.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllCourses,
getCourseById,
createCourse,
updateCourse,
deleteCourse,
enrollCourse
} = require('../controllers/courseController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', getAllCourses);
router.get('/:id', getCourseById);
router.post('/', authenticateToken, authorize('teacher', 'admin'), createCourse);
router.put('/:id', authenticateToken, updateCourse);
router.delete('/:id', authenticateToken, deleteCourse);
router.post('/:id/enroll', authenticateToken, enrollCourse);
module.exports = router;
文章路由 (routes/articles.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllArticles,
getArticleById,
createArticle,
updateArticle,
deleteArticle
} = require('../controllers/articleController');
const { authenticateToken } = require('../middleware/auth');
router.get('/', authenticateToken, getAllArticles);
router.get('/:id', authenticateToken, getArticleById);
router.post('/', authenticateToken, createArticle);
router.put('/:id', authenticateToken, updateArticle);
router.delete('/:id', authenticateToken, deleteArticle);
module.exports = router;
API路由 (routes/apis.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllAPIs,
getAPIById,
createAPI,
updateAPI,
deleteAPI
} = require('../controllers/apiController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', getAllAPIs);
router.get('/:id', getAPIById);
router.post('/', authenticateToken, authorize('admin'), createAPI);
router.put('/:id', authenticateToken, authorize('admin'), updateAPI);
router.delete('/:id', authenticateToken, authorize('admin'), deleteAPI);
module.exports = router;
12. 主应用文件 (app.js)
javascript
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const courseRoutes = require('./routes/courses');
const articleRoutes = require('./routes/articles');
const apiRoutes = require('./routes/apis');
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/courses', courseRoutes);
app.use('/api/articles', articleRoutes);
app.use('/api/apis', apiRoutes);
// 健康检查端点
app.get('/health', (req, res) => {
res.json({
success: true,
message: '教育平台API运行正常',
timestamp: new Date().toISOString()
});
});
// 404处理
app.use('*', (req, res) => {
res.status(404).json({
success: false,
message: 'API端点不存在'
});
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
});
// 启动服务器
app.listen(PORT, () => {
console.log(`教育平台API服务器运行在端口 ${PORT}`);
});
module.exports = app;
13. Package.json 脚本配置
json
{
"name": "education-platform-api",
"version": "1.0.0",
"description": "教育平台API服务",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express": "^4.18.2",
"mysql2": "^3.6.0",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"keywords": ["education", "api", "nodejs", "mysql"],
"author": "Your Name",
"license": "MIT"
}
API使用说明
1. 认证相关API
-
POST /api/auth/register
- 用户注册 -
POST /api/auth/login
- 用户登录 -
GET /api/auth/me
- 获取当前用户信息
2. 用户管理API
-
GET /api/users
- 获取所有用户(仅管理员) -
GET /api/users/:id
- 获取指定用户信息 -
PUT /api/users/:id
- 更新用户信息 -
DELETE /api/users/:id
- 删除用户(仅管理员)
3. 课程管理API
-
GET /api/courses
- 获取所有课程 -
GET /api/courses/:id
- 获取指定课程详情 -
POST /api/courses
- 创建课程(教师和管理员) -
PUT /api/courses/:id
- 更新课程信息 -
DELETE /api/courses/:id
- 删除课程 -
POST /api/courses/:id/enroll
- 报名课程
4. 文章管理API
-
GET /api/articles
- 获取所有文章 -
GET /api/articles/:id
- 获取指定文章详情 -
POST /api/articles
- 创建文章 -
PUT /api/articles/:id
- 更新文章 -
DELETE /api/articles/:id
- 删除文章
5. API文档管理
-
GET /api/apis
- 获取所有API文档 -
GET /api/apis/:id
- 获取指定API文档 -
POST /api/apis
- 创建API文档(仅管理员) -
PUT /api/apis/:id
- 更新API文档(仅管理员) -
DELETE /api/apis/:id
- 删除API文档(仅管理员)
部署和运行
- 安装依赖:
bash
npm install
-
创建MySQL数据库并导入SQL结构
-
配置环境变量(.env文件)
-
启动服务器:
bash
# 开发模式
npm run dev
# 生产模式
npm start
安全注意事项
-
在生产环境中,确保使用强密码和安全的JWT密钥
-
考虑使用HTTPS加密通信
-
实施速率限制防止API滥用
-
定期更新依赖包以修复安全漏洞
-
对用户输入进行严格的验证和清理
这个API系统提供了教育平台的核心功能,您可以根据需要进一步扩展和优化。
使用nodejs链接mysql编写教育网站api,包含登录注册首页和其他页面api接口
我们正在设计一个教育网站的API,包括登录、注册、首页和其他页面的接口。我们将使用Node.js和MySQL。
根据要求,我们需要设计以下API端点:
-
用户认证
-
注册
-
登录
-
获取当前用户信息(可能需要)
-
-
首页
- 获取首页数据(可能包括轮播图、推荐课程、最新文章等)
-
课程相关
-
获取课程列表
-
获取课程详情
-
获取课程分类
-
-
文章相关
-
获取文章列表
-
获取文章详情
-
-
笔记相关
-
获取笔记列表(公开和私密)
-
创建笔记
-
更新笔记
-
删除笔记
-
-
自学页面
- 获取自学课程列表(免费和收费)
-
案例页面
- 获取案例列表
-
API页面
- 获取API列表
考虑到时间,我们先设计主要接口,并实现其中一部分。
我们将使用Express框架,并使用mysql2库连接MySQL数据库。
步骤:
-
初始化项目
-
安装依赖:express, mysql2, bcryptjs, jsonwebtoken, cors, dotenv等
-
创建数据库连接
-
设计数据库表(如果尚未创建)
-
创建路由和控制器
-
实现中间件(如认证中间件)
由于代码量较大,我们将分步骤实现。
首先,我们设计数据库表(假设数据库名为education_platform):
用户表(users):
id, username, email, password, avatar, role, created_at, updated_at
课程表(courses):
id, title, description, price, category_id, teacher_id, cover_image, created_at, updated_at
课程分类表(categories):
id, name, description
文章表(articles):
id, title, content, author_id, cover_image, is_public, created_at, updated_at
笔记表(notes):
id, title, content, user_id, course_id, is_public, created_at, updated_at
案例表(cases):
id, title, description, image, link, created_at
API表(apis):
id, name, endpoint, method, description, parameters, example
由于时间关系,我们不会完全实现所有接口,但会给出一个框架,并实现部分核心功能。
我们先从用户注册和登录开始。
注意:密码需要加密存储,使用bcryptjs。
我们使用JWT进行身份验证。
接下来,我们开始编写代码。
教育网站API设计与实现 (Node.js + MySQL)
下面我将设计一个完整的教育网站API系统,包含用户认证、课程管理、文章管理等功能。
系统架构
技术栈
-
后端: Node.js + Express.js
-
数据库: MySQL
-
身份验证: JWT (JSON Web Tokens)
-
密码加密: bcryptjs
-
其他: cors, dotenv, nodemon
数据库设计
首先创建数据库表结构:
sql
CREATE DATABASE education_platform;
USE education_platform;
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('student', 'teacher', 'admin') DEFAULT 'student',
avatar_url VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 课程表
CREATE TABLE courses (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
instructor_id INT NOT NULL,
price DECIMAL(10, 2) DEFAULT 0,
thumbnail_url VARCHAR(255),
category VARCHAR(100),
level ENUM('beginner', 'intermediate', 'advanced') DEFAULT 'beginner',
duration INT, -- 课程时长(小时)
is_published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (instructor_id) REFERENCES users(id)
);
-- 文章表
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content LONGTEXT NOT NULL,
author_id INT NOT NULL,
course_id INT,
is_public BOOLEAN DEFAULT TRUE,
tags JSON,
view_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (author_id) REFERENCES users(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
-- 用户课程关联表(报名记录)
CREATE TABLE user_courses (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
course_id INT NOT NULL,
enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
progress INT DEFAULT 0, -- 学习进度百分比
completed BOOLEAN DEFAULT FALSE,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (course_id) REFERENCES courses(id),
UNIQUE KEY unique_enrollment (user_id, course_id)
);
-- API表
CREATE TABLE apis (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
endpoint VARCHAR(255) NOT NULL,
method ENUM('GET', 'POST', 'PUT', 'DELETE', 'PATCH') DEFAULT 'GET',
description TEXT,
parameters JSON,
response_example JSON,
requires_auth BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
API实现代码
1. 项目初始化
创建项目并安装依赖:
bash
mkdir education-platform-api
cd education-platform-api
npm init -y
npm install express mysql2 bcryptjs jsonwebtoken cors dotenv
npm install -D nodemon
2. 项目结构
text
education-platform-api/
├── config/
│ └── database.js
├── controllers/
│ ├── authController.js
│ ├── userController.js
│ ├── courseController.js
│ ├── articleController.js
│ └── apiController.js
├── middleware/
│ ├── auth.js
│ └── validation.js
├── routes/
│ ├── auth.js
│ ├── users.js
│ ├── courses.js
│ ├── articles.js
│ └── apis.js
├── .env
├── app.js
└── package.json
3. 环境配置 (.env)
env
PORT=3000
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=education_platform
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
4. 数据库连接配置 (config/database.js)
javascript
const mysql = require('mysql2');
require('dotenv').config();
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
connection.connect((err) => {
if (err) {
console.error('数据库连接失败: ' + err.stack);
return;
}
console.log('数据库连接成功,连接ID: ' + connection.threadId);
});
module.exports = connection;
5. 认证中间件 (middleware/auth.js)
javascript
const jwt = require('jsonwebtoken');
require('dotenv').config();
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, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({
success: false,
message: '访问令牌无效'
});
}
req.user = user;
next();
});
};
const authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: '没有权限执行此操作'
});
}
next();
};
};
module.exports = { authenticateToken, authorize };
6. 认证控制器 (controllers/authController.js)
javascript
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const db = require('../config/database');
require('dotenv').config();
// 用户注册
const register = (req, res) => {
const { username, email, password, role } = req.body;
// 验证必填字段
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: '用户名、邮箱和密码是必填项'
});
}
// 检查用户是否已存在
db.query(
'SELECT id FROM users WHERE email = ? OR username = ?',
[email, username],
async (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length > 0) {
return res.status(409).json({
success: false,
message: '用户名或邮箱已存在'
});
}
try {
// 加密密码
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 创建用户
db.query(
'INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)',
[username, email, hashedPassword, role || 'student'],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '用户创建失败'
});
}
// 生成JWT令牌
const token = jwt.sign(
{
id: results.insertId,
username,
email,
role: role || 'student'
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN }
);
res.status(201).json({
success: true,
message: '用户注册成功',
data: {
token,
user: {
id: results.insertId,
username,
email,
role: role || 'student'
}
}
});
}
);
} catch (error) {
res.status(500).json({
success: false,
message: '服务器错误'
});
}
}
);
};
// 用户登录
const login = (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
success: false,
message: '邮箱和密码是必填项'
});
}
// 查找用户
db.query(
'SELECT * FROM users WHERE email = ?',
[email],
async (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(401).json({
success: false,
message: '邮箱或密码错误'
});
}
const user = results[0];
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({
success: false,
message: '邮箱或密码错误'
});
}
// 生成JWT令牌
const token = jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN }
);
res.json({
success: true,
message: '登录成功',
data: {
token,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
avatar_url: user.avatar_url
}
}
});
}
);
};
// 获取当前用户信息
const getCurrentUser = (req, res) => {
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users WHERE id = ?',
[req.user.id],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
module.exports = { register, login, getCurrentUser };
7. 用户控制器 (controllers/userController.js)
javascript
const db = require('../config/database');
// 获取所有用户
const getAllUsers = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
// 获取用户总数
db.query('SELECT COUNT(*) as total FROM users', (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页用户数据
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users LIMIT ? OFFSET ?',
[limit, offset],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
users: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
}
);
});
};
// 获取单个用户
const getUserById = (req, res) => {
const userId = req.params.id;
db.query(
'SELECT id, username, email, role, avatar_url, created_at FROM users WHERE id = ?',
[userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 更新用户信息
const updateUser = (req, res) => {
const userId = req.params.id;
const { username, avatar_url } = req.body;
// 检查是否有权限更新(只能更新自己的信息或管理员)
if (req.user.id != userId && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此用户信息'
});
}
db.query(
'UPDATE users SET username = ?, avatar_url = ? WHERE id = ?',
[username, avatar_url, userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库更新错误'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
message: '用户信息更新成功'
});
}
);
};
// 删除用户
const deleteUser = (req, res) => {
const userId = req.params.id;
// 只有管理员可以删除用户
if (req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除用户'
});
}
db.query(
'DELETE FROM users WHERE id = ?',
[userId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库删除错误'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.json({
success: true,
message: '用户删除成功'
});
}
);
};
module.exports = { getAllUsers, getUserById, updateUser, deleteUser };
8. 课程控制器 (controllers/courseController.js)
javascript
const db = require('../config/database');
// 获取所有课程
const getAllCourses = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const { category, level, is_published } = req.query;
let query = `
SELECT c.*, u.username as instructor_name
FROM courses c
JOIN users u ON c.instructor_id = u.id
`;
let countQuery = 'SELECT COUNT(*) as total FROM courses c';
let queryParams = [];
let countParams = [];
let conditions = [];
// 添加筛选条件
if (category) {
conditions.push('c.category = ?');
queryParams.push(category);
countParams.push(category);
}
if (level) {
conditions.push('c.level = ?');
queryParams.push(level);
countParams.push(level);
}
if (is_published !== undefined) {
conditions.push('c.is_published = ?');
queryParams.push(is_published === 'true');
countParams.push(is_published === 'true');
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
countQuery += ' WHERE ' + conditions.join(' AND ');
}
query += ' LIMIT ? OFFSET ?';
queryParams.push(limit, offset);
// 获取课程总数
db.query(countQuery, countParams, (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页课程数据
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
courses: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
});
});
};
// 获取单个课程
const getCourseById = (req, res) => {
const courseId = req.params.id;
db.query(
`SELECT c.*, u.username as instructor_name
FROM courses c
JOIN users u ON c.instructor_id = u.id
WHERE c.id = ?`,
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 创建课程
const createCourse = (req, res) => {
const { title, description, price, thumbnail_url, category, level, duration } = req.body;
const instructorId = req.user.id;
if (!title || !description) {
return res.status(400).json({
success: false,
message: '课程标题和描述是必填项'
});
}
db.query(
'INSERT INTO courses (title, description, instructor_id, price, thumbnail_url, category, level, duration) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[title, description, instructorId, price, thumbnail_url, category, level, duration],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程创建失败'
});
}
res.status(201).json({
success: true,
message: '课程创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新课程
const updateCourse = (req, res) => {
const courseId = req.params.id;
const { title, description, price, thumbnail_url, category, level, duration, is_published } = req.body;
// 检查是否是课程创建者或管理员
db.query(
'SELECT instructor_id FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
if (results[0].instructor_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此课程'
});
}
db.query(
'UPDATE courses SET title = ?, description = ?, price = ?, thumbnail_url = ?, category = ?, level = ?, duration = ?, is_published = ? WHERE id = ?',
[title, description, price, thumbnail_url, category, level, duration, is_published, courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程更新失败'
});
}
res.json({
success: true,
message: '课程更新成功'
});
}
);
}
);
};
// 删除课程
const deleteCourse = (req, res) => {
const courseId = req.params.id;
// 检查是否是课程创建者或管理员
db.query(
'SELECT instructor_id FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在'
});
}
if (results[0].instructor_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除此课程'
});
}
db.query(
'DELETE FROM courses WHERE id = ?',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '课程删除失败'
});
}
res.json({
success: true,
message: '课程删除成功'
});
}
);
}
);
};
// 报名课程
const enrollCourse = (req, res) => {
const courseId = req.params.id;
const userId = req.user.id;
// 检查课程是否存在
db.query(
'SELECT id FROM courses WHERE id = ? AND is_published = TRUE',
[courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '课程不存在或未发布'
});
}
// 检查是否已报名
db.query(
'SELECT id FROM user_courses WHERE user_id = ? AND course_id = ?',
[userId, courseId],
(err, enrollmentResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (enrollmentResults.length > 0) {
return res.status(409).json({
success: false,
message: '您已经报名此课程'
});
}
// 报名课程
db.query(
'INSERT INTO user_courses (user_id, course_id) VALUES (?, ?)',
[userId, courseId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '报名失败'
});
}
res.status(201).json({
success: true,
message: '报名成功'
});
}
);
}
);
}
);
};
module.exports = {
getAllCourses,
getCourseById,
createCourse,
updateCourse,
deleteCourse,
enrollCourse
};
9. 文章控制器 (controllers/articleController.js)
javascript
const db = require('../config/database');
// 获取所有文章
const getAllArticles = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const { course_id, is_public, author_id } = req.query;
let query = `
SELECT a.*, u.username as author_name, c.title as course_title
FROM articles a
LEFT JOIN users u ON a.author_id = u.id
LEFT JOIN courses c ON a.course_id = c.id
`;
let countQuery = 'SELECT COUNT(*) as total FROM articles a';
let queryParams = [];
let countParams = [];
let conditions = [];
// 添加筛选条件
if (course_id) {
conditions.push('a.course_id = ?');
queryParams.push(course_id);
countParams.push(course_id);
}
if (is_public !== undefined) {
conditions.push('a.is_public = ?');
queryParams.push(is_public === 'true');
countParams.push(is_public === 'true');
}
if (author_id) {
conditions.push('a.author_id = ?');
queryParams.push(author_id);
countParams.push(author_id);
}
// 非管理员只能查看公开文章或自己的文章
if (req.user.role !== 'admin') {
conditions.push('(a.is_public = TRUE OR a.author_id = ?)');
queryParams.push(req.user.id);
countParams.push(req.user.id);
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
countQuery += ' WHERE ' + conditions.join(' AND ');
}
query += ' ORDER BY a.created_at DESC LIMIT ? OFFSET ?';
queryParams.push(limit, offset);
// 获取文章总数
db.query(countQuery, countParams, (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页文章数据
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
articles: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
});
);
};
// 获取单个文章
const getArticleById = (req, res) => {
const articleId = req.params.id;
let query = `
SELECT a.*, u.username as author_name, c.title as course_title
FROM articles a
LEFT JOIN users u ON a.author_id = u.id
LEFT JOIN courses c ON a.course_id = c.id
WHERE a.id = ?
`;
let queryParams = [articleId];
// 非管理员只能查看公开文章或自己的文章
if (req.user.role !== 'admin') {
query += ' AND (a.is_public = TRUE OR a.author_id = ?)';
queryParams.push(req.user.id);
}
db.query(query, queryParams, (err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在或没有访问权限'
});
}
// 增加浏览次数
if (results[0].is_public || results[0].author_id === req.user.id) {
db.query(
'UPDATE articles SET view_count = view_count + 1 WHERE id = ?',
[articleId]
);
}
res.json({
success: true,
data: results[0]
});
});
};
// 创建文章
const createArticle = (req, res) => {
const { title, content, course_id, is_public, tags } = req.body;
const authorId = req.user.id;
if (!title || !content) {
return res.status(400).json({
success: false,
message: '文章标题和内容是必填项'
});
}
db.query(
'INSERT INTO articles (title, content, author_id, course_id, is_public, tags) VALUES (?, ?, ?, ?, ?, ?)',
[title, content, authorId, course_id, is_public || true, JSON.stringify(tags)],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章创建失败'
});
}
res.status(201).json({
success: true,
message: '文章创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新文章
const updateArticle = (req, res) => {
const articleId = req.params.id;
const { title, content, course_id, is_public, tags } = req.body;
// 检查是否是文章作者或管理员
db.query(
'SELECT author_id FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在'
});
}
if (results[0].author_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限更新此文章'
});
}
db.query(
'UPDATE articles SET title = ?, content = ?, course_id = ?, is_public = ?, tags = ? WHERE id = ?',
[title, content, course_id, is_public, JSON.stringify(tags), articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章更新失败'
});
}
res.json({
success: true,
message: '文章更新成功'
});
}
);
}
);
};
// 删除文章
const deleteArticle = (req, res) => {
const articleId = req.params.id;
// 检查是否是文章作者或管理员
db.query(
'SELECT author_id FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: '文章不存在'
});
}
if (results[0].author_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
message: '没有权限删除此文章'
});
}
db.query(
'DELETE FROM articles WHERE id = ?',
[articleId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '文章删除失败'
});
}
res.json({
success: true,
message: '文章删除成功'
});
}
);
}
);
};
module.exports = {
getAllArticles,
getArticleById,
createArticle,
updateArticle,
deleteArticle
};
10. API控制器 (controllers/apiController.js)
javascript
const db = require('../config/database');
// 获取所有API
const getAllAPIs = (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
// 获取API总数
db.query('SELECT COUNT(*) as total FROM apis', (err, countResults) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
const total = countResults[0].total;
const totalPages = Math.ceil(total / limit);
// 获取分页API数据
db.query(
'SELECT * FROM apis LIMIT ? OFFSET ?',
[limit, offset],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
res.json({
success: true,
data: {
apis: results,
pagination: {
page,
limit,
total,
totalPages
}
}
});
}
);
});
};
// 获取单个API
const getAPIById = (req, res) => {
const apiId = req.params.id;
db.query(
'SELECT * FROM apis WHERE id = ?',
[apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: '数据库查询错误'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
data: results[0]
});
}
);
};
// 创建API
const createAPI = (req, res) => {
const { name, endpoint, method, description, parameters, response_example, requires_auth } = req.body;
if (!name || !endpoint) {
return res.status(400).json({
success: false,
message: 'API名称和端点是必填项'
});
}
db.query(
'INSERT INTO apis (name, endpoint, method, description, parameters, response_example, requires_auth) VALUES (?, ?, ?, ?, ?, ?, ?)',
[name, endpoint, method, description, JSON.stringify(parameters), JSON.stringify(response_example), requires_auth],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API创建失败'
});
}
res.status(201).json({
success: true,
message: 'API创建成功',
data: {
id: results.insertId
}
});
}
);
};
// 更新API
const updateAPI = (req, res) => {
const apiId = req.params.id;
const { name, endpoint, method, description, parameters, response_example, requires_auth } = req.body;
db.query(
'UPDATE apis SET name = ?, endpoint = ?, method = ?, description = ?, parameters = ?, response_example = ?, requires_auth = ? WHERE id = ?',
[name, endpoint, method, description, JSON.stringify(parameters), JSON.stringify(response_example), requires_auth, apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API更新失败'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
message: 'API更新成功'
});
}
);
};
// 删除API
const deleteAPI = (req, res) => {
const apiId = req.params.id;
db.query(
'DELETE FROM apis WHERE id = ?',
[apiId],
(err, results) => {
if (err) {
return res.status(500).json({
success: false,
message: 'API删除失败'
});
}
if (results.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'API不存在'
});
}
res.json({
success: true,
message: 'API删除成功'
});
}
);
};
module.exports = {
getAllAPIs,
getAPIById,
createAPI,
updateAPI,
deleteAPI
};
11. 路由配置
创建各个模块的路由文件:
认证路由 (routes/auth.js)
javascript
const express = require('express');
const router = express.Router();
const { register, login, getCurrentUser } = require('../controllers/authController');
const { authenticateToken } = require('../middleware/auth');
router.post('/register', register);
router.post('/login', login);
router.get('/me', authenticateToken, getCurrentUser);
module.exports = router;
用户路由 (routes/users.js)
javascript
const express = require('express');
const router = express.Router();
const { getAllUsers, getUserById, updateUser, deleteUser } = require('../controllers/userController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', authenticateToken, authorize('admin'), getAllUsers);
router.get('/:id', authenticateToken, getUserById);
router.put('/:id', authenticateToken, updateUser);
router.delete('/:id', authenticateToken, authorize('admin'), deleteUser);
module.exports = router;
课程路由 (routes/courses.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllCourses,
getCourseById,
createCourse,
updateCourse,
deleteCourse,
enrollCourse
} = require('../controllers/courseController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', getAllCourses);
router.get('/:id', getCourseById);
router.post('/', authenticateToken, authorize('teacher', 'admin'), createCourse);
router.put('/:id', authenticateToken, updateCourse);
router.delete('/:id', authenticateToken, deleteCourse);
router.post('/:id/enroll', authenticateToken, enrollCourse);
module.exports = router;
文章路由 (routes/articles.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllArticles,
getArticleById,
createArticle,
updateArticle,
deleteArticle
} = require('../controllers/articleController');
const { authenticateToken } = require('../middleware/auth');
router.get('/', authenticateToken, getAllArticles);
router.get('/:id', authenticateToken, getArticleById);
router.post('/', authenticateToken, createArticle);
router.put('/:id', authenticateToken, updateArticle);
router.delete('/:id', authenticateToken, deleteArticle);
module.exports = router;
API路由 (routes/apis.js)
javascript
const express = require('express');
const router = express.Router();
const {
getAllAPIs,
getAPIById,
createAPI,
updateAPI,
deleteAPI
} = require('../controllers/apiController');
const { authenticateToken, authorize } = require('../middleware/auth');
router.get('/', getAllAPIs);
router.get('/:id', getAPIById);
router.post('/', authenticateToken, authorize('admin'), createAPI);
router.put('/:id', authenticateToken, authorize('admin'), updateAPI);
router.delete('/:id', authenticateToken, authorize('admin'), deleteAPI);
module.exports = router;
12. 主应用文件 (app.js)
javascript
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const courseRoutes = require('./routes/courses');
const articleRoutes = require('./routes/articles');
const apiRoutes = require('./routes/apis');
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/courses', courseRoutes);
app.use('/api/articles', articleRoutes);
app.use('/api/apis', apiRoutes);
// 健康检查端点
app.get('/health', (req, res) => {
res.json({
success: true,
message: '教育平台API运行正常',
timestamp: new Date().toISOString()
});
});
// 404处理
app.use('*', (req, res) => {
res.status(404).json({
success: false,
message: 'API端点不存在'
});
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
});
// 启动服务器
app.listen(PORT, () => {
console.log(`教育平台API服务器运行在端口 ${PORT}`);
});
module.exports = app;
13. Package.json 脚本配置
json
{
"name": "education-platform-api",
"version": "1.0.0",
"description": "教育平台API服务",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express": "^4.18.2",
"mysql2": "^3.6.0",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"keywords": ["education", "api", "nodejs", "mysql"],
"author": "Your Name",
"license": "MIT"
}
API使用说明
1. 认证相关API
-
POST /api/auth/register
- 用户注册 -
POST /api/auth/login
- 用户登录 -
GET /api/auth/me
- 获取当前用户信息
2. 用户管理API
-
GET /api/users
- 获取所有用户(仅管理员) -
GET /api/users/:id
- 获取指定用户信息 -
PUT /api/users/:id
- 更新用户信息 -
DELETE /api/users/:id
- 删除用户(仅管理员)
3. 课程管理API
-
GET /api/courses
- 获取所有课程 -
GET /api/courses/:id
- 获取指定课程详情 -
POST /api/courses
- 创建课程(教师和管理员) -
PUT /api/courses/:id
- 更新课程信息 -
DELETE /api/courses/:id
- 删除课程 -
POST /api/courses/:id/enroll
- 报名课程
4. 文章管理API
-
GET /api/articles
- 获取所有文章 -
GET /api/articles/:id
- 获取指定文章详情 -
POST /api/articles
- 创建文章 -
PUT /api/articles/:id
- 更新文章 -
DELETE /api/articles/:id
- 删除文章
5. API文档管理
-
GET /api/apis
- 获取所有API文档 -
GET /api/apis/:id
- 获取指定API文档 -
POST /api/apis
- 创建API文档(仅管理员) -
PUT /api/apis/:id
- 更新API文档(仅管理员) -
DELETE /api/apis/:id
- 删除API文档(仅管理员)
部署和运行
- 安装依赖:
bash
npm install
-
创建MySQL数据库并导入SQL结构
-
配置环境变量(.env文件)
-
启动服务器:
bash
# 开发模式
npm run dev
# 生产模式
npm start
安全注意事项
-
在生产环境中,确保使用强密码和安全的JWT密钥
-
考虑使用HTTPS加密通信
-
实施速率限制防止API滥用
-
定期更新依赖包以修复安全漏洞
-
对用户输入进行严格的验证和清理
这个API系统提供了教育平台的核心功能,您可以根据需要进一步扩展和优化。