计算机专业本科毕设实战指南:从选题到部署的全链路技术实践
摘要:很多计算机专业本科生在毕设里“选题空、技术乱、工程散”,最后跑不通、写不出、答不好。本文用一套真实可跑的“课程作业提交系统”当主线,把从选题到上线的完整链路拆给你看,帮你把“作业”做成“产品”。
1. 背景痛点:为什么你的毕设总像“课堂实验 plus”
- 需求拍脑袋:老师给个方向,学生拍个题目,结果“智能”“大数据”喊得响,落地一页纸。
- 技术堆乐高:Spring Boot 没学完就敢加 Kafka,Vue 刚跑通就塞 TypeScript,最后依赖冲突到跑不起来。
- 代码无规范:变量拼音、函数写 200 行、一个 Git 仓库里 97 个“test”文件夹,自己三天都看不懂。
- 运行靠人品:答辩现场换电脑,JDK 版本不对,端口被占用,数据库脚本忘导,演示直接翻车。
一句话:缺“工程化”思维,把“能跑”当终点,而不是把“能跑、好改、好扩展”当起点。
2. 技术选型:先选场景,再选栈
| 场景方向 | 推荐技术组合 | 适用痛点 | 备注 |
|---|---|---|---|
| Web 管理型系统(作业提交、文档管理) | Flask + Vue3 + MySQL + Docker | 快速成型、轻量、易部署 | 适合单人 3-4 个月 |
| 数据分析/可视化 | Python(Pandas+Flask)+ React + MySQL | 重算法、轻并发 | 前端交互强,图表丰富 |
| 嵌入式/物联网 | C++ + FreeRTOS + MQTT + Node-RED | 实时性、硬件资源受限 | 需要板子,调试点多 |
| 微服务练手 | Spring Cloud + MyBatis-Plus + MySQL | 高并发、分布式 | 学习曲线陡,慎选 |
经验:本科毕设优先选“单体能拆”的架构——先跑通一条线,再拆模块,别一上来就“八股文”微服务。
3. 核心实现:课程作业提交系统(Flask + Vue3)
3.1 需求一句话
学生上传作业,老师下载批改,管理员看统计,支持批量导出、截止期限、防抄袭查重(简易版)。
3.2 系统架构
- 前端:Vue3 + Vite + ElementPlus
- 后端:Flask + SQLAlchemy + MySQL8
- 文件存储:本地目录 + UUID 重命名(演示用,生产可换 OSS)
- 部署:Docker + gunicorn + nginx
3.3 数据库设计(精简 ER)
- user(id, username, password_hash, role)
- course(id, name, teacher_id)
- assignment(id, course_id, title, deadline)
- submission(id, assignment_id, student_id, file_path, submit_time, similarity_score)
3.4 前后端解耦关键点
- 端口分离:前端 5173,后端 5000,开发阶段用 vite.proxy 转发
/api。 - 统一返回格式:
{ "code": 0, "msg": "ok", "data": {...} } - 异常拦截:Flask 用
@app.errorhandler(HTTPException)统一包装,前端 axios 拦截器直接弹窗。
3.5 身份认证:JWT + Redis 黑名单
- 登录后返回
access_token(15 min)+refresh_token(7 d)。 - Redis 存黑名单,退出即失效,解决 JWT 无法主动失效问题。
- 角色鉴权用装饰器,一行代码解决:
def role_required(allowed): def decorator(fn): @wraps(fn) def wrapper(*args, **kwargs): token = request.headers.get("Authorization","").split()[-1] payload = decode_jwt(token) # 自定义解析 if payload.get("role") not in allowed: return jsonify(code=403, msg="权限不足"), 403 return fn(*args, **kwargs) return wrapper return decorator调用示例:
@bp.route('/assignment', methods=['POST']) @role_required(["teacher"]) def create_assignment(): ...4. 关键代码片段
4.1 JWT 生成与刷新(utils/jwt_helper.py)
import jwt, datetime, redis, os r = redis.Redis(host='redis', port=6379, db=0, decode_responses=True) SECRET = os.getenv("JWT_SECRET", "change_me") def create_token(user_id, role): payload = { "user_id": user_id, "role": role, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15), "type": "access" } return jwt.encode(payload, SECRET, algorithm="HS256") def refresh_if_need(token): try: payload = jwt.decode(token, SECRET, algorithms=["HS256"]) if payload.get("type") != "refresh": return None # 距离过期 < 3 天则发新 token if datetime.datetime.fromtimestamp(payload["exp"]) - datetime.datetime.utcnow() < datetime.timedelta(days=3): return create_token(payload["user_id"], payload["role"]) except jwt.ExpiredSignatureError: return None4.2 Dockerfile(后端)
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple COPY . . CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]4.3 docker-compose.yml(一键起)
version: "3.9" services: db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: homework volumes: ["./sql:/docker-entrypoint-initdb.d"] backend: build: ./backend ports: ["5000:5000"] depends_on: [db] environment: SQLALCHEMY_DATABASE_URI: mysql+pymysql://root:root@db:3306/homework frontend: image: nginx:alpine ports: ["80:80"] volumes: ["./frontend/dist:/usr/share/nginx/html"]5. 性能与安全:别让“小水管”和“小脚本”毁演示
5.1 并发小测试
- 本地用 locust:模拟 200 用户同时上传 2 MB 文件。
- gunicorn 开 4 个 sync worker 时 RPS≈45,CPU 先顶满;换 gevent worker 后 RPS≈110,IO 等待明显下降。
- 结论:文件上传这种 IO 密集场景,worker 类型 > worker 数量。
5.2 安全三板斧
- XSS:Vue 本身默认转义,但后端返回富文本需用
bleach.clean()过滤标签。 - SQL 注入:SQLAlchemy ORM 已参数化,拒绝裸拼 SQL。
- 文件上传:
- 白名单后缀 + MIME 二次检测
- 统一 UUID 重命名,路径与业务隔离
- nginx 加
client_max_body_size 20m;防 DoS
6. 生产环境避坑指南
- Git 提交规范:用 Conventional Commits(
feat:/fix:/docs:),配合commitlint在 CI 强制检查,回滚时一眼看懂。 - 日志分级:Flask 自带
app.logger只到 INFO,线上加RotatingFileHandler存 DEBUG,保留 7 天,排错不用靠 print。 - 依赖锁定:
- Python 用
requirements.txt由pip freeze > requirements.txt生成,别手写。 - Node 用
package-lock.json,CI 里npm ci确保版本一致。
- Python 用
- 配置外置:
- 数据库地址、JWT 密钥、Redis 密码全部写进
.env,仓库只留.env.example,防泄密。
- 数据库地址、JWT 密钥、Redis 密码全部写进
- 备份脚本:
- MySQL 用
mysqldump+cron每日凌晨 2 点打包到/backup,保留 30 份。 - 文件目录同理
tar + date +%F,定期清理。
- MySQL 用
7. 一键验收清单(答辩前对照打钩)
- [ ]
docker-compose up -d能在全新机器跑通 - [ ] 所有 API 在 Postman 集合测试通过
- [ ] 前端页面 F12 无报错,控制台无 404
- [ ] 单元测试覆盖率 ≥ 60%(pytest-cov 生成 badge 贴 README)
- [ ] 日志目录小于 1 GB,备份文件存在
- [ ] README 写明“如何运行、如何配置、如何测试”三段,截图放最后
8. 结语:把“能跑”变成“好改”
毕设不是“写完了”就行,而是“别人能在一台新电脑五分钟跑起来”才算合格。本文的示范代码已放到 GitHub 模板仓库,你可以直接Use this template开坑,再按下面思路继续加料:
- 把文件存储换成阿里云 OSS,练练云原生
- 给作业提交加 WebSocket 实时进度条,让演示更丝滑
- 用 GitHub Actions 跑 CI,把单元测试覆盖率报告推到 README 徽章
- 或者干脆把“查重”模块换成 NLP 文本相似度,瞬间升级 AI 范儿
动手改一行代码,比看十篇“理论框架”更有用。祝你毕设一遍过,答辩不翻车,代码常跑,头发常绿。