基于Django与Vue 3的AI实验室智能预约系统实战 1. 项目缘起与整体设计思路实验室预约这件事说大不大说小也真不小。我所在的课题组有六台高性能计算节点、两台示波器、一台频谱仪还有几套需要提前预约的测试台架。过去两年我们一直用共享表格加群接龙的方式管理这些设备结果就是有人约了不来有人来了发现设备被占管理员每周要花三四个小时手动对账。最离谱的一次两位同学同时约了同一台设备到了现场才发现只能一个人干等。这个项目就是在这种背景下启动的。核心目标很明确做一个基于 Django 与 Vue 3 的 AI 实验室智能预约系统把设备信息、预约时段、用户权限、使用记录全部管起来同时引入一些智能化的能力比如根据历史使用数据推荐空闲时段、自动识别冲突预约、生成使用统计报表。技术栈选型上后端用Django DRF前端用Vue 3数据库用MySQL整体架构是前后端分离的 RESTful API 模式。为什么这么选我对比过几种方案。纯 Django 模板渲染也能做但交互体验会比较割裂尤其是日历选择、实时状态刷新这些场景前端框架的优势很明显。用 Spring Boot 加 Vue 也不是不行但团队里 Python 生态更熟Django 自带的 ORM、Admin 后台、认证体系能省掉大量重复劳动。DRF 负责把数据模型暴露成 APIVue 3 负责交互层职责清晰后期维护也方便。这个系统适合谁来参考如果你正在做毕业设计、课程项目、实验室管理系统或者想找一个Django Vue 3 前后端分离的完整实战案例这套方案可以直接抄作业。即使你之前只写过单体 Django 项目跟着思路走也能把前后端串起来。下面我会从架构设计、核心模块、实操步骤、踩坑经验几个维度把整个项目的实现过程拆开讲清楚。2. 技术选型与架构拆解2.1 为什么是 Django DRF Vue 3 这套组合先说说后端。Django 最大的优势是“开箱即用”它自带 ORM、用户认证、Admin 管理后台、表单验证、CSRF 防护这些东西如果从零搭至少要多花一周时间。DRF 在此基础上提供了序列化器、视图集、权限控制、分页、过滤把数据模型快速变成 API 接口。对于预约系统这种以 CRUD 为主、逻辑不算特别复杂的场景DRF 的开发效率非常高。前端选 Vue 3主要考虑三点。第一组合式 API 写起来更灵活逻辑复用比 Options API 清爽很多。第二Vue 3 的响应式系统基于 Proxy对数组和对象的监听更彻底处理预约列表的实时更新很顺手。第三Element Plus 这套 UI 组件库对表格、表单、日历、弹窗的支持很完善能省掉大量样式工作。数据库用 MySQL是因为实验室现有的运维体系对 MySQL 更熟悉备份、监控、权限管理都有现成方案。虽然 SQLite 在开发阶段更方便但生产环境还是需要 MySQL 的并发能力和稳定性。这里有个细节Django 默认使用utf8字符集而 MySQL 8.0 默认是utf8mb4建库时最好显式指定utf8mb4否则存储中文或特殊符号时可能出问题。2.2 前后端分离的接口设计原则前后端分离最怕的就是接口设计混乱。我的做法是所有接口以/api/开头按资源划分路径比如/api/devices/、/api/reservations/、/api/users/。每个资源支持标准的 GET、POST、PUT、PATCH、DELETE 方法返回统一的 JSON 结构{ code: 200, message: success, data: {} }分页接口统一返回count、next、previous、results四个字段这是 DRF 的默认风格前端可以直接复用分页组件。认证方式用 JWT登录后返回 access token 和 refresh token前端把 token 存在 localStorage 里每次请求通过Authorization: Bearer token携带。注意JWT 的 access token 有效期不要设太长建议 30 分钟到 2 小时。refresh token 可以设 7 天但要做好黑名单机制否则用户登出后旧 token 仍然有效。2.3 数据库表结构设计核心表有四张用户表、设备表、预约表、使用记录表。用户表直接用 Django 自带的auth_user通过扩展 Profile 表存实验室角色学生、教师、管理员。设备表存设备名称、型号、位置、状态、可预约时段。预约表是核心字段包括用户、设备、开始时间、结束时间、状态、备注。使用记录表在预约结束后由管理员确认记录实际使用时长和异常情况。这里有个设计上的取舍预约时段是存“开始时间 结束时间”还是“时间段 ID”我选前者因为灵活性更高用户可以自由选择任意时段不受固定时间片的限制。但这样会带来一个问题判断冲突时需要做区间重叠检测。SQL 层面的逻辑是new_start existing_end AND new_end existing_start只要满足这个条件就说明有冲突。3. 核心模块实现与实操要点3.1 环境搭建与项目初始化先列一下我用的版本Python 3.10、Django 4.2、DRF 3.14、MySQL 8.0、Node.js 18、Vue 3.3、Element Plus 2.3。版本不需要完全一致但 Django 建议用 4.xVue 必须用 3.x。后端初始化步骤# 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖 pip install django djangorestframework djangorestframework-simplejwt mysqlclient django-cors-headers # 创建项目 django-admin startproject lab_reservation cd lab_reservation python manage.py startapp apiMySQL 这边先建库CREATE DATABASE lab_reservation CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER lab_userlocalhost IDENTIFIED BY your_password; GRANT ALL PRIVILEGES ON lab_reservation.* TO lab_userlocalhost; FLUSH PRIVILEGES;然后在settings.py里配置数据库连接。这里有个坑mysqlclient在 Windows 上安装经常报错需要先装 MySQL Connector/C 或者用pymysql替代。如果实在装不上可以在__init__.py里加import pymysql pymysql.install_as_MySQLdb()前端初始化npm create vuelatest lab-reservation-frontend cd lab-reservation-frontend npm install npm install element-plus axios vue-router pinia3.2 用户认证与权限控制认证用djangorestframework-simplejwt配置好之后登录接口返回 token前端存起来。权限控制分三级普通用户只能看自己的预约教师可以看自己课题组的预约管理员可以看全部。DRF 的权限类可以这样写from rest_framework.permissions import BasePermission class IsOwnerOrAdmin(BasePermission): def has_object_permission(self, request, view, obj): if request.user.is_staff: return True return obj.user request.user前端路由守卫里判断 token 是否存在不存在就跳登录页。这里有个细节token 过期后axios 拦截器要能自动刷新 token否则用户操作到一半突然跳登录体验很差。我的做法是在响应拦截器里判断 401然后用 refresh token 换新的 access token换成功后重发原请求。实操心得refresh token 刷新时要做并发控制。如果同时有多个请求返回 401不要每个都去刷新否则会生成多个新 token。用一个 flag 标记是否正在刷新其他请求排队等待。3.3 设备管理与预约冲突检测设备管理模块相对简单就是标准的 CRUD。重点说预约冲突检测。后端在创建预约时先查该设备在目标时段内是否已有预约def check_conflict(device_id, start_time, end_time, exclude_idNone): queryset Reservation.objects.filter( device_iddevice_id, status__in[pending, approved], start_time__ltend_time, end_time__gtstart_time ) if exclude_id: queryset queryset.exclude(idexclude_id) return queryset.exists()这个逻辑看起来简单但有几个边界情况要注意。第一如果预约是“待审核”状态是否算冲突我的处理是算因为待审核的预约也可能被批准如果不算冲突批准时又要重新检查逻辑会更复杂。第二跨天预约怎么处理比如晚上 10 点到第二天早上 8 点这种直接按时间戳比较就行不需要特殊处理。第三设备维护时段怎么排除我在设备表里加了maintenance_start和maintenance_end字段创建预约时先检查是否落在维护时段内。前端这边用 Element Plus 的el-date-picker选择时间范围选完后实时调用后端接口检查冲突如果有冲突就提示用户换时段。这个交互比提交后再报错友好得多。3.4 智能推荐与数据统计“智能”这部分我做了两个功能。第一个是空闲时段推荐根据设备的历史预约数据统计哪些时段最空闲在用户选择设备后推荐三个可选时段。实现方式是用 MySQL 的GROUP BY按小时统计预约数量然后取预约量最少的时段。SELECT HOUR(start_time) AS hour, COUNT(*) AS cnt FROM api_reservation WHERE device_id %s AND status approved GROUP BY HOUR(start_time) ORDER BY cnt ASC LIMIT 3;第二个是使用统计报表按周、月统计每台设备的使用时长、预约次数、取消率。前端用 ECharts 画柱状图和折线图后端返回聚合数据。这里有个性能优化点统计查询不要每次实时算可以每天凌晨跑一次定时任务把结果存到统计表里前端直接读统计表。注意统计查询如果数据量大一定要加索引。device_id、start_time、status这三个字段建议建联合索引否则几万条数据之后查询会明显变慢。4. 前后端联调与部署实操4.1 跨域配置与接口联调前后端分离开发时跨域是第一个要解决的问题。Django 这边装django-cors-headers在settings.py里配置INSTALLED_APPS [ ... corsheaders, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, ... ] CORS_ALLOWED_ORIGINS [ http://localhost:5173, http://127.0.0.1:5173, ]前端在vite.config.js里配置代理把/api转发到 Django 的 8000 端口export default defineConfig({ server: { proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true, } } } })联调时建议先用 Postman 或 curl 把后端接口跑通再写前端。我见过不少新手直接前后端一起写结果出了问题不知道是哪边的锅。先用 Postman 确认接口返回正确前端只负责展示和交互排查效率会高很多。4.2 生产环境部署方案开发环境跑通后部署到生产环境。我的方案是 Nginx Gunicorn MySQL Vue 静态文件。Gunicorn 跑 DjangoNginx 做反向代理和静态文件服务Vue 打包后的dist目录直接由 Nginx 托管。Gunicorn 启动命令gunicorn lab_reservation.wsgi:application \ --bind 127.0.0.1:8000 \ --workers 4 \ --threads 2 \ --timeout 120 \ --access-logfile /var/log/gunicorn/access.log \ --error-logfile /var/log/gunicorn/error.logNginx 配置关键部分server { listen 80; server_name your_domain.com; location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { root /var/www/lab-reservation-frontend/dist; try_files $uri $uri/ /index.html; } }实操心得Vue Router 用 history 模式时Nginx 必须配置try_files $uri $uri/ /index.html否则刷新页面会 404。这个坑我踩过两次每次都要查半天。4.3 数据备份与日志监控生产环境一定要做数据备份。MySQL 用mysqldump每天凌晨备份一次保留最近 30 天。Django 的日志配置好logging把错误日志单独输出到文件方便排查。Gunicorn 的 access log 和 error log 也要定期清理否则磁盘会被撑满。# 每天凌晨 2 点备份 0 2 * * * mysqldump -u lab_user -ppassword lab_reservation | gzip /backup/lab_$(date \%Y\%m\%d).sql.gz5. 常见问题与排查技巧实录5.1 数据库连接与迁移问题问题一django.db.utils.OperationalError: (2002, Cant connect to MySQL server)这个通常是 MySQL 服务没启动或者settings.py里的HOST配错了。Windows 上用net start mysql启动服务Linux 上用systemctl start mysql。如果 MySQL 跑在 Docker 里HOST要填容器 IP 或宿主机的host.docker.internal。问题二迁移时提示Table django_migrations doesnt exist说明数据库里还没有迁移记录表。先执行python manage.py migrate --run-syncdb或者检查INSTALLED_APPS里是否漏了api应用。问题三django执行查询-删除对象时外键约束报错Django 默认的外键删除行为是CASCADE但如果你手动改了on_delete删除主表记录时可能报错。建议在模型里显式指定on_deletemodels.CASCADE或models.SET_NULL不要依赖默认值。5.2 前端打包与接口报错问题四Vue 打包后接口 404检查 Nginx 的location /api/配置是否正确proxy_pass后面不要多加斜杠。另外确认 Django 的ALLOWED_HOSTS里加了服务器 IP 或域名。问题五JWT token 过期后前端一直跳登录在 axios 响应拦截器里处理 401先尝试刷新 token刷新失败再跳登录。不要直接在路由守卫里判断 token 是否存在因为 token 可能已过期但还在 localStorage 里。问题六Element Plus 表格数据不更新Vue 3 的响应式对数组的某些操作如直接修改索引不会触发更新。用ref包裹数组时修改要用arr.value [...arr.value]或者用reactive配合push、splice等方法。5.3 性能与安全注意事项性能方面预约列表接口一定要分页默认每页 20 条。如果数据量大加select_related和prefetch_related减少查询次数。统计接口用缓存不要每次都查数据库。安全方面Django 的SECRET_KEY不要提交到代码仓库用环境变量注入。DEBUG在生产环境必须设为False。数据库密码不要明文写在settings.py里用.env文件管理。用户输入的时间、备注等字段要做校验防止 SQL 注入和 XSS 攻击。最后分享一个小技巧开发阶段可以用 Django 的django-debug-toolbar查看每个请求的 SQL 查询次数和耗时对优化性能很有帮助。生产环境记得关掉。这个系统上线后实验室的设备冲突率从每周三四次降到了零管理员对账时间从三小时缩短到十分钟。后续还可以扩展的功能包括微信小程序端、设备使用评价、自动生成使用报告等。如果你也在做类似的项目希望这套方案能帮你少走弯路。