基于 Vue 3 + Node.js + MongoDB的全栈校园论坛系统
1. 项目概述
随着互联网技术的快速发展和社交网络的普及,校友之间的交流与联系变得越来越重要。传统的校友联系方式存在信息分散、交流不便等问题,难以满足现代校友群体的需求。为了解决这些问题,本文设计并实现了一个基于 Web 技术的校友论坛系统。
本系统采用前后端分离的架构模式,前端使用 Vue 3 框架结合 TypeScript 和 Element Plus 组件库构建用户界面,后端使用 Node.js 的 Koa 框架提供 RESTful API 服务,数据存储采用 MongoDB 非关系型数据库。系统实现了用户注册登录、帖子发布与浏览、评论互动、私信交流、公告管理等核心功能,并提供了完善的管理后台用于内容管理和数据统计。
2. 项目截图
用户端:








后台管理:




3.功能需求
3.1 用户角色分析
系统设计了两种用户角色:
1. 普通用户
普通用户是系统的主要使用者,包括在校学生和已毕业校友。其主要需求包括:
- 注册账号并完善个人信息
- 浏览和搜索帖子内容
- 发布帖子分享经验和求助
- 评论和点赞帖子
- 与其他用户私信交流
- 查看系统公告
2. 管理员
管理员负责系统的日常管理和维护,其主要需求包括:
- 管理用户账号(查看、禁用、启用)
- 管理帖子内容(删除违规内容、置顶重要帖子)
- 管理评论(删除不当评论)
- 发布和管理系统公告
- 查看系统数据统计
3.2 功能模块划分
根据用户需求,系统划分为以下功能模块:
1. 用户管理模块
该模块负责用户相关的所有功能:
- 用户注册:新用户通过邮箱注册账号,填写基本信息
- 用户登录:已注册用户通过邮箱和密码登录系统
- 个人资料管理:用户可以查看和编辑个人信息,包括用户名、学校、专业、毕业年份、职业、个人简介等
- 密码管理:用户可以修改登录密码
- 用户主页:展示用户的基本信息和发帖历史
2. 帖子管理模块
该模块是系统的核心功能:
- 帖子发布:用户可以发布新帖子,包括标题、内容、分类和标签
- 帖子浏览:用户可以浏览帖子列表,支持按分类筛选和排序
- 帖子详情:查看帖子的完整内容、作者信息、评论列表
- 帖子搜索:通过关键词搜索相关帖子
- 帖子编辑:作者可以编辑自己发布的帖子
- 帖子删除:作者和管理员可以删除帖子
- 帖子点赞:用户可以对帖子进行点赞或取消点赞
帖子分类包括:
- 求助:寻求帮助和建议
- 分享:分享经验和知识
- 求职:求职信息和职业发展
- 学习:学习资源和心得
- 创业:创业经验和项目
- 生活:生活话题和闲聊
3. 评论管理模块
该模块支持用户对帖子进行评论互动:
- 发表评论:用户可以对帖子发表评论
- 评论点赞:用户可以对评论进行点赞
- 删除评论:评论作者、帖子作者和管理员可以删除评论
- 评论列表:按时间顺序展示帖子的所有评论
4. 私信模块
该模块支持用户之间的私密交流:
- 发送私信:用户可以向其他用户发送私信
- 会话列表:展示所有私信会话,显示未读消息数量
- 消息详情:查看与某个用户的完整对话历史
- 消息已读:自动标记已读消息
5. 公告模块
该模块用于发布系统公告和重要通知:
- 公告展示:在首页顶部展示活跃的公告
- 公告管理:管理员可以创建、编辑、删除公告
- 公告分类:系统、活动、维护、重要等类型
- 公告优先级:支持设置公告的优先级
- 公告过期:支持设置公告的过期时间
6. 管理后台模块
该模块为管理员提供系统管理功能:
- 用户管理:查看用户列表,搜索用户,禁用/启用用户账号
- 内容管理:查看所有帖子,删除违规内容,置顶重要帖子
- 评论管理:查看所有评论,删除不当评论
- 公告管理:创建、编辑、删除公告
- 数据统计:展示用户数、帖子数、评论数等统计数据
4. 系统架构设计
4.1 总体架构
本系统采用前后端分离的架构模式,将系统分为三层:表现层、业务逻辑层和数据访问层。系统总体架构如图 3-1 所示。
bash
┌─────────────────────────────────────────┐
│ 表现层(前端) │
│ ┌─────────────────────────────────┐ │
│ │ Vue 3 + TypeScript + Vite │ │
│ │ ├─ 路由管理(Vue Router) │ │
│ │ ├─ 状态管理(Pinia) │ │
│ │ ├─ UI 组件(Element Plus) │ │
│ │ └─ HTTP 客户端(Axios) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
↕ HTTP/HTTPS
┌─────────────────────────────────────────┐
│ 业务逻辑层(后端) │
│ ┌─────────────────────────────────┐ │
│ │ Node.js + Koa │ │
│ │ ├─ 路由层(Koa Router) │ │
│ │ ├─ 中间件层(认证、日志等) │ │
│ │ ├─ 业务逻辑层(Controllers) │ │
│ │ └─ 数据访问层(Mongoose) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
↕
┌─────────────────────────────────────────┐
│ 数据访问层(数据库) │
│ ┌─────────────────────────────────┐ │
│ │ MongoDB │ │
│ │ ├─ users(用户集合) │ │
│ │ ├─ posts(帖子集合) │ │
│ │ ├─ comments(评论集合) │ │
│ │ ├─ messages(消息集合) │ │
│ │ └─ announcements(公告集合) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
架构特点:
- 前后端分离:前端和后端独立开发、部署和维护
- RESTful API:前后端通过标准的 RESTful API 进行通信
- 模块化设计:各层职责明确,模块之间低耦合高内聚
- 可扩展性强:便于功能扩展和技术升级
4.2 前端架构
前端采用 Vue 3 框架,结合 TypeScript 和 Element Plus 构建单页应用(SPA)。前端架构如图 3-2 所示。
bash
src/
├── api/ # API 接口封装
│ ├── index.ts # Axios 实例配置
│ ├── auth.ts # 认证相关接口
│ ├── posts.ts # 帖子相关接口
│ └── ...
├── assets/ # 静态资源
├── components/ # 通用组件
│ ├── Header.vue # 头部组件
│ ├── Footer.vue # 底部组件
│ └── ...
├── router/ # 路由配置
│ └── index.ts # 路由定义和守卫
├── stores/ # 状态管理
│ ├── auth.ts # 用户认证状态
│ └── ...
├── types/ # TypeScript 类型定义
│ └── index.ts # 全局类型
├── utils/ # 工具函数
│ └── index.ts # 通用工具
├── views/ # 页面组件
│ ├── Home.vue # 首页
│ ├── Login.vue # 登录页
│ ├── Posts.vue # 帖子列表
│ └── ...
├── App.vue # 根组件
└── main.ts # 应用入口
关键技术:
- Vue Router:实现单页应用的路由管理
- Pinia:轻量级的状态管理库
- Axios:HTTP 客户端,封装 API 请求
- TypeScript:提供类型安全和更好的开发体验
4.3 后端架构
后端采用 Node.js 的 Koa 框架,使用 Mongoose 进行数据库操作。后端架构如图 3-3 所示。
bash
server/
├── src/
│ ├── config/ # 配置文件
│ │ └── database.js # 数据库配置
│ ├── middleware/ # 中间件
│ │ └── auth.js # 认证中间件
│ ├── models/ # 数据模型
│ │ ├── User.js # 用户模型
│ │ ├── Post.js # 帖子模型
│ │ └── ...
│ ├── routes/ # 路由处理
│ │ ├── auth.js # 认证路由
│ │ ├── posts.js # 帖子路由
│ │ └── ...
│ └── app.js # 应用入口
├── scripts/ # 脚本文件
│ └── import-data.js # 数据导入脚本
└── .env # 环境变量
关键技术:
- Koa:轻量级的 Web 框架
- Koa Router:路由管理
- Mongoose:MongoDB ODM
- JWT:身份认证
- bcryptjs:密码加密
4.4 数据库设计
数据库选型
本系统选择 MongoDB 作为数据库,主要基于以下考虑:
- 灵活的数据模型:MongoDB 使用文档模型,适合存储非结构化数据
- 易于扩展:支持水平扩展,便于应对数据增长
- 开发效率高:与 JavaScript 对象模型匹配,开发便捷
- 性能优秀:读写性能好,适合高并发场景
数据模型设计
1. 用户(User)集合
javascript
{
_id: ObjectId, // 用户ID
username: String, // 用户名(唯一)
email: String, // 邮箱(唯一)
password: String, // 密码(加密)
avatar: String, // 头像URL
role: String, // 角色(user/admin)
school: String, // 学校
major: String, // 专业
graduationYear: Number, // 毕业年份
profession: String, // 职业
bio: String, // 个人简介
points: Number, // 积分
isVerified: Boolean, // 是否认证
lastLoginAt: Date, // 最后登录时间
isActive: Boolean, // 是否激活
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}
2. 帖子(Post)集合
javascript
{
_id: ObjectId, // 帖子ID
title: String, // 标题
content: String, // 内容
category: String, // 分类
tags: [String], // 标签数组
author: ObjectId, // 作者ID(引用User)
likes: [{ // 点赞列表
user: ObjectId, // 用户ID
createdAt: Date // 点赞时间
}],
views: Number, // 浏览量
comments: [ObjectId], // 评论ID数组
isPinned: Boolean, // 是否置顶
isActive: Boolean, // 是否激活
isDeleted: Boolean, // 是否删除
deletedAt: Date, // 删除时间
deletedBy: ObjectId, // 删除者ID
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}
3. 评论(Comment)集合
javascript
{
_id: ObjectId, // 评论ID
content: String, // 内容
author: ObjectId, // 作者ID(引用User)
post: ObjectId, // 帖子ID(引用Post)
likes: [{ // 点赞列表
user: ObjectId, // 用户ID
createdAt: Date // 点赞时间
}],
parentComment: ObjectId, // 父评论ID(用于回复)
replies: [ObjectId], // 回复ID数组
isActive: Boolean, // 是否激活
isDeleted: Boolean, // 是否删除
deletedAt: Date, // 删除时间
deletedBy: ObjectId, // 删除者ID
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}
4. 消息(Message)集合
javascript
{
_id: ObjectId, // 消息ID
sender: ObjectId, // 发送者ID(引用User)
receiver: ObjectId, // 接收者ID(引用User)
content: String, // 内容
isRead: Boolean, // 是否已读
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}
5. 公告(Announcement)集合
javascript
{
_id: ObjectId, // 公告ID
title: String, // 标题
content: String, // 内容
type: String, // 类型(system/activity/maintenance/important)
author: ObjectId, // 作者ID(引用User)
priority: Number, // 优先级
expiresAt: Date, // 过期时间
viewCount: Number, // 浏览量
isActive: Boolean, // 是否激活
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}
5. 技术知识点文档
5.1 前端技术栈
5.1.1 Vue 3
Composition API
- setup 函数: 组件的入口点,替代 Options API
- 响应式 API :
ref(): 创建响应式引用,适用于基本类型reactive(): 创建响应式对象,适用于对象类型computed(): 创建计算属性watch(): 监听响应式数据变化
- 生命周期钩子 :
onMounted(): 组件挂载后执行onUnmounted(): 组件卸载前执行onBeforeMount(): 组件挂载前执行
Vue Router 4
- 路由配置: 定义应用的路由规则
- 路由守卫 :
- 全局前置守卫
beforeEach: 用于权限验证 - 路由独享守卫: 特定路由的守卫
- 全局前置守卫
- 编程式导航 :
router.push(): 跳转到新路由router.replace(): 替换当前路由router.back(): 返回上一页
- 动态路由 : 使用参数匹配路由,如
/posts/:id
Pinia 状态管理
- Store 定义 : 使用
defineStore创建状态仓库 - State: 存储应用状态
- Getters: 计算派生状态
- Actions: 定义修改状态的方法
- 持久化: 使用 localStorage 持久化用户信息和 Token
5.1.2 TypeScript
类型系统
- 基础类型: string, number, boolean, array, object
- 接口定义: 定义对象的结构
- 类型别名 : 使用
type定义复杂类型 - 泛型: 创建可重用的组件
- 类型断言: 手动指定值的类型
在 Vue 中使用 TypeScript
- Props 类型定义 : 使用
defineProps<T>() - Emit 类型定义 : 使用
defineEmits<T>() - Ref 类型 :
Ref<T>,ComputedRef<T> - 组件实例类型 :
ComponentPublicInstance
5.1.3 Element Plus
组件库
- 表单组件: el-form, el-input, el-select, el-button
- 数据展示: el-table, el-card, el-tag, el-avatar
- 反馈组件: el-message, el-dialog, el-loading
- 导航组件: el-menu, el-tabs, el-pagination
- 布局组件: el-container, el-row, el-col
5.1.4 Axios
HTTP 客户端
- 实例创建: 创建自定义 axios 实例
- 请求拦截器: 在请求发送前添加 Token
- 响应拦截器: 统一处理响应和错误
- 错误处理: 根据状态码显示不同错误信息
API 封装
- 模块化: 按功能模块划分 API
- 类型安全: 使用 TypeScript 定义请求和响应类型
- 统一格式: 保持 API 调用的一致性
5.2 后端技术栈
5.2.1 Koa 框架
- 中间件: 洋葱模型的中间件机制
- Context 对象: 封装 request 和 response
- 错误处理: 统一的错误处理机制
- 异步流程: 基于 async/await
5.2.2 常用中间件
- koa-router: 路由管理
- koa-bodyparser: 解析请求体
- koa-cors: 处理跨域请求
- 自定义中间件: 认证、日志等
5.3 数据库:MongoDB
5.3.1 NoSQL 数据库
- 文档存储: 使用 JSON 格式存储数据
- 集合: 类似关系型数据库的表
- 灵活的模式: 无需预定义结构
- 索引: 提高查询性能
5.3.2 Mongoose ODM
- Schema 定义: 定义数据模型结构
- 模型方法: 实例方法和静态方法
- 中间件: pre/post 钩子
- 验证: 数据验证规则
- 关联: 使用 ref 建立文档关联
- 查询构建器: 链式查询 API
5.3.3 常用操作
- CRUD: Create, Read, Update, Delete
- 查询: find, findOne, findById
- 更新: updateOne, updateMany, findByIdAndUpdate
- 删除: deleteOne, deleteMany, findByIdAndDelete
- 聚合: aggregate 管道操作
6. 系统详细设计与实现
6.1 前端实现
前端项目结构
本系统前端采用 Vue3 + TypeScript + Vite 技术栈,项目结构如下:
less
src/
├── api/ // 封装所有后端接口请求
├── assets/ // 静态资源
├── components/ // 公共组件
├── layouts/ // 页面布局
├── router/ // 路由配置
├── stores/ // 状态管理
├── styles/ // 全局样式
├── types/ // 类型定义
├── views/ // 业务页面
└── main.ts // 入口文件
路由与页面设计
前端采用 Vue Router 实现页面路由管理,主要路由包括:首页、登录、注册、帖子列表、帖子详情、个人中心、消息中心、后台管理等。部分路由配置示例:
js
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
// ... 其他页面
const routes = [
{ path: '/', component: Home },
{ path: '/login', component: Login },
{ path: '/register', component: Register },
// ... 其他路由
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
状态管理 本系统采用 Pinia 进行全局状态管理,主要管理用户信息、帖子列表、消息通知等。以用户认证为例:
js
// src/stores/auth.ts
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', {
state: () => ({
token: '',
user: null,
}),
actions: {
setToken(token: string) {
this.token = token
},
setUser(user: any) {
this.user = user
},
logout() {
this.token = ''
this.user = null
}
}
})
API 封装
所有后端接口请求统一封装在 src/api/ 目录下,便于维护和复用。例如用户登录接口:
js
// src/api/auth.ts
import axios from 'axios'
export function login(data: { username: string, password: string }) {
return axios.post('/api/auth/login', data)
}
主要页面与组件实现
- 首页(Home.vue):展示最新帖子、热门标签、公告等
- 帖子列表(Posts.vue):分页展示所有帖子,支持分类、标签筛选
- 帖子详情(PostDetail.vue):展示帖子内容、评论列表、评论输入框
- 登录/注册(Login.vue/Register.vue):表单校验、接口对接
- 个人中心(Profile.vue):展示和编辑个人信息
- 消息中心(Messages.vue):展示系统消息、私信
- 后台管理(admin/):管理员专属页面,管理用户、帖子、举报等
6.2 后端实现
后端项目结构
后端采用 Node.js + Express + MongoDB 技术栈,项目结构如下:
arduino
server/
├── src/
├── app.js // 应用入口
├── config/ // 配置文件
├── middleware/ // 中间件
├── models/ // 数据模型
├── routes/ // 路由
└── ... // 其他
路由与中间件
后端采用 Express 路由机制,将不同功能模块的接口分离管理。例如用户认证路由:
js
// server/src/routes/auth.js
const express = require('express')
const router = express.Router()
const { login, register } = require('../controllers/authController')
router.post('/login', login)
router.post('/register', register)
module.exports = router
中间件用于处理请求认证、错误处理、日志记录等。例如 JWT 认证中间件:
js
// server/src/middleware/auth.js
const jwt = require('jsonwebtoken')
module.exports = function (req, res, next) {
const token = req.headers['authorization']
if (!token) return res.status(401).json({ message: '未登录' })
try {
const decoded = jwt.verify(token, 'your_jwt_secret')
req.user = decoded
next()
} catch (err) {
res.status(401).json({ message: 'Token无效' })
}
}
数据模型设计
后端采用 Mongoose 定义数据模型。例如用户模型:
评论与互动
- 用户可对帖子进行评论,评论可被回复
- 评论数据与帖子关联,支持评论点赞、删除
- 评论成功后,系统自动生成通知消息
消息通知
- 用户收到评论、回复、私信等事件时,后端生成消息记录
- 前端定时拉取或通过 WebSocket 获取新消息
- 用户可在消息中心查看、标记已读
举报与后台管理
- 用户可对违规内容进行举报,填写举报原因
- 后端存储举报信息,管理员后台可查看、处理举报
- 管理员可对用户、帖子、评论等进行管理操作
6.3 代码实现示例
6.1 用户注册接口(后端)
js
// server/src/controllers/authController.js
const User = require('../models/User')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
exports.register = async (req, res) => {
const { username, password, email } = req.body
const user = await User.findOne({ username })
if (user) return res.status(400).json({ message: '用户名已存在' })
const hash = await bcrypt.hash(password, 10)
const newUser = new User({ username, password: hash, email })
await newUser.save()
res.json({ message: '注册成功' })
}
6.2 用户登录接口(后端)
php
exports.login = async (req, res) => {
const { username, password } = req.body
const user = await User.findOne({ username })
if (!user) return res.status(400).json({ message: '用户不存在' })
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) return res.status(400).json({ message: '密码错误' })
const token = jwt.sign({ id: user._id, username: user.username }, 'your_jwt_secret', { expiresIn: '7d' })
res.json({ token, user })
}
6.3 帖子发布接口(后端)
js
// server/src/controllers/postController.js
const Post = require('../models/Post')
exports.createPost = async (req, res) => {
const { title, content, category, tags } = req.body
const post = new Post({
author: req.user.id,
title,
content,
category,
tags,
createdAt: new Date()
})
await post.save()
res.json({ message: '帖子发布成功', post })
}