当前位置: 首页 > news >正文

Nodejs+html+mysql实现轻量web应用

教育平台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_titleFROM articles a LEFT JOIN users u ON a.author_id = u.idLEFT 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_titleFROM articles a LEFT JOIN users u ON a.author_id = u.idLEFT JOIN courses c ON a.course_id = c.idWHERE 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文档(仅管理员)

部署和运行

  1. 安装依赖:

bash

npm install
  1. 创建MySQL数据库并导入SQL结构

  2. 配置环境变量(.env文件)

  3. 启动服务器:

bash

# 开发模式
npm run dev# 生产模式
npm start

安全注意事项

  1. 在生产环境中,确保使用强密码和安全的JWT密钥

  2. 考虑使用HTTPS加密通信

  3. 实施速率限制防止API滥用

  4. 定期更新依赖包以修复安全漏洞

  5. 对用户输入进行严格的验证和清理

这个API系统提供了教育平台的核心功能,您可以根据需要进一步扩展和优化。

使用nodejs链接mysql编写教育网站api,包含登录注册首页和其他页面api接口

我们正在设计一个教育网站的API,包括登录、注册、首页和其他页面的接口。我们将使用Node.js和MySQL。

根据要求,我们需要设计以下API端点:

  1. 用户认证

    • 注册

    • 登录

    • 获取当前用户信息(可能需要)

  2. 首页

    • 获取首页数据(可能包括轮播图、推荐课程、最新文章等)

  3. 课程相关

    • 获取课程列表

    • 获取课程详情

    • 获取课程分类

  4. 文章相关

    • 获取文章列表

    • 获取文章详情

  5. 笔记相关

    • 获取笔记列表(公开和私密)

    • 创建笔记

    • 更新笔记

    • 删除笔记

  6. 自学页面

    • 获取自学课程列表(免费和收费)

  7. 案例页面

    • 获取案例列表

  8. API页面

    • 获取API列表

考虑到时间,我们先设计主要接口,并实现其中一部分。

我们将使用Express框架,并使用mysql2库连接MySQL数据库。

步骤:

  1. 初始化项目

  2. 安装依赖:express, mysql2, bcryptjs, jsonwebtoken, cors, dotenv等

  3. 创建数据库连接

  4. 设计数据库表(如果尚未创建)

  5. 创建路由和控制器

  6. 实现中间件(如认证中间件)

由于代码量较大,我们将分步骤实现。

首先,我们设计数据库表(假设数据库名为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_titleFROM articles a LEFT JOIN users u ON a.author_id = u.idLEFT 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_titleFROM articles a LEFT JOIN users u ON a.author_id = u.idLEFT JOIN courses c ON a.course_id = c.idWHERE 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文档(仅管理员)

部署和运行

  1. 安装依赖:

bash

npm install
  1. 创建MySQL数据库并导入SQL结构

  2. 配置环境变量(.env文件)

  3. 启动服务器:

bash

# 开发模式
npm run dev# 生产模式
npm start

安全注意事项

  1. 在生产环境中,确保使用强密码和安全的JWT密钥

  2. 考虑使用HTTPS加密通信

  3. 实施速率限制防止API滥用

  4. 定期更新依赖包以修复安全漏洞

  5. 对用户输入进行严格的验证和清理

这个API系统提供了教育平台的核心功能,您可以根据需要进一步扩展和优化。

http://www.dtcms.com/a/392632.html

相关文章:

  • AI模型测评平台工程化实战十二讲(第二讲:目标与指标:把“测评”这件事说清楚(需求到蓝图))
  • 20.二进制和序列化
  • 接口自动化测试实战
  • 为企业系统无缝集成AI检测能力:陌讯AIGC检测系统API接口调用全指南
  • RESTful API
  • Linux知识回顾总结----进程间通信(上)
  • Qwen3-Next深度解析:阿里开源“最强性价比“AI模型,如何用3%参数超越全参数模型?
  • AutoResetEvent:C# 线程同步工具
  • ThinkSound - 阿里通义开源的AI音频生成模型
  • Wan2.2-S2V-14B:音频驱动的电影级视频生成模型全方位详解
  • 基于C++11手撸前端Promise——从异步编程到现代C++实践
  • 构建AI智能体:三十九、中文新闻智能分类:K-Means聚类与Qwen主题生成的融合应用
  • [vibe code追踪] 程序列表视图 | renderNodeList
  • 解决 `sudo rosdepc init` 报错:`command not found` 的完整指南
  • 大数据毕业设计选题推荐-基于大数据的气候驱动的疾病传播可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • Maven 实战:多模块项目与高级打包配置
  • AI 精准绘图专栏:从描述到图像,让创意精准落地​
  • 基于C++11手撸前端Promise进阶——链式调用与组合操作(All/Race)的实现
  • 美国批准通用上市标准!加密货币ETF即将爆发?
  • 子查询及其分类
  • MySQL的存储引擎(一条sql语句的执行流程是什么样的?)
  • JavaScript学习笔记(二):遍历方法汇总
  • Ubuntu22.04显卡掉驱动,重装命令
  • 模式组合应用-享元模式
  • 租房小程序房产小程序源码方案详解
  • p-value与e-value
  • 面经分享--京东一面
  • 大数据毕业设计选题推荐-基于大数据的帕金森病数据可视化分析系统-Spark-Hadoop-Bigdata
  • stack 和 queue
  • 执行yarn init报错:error Invalid package name.(question name)包名格式不对