会议室预约小程序+Django服务端,从源码到部署的完整开发指南
2026/9/14 15:15:46 网站建设 项目流程

简介:这份资源是一套基于Python开发的会议室预约微信小程序与Django服务端后台的毕业设计完整源码,附带项目说明文档,面向需要完成前后端全栈毕业设计的学生及初级开发者,可帮助理解会议室预约、会议管理、微信鉴权等核心功能的完整实现。压缩包共99个文件,以py源码、js脚本、wxml/wxss页面、json配置以及图片和说明文档为主,其中py文件对应Django服务端的模型、接口与业务逻辑,并包含meetings、wechat等应用模块;js、wxml、wxss构成小程序端界面与交互,覆盖room、meeting等预约相关页面,json涵盖项目及各类页面配置,包体仅733KB,按server与miniprogram目录组织,整体结构清晰。目前已有421人学习浏览。借助项目说明文档,使用者能快速完成数据库创建(如meeting库)、服务端本地配置、小程序API地址与appid修改,并在微信开发者工具中编译运行,高效掌握微信小程序与Django服务端联调的完整流程,是一份实操性很强的毕设参考资料。

1. 会议室预约小程序加 Django 服务端,毕设项目到底在做什么

会议室预约这个场景,几乎所有公司都有,但真把它做成一个完整项目的人不多。一个带小程序前端、Django 服务端后台、管理端和用户端分离的毕设项目,恰好覆盖了 Web 开发里最核心的几条线:小程序端要处理微信登录、预约状态流转和页面交互;Django 服务端要管好 REST API、数据库事务和权限校验;后台还要能维护会议室信息、审批预约记录。这套组合做下来,前端、后端、数据库、接口设计全练到了,所以它常被选作毕业设计题目。老实说,这类项目拿到的源码质量参差不齐,有的能跑通,有的依赖缺失、数据库表对不上,甚至需要在 Python 3.8 和 Django 2.x 的老环境里才能启动。这篇博文不评价任何一份具体源码,只从头讲清楚:如果你拿到一份"会议室预约小程序 + Django 服务端后台"的项目包,该怎么看结构、怎么改配置、怎么把关键逻辑跑通,以及毕设答辩时哪些点最容易成为追问目标。

2. 拆解项目包的源码结构与 Django 数据模型设计

拿到压缩包先别急着运行,第一步是看目录结构。一个规范的 Django + 小程序项目,根目录下通常有backend/(Django 工程)、miniprogram/(微信小程序前端)、requirements.txtREADME.md数据库脚本.sql。先确认这些零件齐不齐,再决定怎么启动。

2.1 项目目录结构怎么看

常见做法是,Django 工程以manage.py为入口,backend/里包含settings.pyurls.py以及若干 app(如meetinguserorder)。小程序端则用微信开发者工具打开miniprogram/目录,里面至少有pages/app.jsapp.jsonproject.config.json。别急着改代码,先把requirements.txt打开,看它锁定的 Django 版本和依赖库。

# 在项目根目录下执行,先看目录结构 tree -L 2 -I "node_modules|.git|__pycache__" # 查看 Python 依赖清单 cat requirements.txt

依赖清单里最常见的组合是Django==2.2.xdjangorestframework==3.11.xPyMySQLrequests。如果是 Python 3.7+ 环境,Django 2.2 可以正常跑,但要注意它已经在 2022 年停止官方维护。如果依赖里出现django-cors-headers,说明小程序端和后端是分离部署,跨域配置绕不开。

2.2 核心数据表设计:用户、会议室、预约单

会议室预约系统的核心数据模型,无非是三张主表和一张关联表。用户表(User)既包含小程序端注册的普通用户,也要区分管理员角色;会议室表(MeetingRoom)记录会议室名称、容纳人数、设备清单;预约表(Reservation)则关联用户与会议室,记录起止时间、预约事由和审批状态。很多毕设源码里,预约状态用的是整型字段,比如 0 代表待审批、1 代表已通过、2 代表已拒绝、3 代表已取消。

2.2.1 Django 模型定义示例

以我的习惯,模型不应该写得过于复杂,但状态字段、时间字段和唯一约束必须明确。下面是一个贴近毕设项目常见写法的模型片段,你也可以直接对照源码里的models.py看差异。

# backend/meeting/models.py from django.db import models from django.contrib.auth.models import User class MeetingRoom(models.Model): name = models.CharField('会议室名称', max_length=50, unique=True) location = models.CharField('所在位置', max_length=100) capacity = models.IntegerField('容纳人数', default=10) has_projector = models.BooleanField('投影仪', default=False) has_whiteboard = models.BooleanField('白板', default=False) is_active = models.BooleanField('是否启用', default=True) class Meta: db_table = 'meeting_room' def __str__(self): return self.name class Reservation(models.Model): STATUS_CHOICES = ( (0, '待审批'), (1, '已通过'), (2, '已拒绝'), (3, '已取消'), ) user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='预约人') room = models.ForeignKey(MeetingRoom, on_delete=models.CASCADE, verbose_name='会议室') start_time = models.DateTimeField('开始时间') end_time = models.DateTimeField('结束时间') title = models.CharField('会议主题', max_length=100) participants = models.IntegerField('参会人数', default=1) status = models.SmallIntegerField('状态', choices=STATUS_CHOICES, default=0) remark = models.TextField('备注', blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'reservation' ordering = ['-created_at']

这段代码有四个地方值得注意。ForeignKey直接引用了 Django 自带的User表,毕设级别够用,但如果要扩展部门或角色建议换成自定义用户模型。STATUS_CHOICES把状态码固化在模型层,后面 API 返回时直接映射成中文,避免前端写死数字。db_table显式指定表名,保证你导入 SQL 脚本时表名不会和默认的app名_类名冲突。ordering按创建时间倒序,列表接口不用再额外排序。数据库选型上,学校机房多半给的是 MySQL 5.7,PyMySQL 装上后在settings.py里执行pymysql.install_as_MySQLdb()即可连上。

2.3 settings.py 的关键配置

拿到源码跑不起来,八成卡在settings.py。你需要在INSTALLED_APPS里确认rest_framework和业务 app 都已注册,在DATABASES里修改为本机 MySQL 的账号密码,在LANGUAGE_CODETIME_ZONE上做点小处理。

# backend/backend/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', # DRF 'corsheaders', # 跨域 'meeting', # 业务 app 'user', # 用户 app ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'meeting_room_db', 'USER': 'root', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } } import pymysql pymysql.install_as_MySQLdb() LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = True

USE_TZ = True是很多坑的源头。数据库里存的是 UTC 时间,小程序端显示的时间如果不做时区转换,会出现预约时间差 8 小时的问题。毕设项目里最稳妥的做法是保持USE_TZ = True,在序列化器里统一用本地时间输出,具体方案在 API 那节展开。corsheaders的配置还需要在MIDDLEWARE里加上CorsMiddleware,并且设置CORS_ORIGIN_ALLOW_ALL = True,开发阶段能省掉很多联调烦恼。

2.4 初始化数据库与创建超级管理员

配置改好后,按顺序执行迁移命令。这里有一个常见的顺序错误:项目包自带的 SQL 脚本和 Django 的 migration 文件可能同时存在,如果直接导入 SQL 再执行 migrate,会出现表已存在的报错。稳妥的方法是以 Django migration 为准,先删掉旧数据库再重建。

# 创建数据库(MySQL 命令行执行) mysql -u root -p -e "CREATE DATABASE meeting_room_db DEFAULT CHARACTER SET utf8mb4;" # 在 backend 目录下执行迁移 python manage.py makemigrations python manage.py migrate # 创建管理员账号 python manage.py createsuperuser # 启动开发服务器 python manage.py runserver 0.0.0.0:8000

makemigrations会扫描 app 下的模型变动,生成迁移文件。如果你的源码包已经带好了 migration 文件,这一步可以直接跳过。createsuperuser创建的账号既用于 Django admin 后台登录,也在后续的接口鉴权中作为管理员身份使用。运行runserver后访问http://127.0.0.1:8000/admin,登录进去能看到 admin 界面,说明数据库这关过了。

3. 服务端 API 设计与预约冲突检测逻辑

Django 服务端的核心不只是把 CRUD 接口写出来,真正拉开差距的是预约冲检测、权限控制和小程序登录鉴权。这三块做扎实了,前端再简单,项目整体完成度也低不了。

3.1 用 DRF 实现会议室与预约接口

DRF(Django REST Framework)是这类项目最常见的 API 实现方式。先用ModelSerializer定义序列化器,再写ViewSet挂到路由上。注意,序列化器里不要直接把 User 表的所有字段暴露出去,预约接口只需要返回用户 ID 和用户名。

# backend/meeting/serializers.py from rest_framework import serializers from .models import MeetingRoom, Reservation class MeetingRoomSerializer(serializers.ModelSerializer): class Meta: model = MeetingRoom fields = ['id', 'name', 'location', 'capacity', 'has_projector', 'has_whiteboard'] class ReservationSerializer(serializers.ModelSerializer): room_name = serializers.CharField(source='room.name', read_only=True) user_name = serializers.CharField(source='user.username', read_only=True) status_text = serializers.SerializerMethodField() class Meta: model = Reservation fields = ['id', 'room', 'room_name', 'user', 'user_name', 'start_time', 'end_time', 'title', 'participants', 'status', 'status_text', 'remark'] read_only_fields = ['user', 'status'] def get_status_text(self, obj): return obj.get_status_display()

room_nameuser_name通过source直接取关联表的字段,前端展示列表时不用再发请求查详情。get_status_display()是 Django 模型自带的取 choice 中文映射的方法,配合SerializerMethodField输出成接口字段,小程序端直接渲染。接下来写视图层。预约创建接口要额外校验时间冲突,不能直接靠ModelViewSet一把梭。

# backend/meeting/views.py from rest_framework import viewsets, permissions, status from rest_framework.response import Response from django.db.models import Q from datetime import timedelta from .models import MeetingRoom, Reservation from .serializers import MeetingRoomSerializer, ReservationSerializer class ReservationViewSet(viewsets.ModelViewSet): queryset = Reservation.objects.all() serializer_class = ReservationSerializer def get_permissions(self): if self.action in ['create', 'list', 'retrieve']: return [permissions.IsAuthenticated()] return [permissions.IsAdminUser()] def perform_create(self, serializer): start = serializer.validated_data['start_time'] end = serializer.validated_data['end_time'] room = serializer.validated_data['room'] if start >= end: return Response({'detail': '开始时间必须早于结束时间'}, status=status.HTTP_400_BAD_REQUEST) # 查询同一会议室时间重叠的已通过预约 conflict = Reservation.objects.filter( room=room, status=1, start_time__lt=end, end_time__gt=start ).exists() if conflict: raise serializers.ValidationError('该时间段已被预约') serializer.save(user=self.request.user, status=0) def list(self, request, *args, **kwargs): # 支持按日期过滤 date = request.query_params.get('date') if date: self.queryset = self.queryset.filter(start_time__date=date) return super().list(request, *args, **kwargs)

这个视图里藏着三个关键点。get_permissions区分了普通用户和管理员的权限边界:创建、查看预约登录即可,修改、删除预约(通常是审批操作)必须是管理员。冲突检测用的是区间重叠判断条件start_time__lt=end, end_time__gt=start,这个条件覆盖了四种重叠场景:新预约完全落在已有预约内、已有预约完全落在新预约内、新预约开始时间在已有预约中间、新预约结束时间在已有预约中间。filter(status=1)排除了已拒绝和已取消的预约,避免脏数据干扰。最后通过serializer.save(user=self.request.user, status=0)把当前登录用户写进预约记录,状态默认置为待审批。

3.2 小程序登录鉴权与 token 处理

小程序端的登录流程和网页登录不一样。前端调用wx.login()拿到临时code,传给后端,后端拿code去微信接口换openidsession_key。毕设项目不需要维护真实微信支付或消息推送,所以拿到openid后通常直接创建或匹配本地用户,签发一个自定义 token 返回给前端。

# backend/user/views.py import requests from django.conf import settings from django.contrib.auth.models import User from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import AllowAny import hashlib class WxLoginView(APIView): permission_classes = [AllowAny] def post(self, request): code = request.data.get('code') if not code: return Response({'detail': '缺少 code'}, status=400) # 向微信接口换取 openid url = 'https://api.weixin.qq.com/sns/jscode2session' params = { 'appid': settings.WX_APPID, 'secret': settings.WX_SECRET, 'js_code': code, 'grant_type': 'authorization_code' } resp = requests.get(url, params=params).json() if 'openid' not in resp: return Response({'detail': '微信登录失败', 'error': resp}, status=400) openid = resp['openid'] # 查找或创建本地用户 user, created = User.objects.get_or_create( username=f'wx_{openid[:20]}', defaults={'first_name': '微信用户'} ) # 生成简单 token(毕设场景够用) token = hashlib.md5(f'{openid}{user.id}'.encode()).hexdigest() return Response({'token': token, 'user_id': user.id, 'is_new': created})

这里用get_or_createopenid查找用户,避免重复注册。token 生成方式在商用项目里太简陋,但毕设答辩时你可以主动说明这只是演示,正式环境应换成 JWT 或 DRF 自带的 TokenAuthentication。在settings.py里配置 REST_FRAMEWORK 时,默认认证类改为 TokenAuthentication,前端每次请求在 header 里带上Authorization: Token <token>就行。WX_APPIDWX_SECRET放到settings.py里,不要在代码里硬编码,答辩时这也是一个可说的安全考量。

3.3 管理后台的审批操作

审批通过或拒绝预约,通常放在 Django admin 后台操作,或者在管理端小程序里提供一个审批接口。如果走 admin,需要在admin.py里做简单的注册;如果走接口,需要在ReservationViewSet里加一个自定义 action。

# backend/meeting/views.py from rest_framework.decorators import action class ReservationViewSet(viewsets.ModelViewSet): @action(detail=True, methods=['post']) def approve(self, request, pk=None): reservation = self.get_object() reservation.status = 1 reservation.save() return Response({'detail': '已通过', 'status': 1}) @action(detail=True, methods=['post']) def reject(self, request, pk=None): reservation = self.get_object() reservation.status = 2 reservation.save() return Response({'detail': '已拒绝', 'status': 2})

在 admin 里注册则更直观:

# backend/meeting/admin.py from django.contrib import admin from .models import MeetingRoom, Reservation @admin.register(MeetingRoom) class MeetingRoomAdmin(admin.ModelAdmin): list_display = ('name', 'location', 'capacity', 'is_active') @admin.register(Reservation) class ReservationAdmin(admin.ModelAdmin): list_display = ('title', 'user', 'room', 'start_time', 'end_time', 'status') list_filter = ('status', 'room') date_hierarchy = 'start_time' actions = ['make_approved', 'make_rejected'] def make_approved(self, request, queryset): queryset.update(status=1) def make_rejected(self, request, queryset): queryset.update(status=2)

date_hierarchy会在 admin 列表页顶部生成一个按日期筛选的层级导航,对会议室预约这种强时间属性的数据非常实用。自定义 actions 让你在勾选多条预约后一键批量审批。对于毕设展示,建议在答辩前预先造好几条不同状态的预约数据,现场审批操作时页面不会空空荡荡。

3.4 定时清理过期预约

还有一个容易被忽略的逻辑:会议时间已经过去的预约,状态不应该一直停留在"待审批"或"已通过"。常见做法是写一个 Django management command,定时将end_time < now()的记录标记为"已完成"或直接逻辑删除。但毕设项目如果没要求,也可以不处理,靠前端判断时间来变灰显示历史记录。如果想加分,可以加一个简单的定时任务:

# backend/meeting/management/commands/clean_expired.py from django.core.management.base import BaseCommand from django.utils import timezone from meeting.models import Reservation class Command(BaseCommand): help = '将已结束的预约标记为已完成' def handle(self, *args, **options): expired = Reservation.objects.filter( end_time__lt=timezone.now(), status__in=[0, 1] ).update(status=3) self.stdout.write(f'已清理 {expired} 条过期预约')

然后用系统 crontab 或 Celery beat 定时执行。毕设项目不需要引 Celery,直接写一个 crontab 条目调python manage.py clean_expired就够了。这样既展示了你对 Django management command 的熟悉,又避免了项目过度设计。

4. 小程序端从零联调:登录、预约列表与表单提交

小程序端技术栈通常是原生微信小程序,也有用 uniapp 写的。原生小程序更贴近毕设课程所学,uniapp 的组件和生命周期与 Vue 类似,这里以原生为例。拿到小程序源码后,第一件事是改project.config.json里的appid,并确认url请求域名指向本地开发服务器。

4.1 开发者工具配置与本地调试

微信开发者工具打开项目后,点右上角"详情",在"本地设置"里勾选"不校验合法域名"。否则请求http://127.0.0.1:8000会被拦截。接着把小程序端封装的request工具打开,统一设置 baseURL。

// miniprogram/utils/request.js const BASE_URL = 'http://127.0.0.1:8000/api'; const request = (url, method = 'GET', data = {}) => { return new Promise((resolve, reject) => { wx.request({ url: `${BASE_URL}${url}`, method, data, header: { 'Content-Type': 'application/json', 'Authorization': `Token ${wx.getStorageSync('token')}` }, success: (res) => { if (res.statusCode === 401) { wx.navigateTo({ url: '/pages/login/login' }); reject(res); } else { resolve(res.data); } }, fail: reject }); }); }; module.exports = { request };

封装时把 token 从wx.getStorageSync取出来放进 header,这样所有接口自动携带认证信息。401状态码统一跳登录页,自己的业务代码里不用再重复判断登录态。注意代码里没有处理 token 过期后的刷新逻辑,毕设答辩时如果老师问起来,回答"目前采用重新登录策略,简单有效"即可。

4.2 登录页与首页会议室列表

登录页的思路是:用户进入小程序后,先通过wx.login获取 code,调用后端/api/wx-login/接口,成功后拿到 token 存起来,再跳转首页。首页获取会议室列表,展示名称、容量和设备标签。

// miniprogram/pages/login/login.js Page({ handleLogin() { wx.login({ success: async (res) => { const { code } = res; const data = await request('/wx-login/', 'POST', { code }); wx.setStorageSync('token', data.token); wx.setStorageSync('userId', data.user_id); wx.switchTab({ url: '/pages/index/index' }); } }); } });

app.jsonLaunch里也可以先静默调用wx.login,然后根据本地是否有 token 决定跳转逻辑。但要注意,wx.login每次生成的 code 只能用一次,后端换取 openid 后失效,所以不能反复调用。更合理的流程是检查wx.getStorageSync('token'),有就直接沿用,没有才调登录接口。

首页会议室列表的数据流很简单:onLoad里调request('/rooms/'),拿到数组后setData。这里有个体验细节:会议室列表项不是简单的卡片,要显示"空闲中 / 使用中"状态,但这个状态不能由前端算,应该在后端接口里直接返回。可以给MeetingRoomSerializer加一个current_status字段,动态查询当前时间是否有已通过的预约。

# backend/meeting/serializers.py from django.utils import timezone from datetime import timedelta class MeetingRoomSerializer(serializers.ModelSerializer): current_status = serializers.SerializerMethodField() class Meta: model = MeetingRoom fields = ['id', 'name', 'location', 'capacity', 'has_projector', 'has_whiteboard', 'current_status'] def get_current_status(self, obj): now = timezone.now() booking = Reservation.objects.filter( room=obj, status=1, start_time__lte=now, end_time__gte=now ).first() if booking: return f"使用中({booking.title})" return "空闲"

这个写法在会议室数量少时没问题,如果会议室很多、预约很密集,N+1 查询会拖慢接口。优化方式是先查出当前所有使用中的预约,再用字典分组映射到会议室,但毕设数据量小,直接查也不慢。答辩时老师问性能优化,你能接上这个话题就是加分项。

4.3 预约表单的时间选择与提交

预约页是另一个核心页面。它需要选择会议室、填写会议主题、参会人数、开始时间和结束时间。原生小程序里没有好用的日期时间选择器组件,最省事的方案是用pickermode="date"mode="time"分开选,前端把日期和时间拼接后提交给后端。

// miniprogram/pages/book/book.js Page({ data: { roomId: '', title: '', date: '2024-06-01', startTime: '09:00', endTime: '10:00', }, submitBooking() { const { roomId, title, date, startTime, endTime } = this.data; if (!roomId || !title) { wx.showToast({ title: '请补全信息', icon: 'none' }); return; } request('/reservations/', 'POST', { room: roomId, title, start_time: `${date}T${startTime}:00`, end_time: `${date}T${endTime}:00`, participants: 1 }).then(() => { wx.showToast({ title: '提交成功' }); setTimeout(() => wx.navigateBack(), 1500); }).catch((err) => { wx.showToast({ title: err.detail || '预约失败', icon: 'none' }); }); } });

提交后端的start_time格式是 ISO 8601 无时区字符串。Django 的 DateTimeField 能直接解析2024-06-01T09:00:00这种格式。时区处理上有个隐患:前端传的是北京时间,Django 在USE_TZ=True时会把无时区的时间按TIME_ZONE设置(Asia/Shanghai)解释并转成 UTC 存储。你从数据库里查出来再序列化输出时,DRF 会转回Asia/Shanghai的时间。只要TIME_ZONE是上海,整条链路时间显示是自洽的。

4.4 我的预约列表与取消操作

我的预约列表通常展示当前用户的所有记录,按状态分类。小程序端常见做法是顶部放几个 tab,分别切"待审批 / 已通过 / 已拒绝 / 历史"。这个页面调/reservations/?user=当前用户ID并按状态过滤。不过上面的ReservationViewSet默认返回全部用户的数据,需要重写get_queryset

# backend/meeting/views.py class ReservationViewSet(viewsets.ModelViewSet): def get_queryset(self): user = self.request.user if user.is_staff: return Reservation.objects.all() return Reservation.objects.filter(user=user)

这样普通用户只能看到自己的预约,管理员在后台看全部。取消预约在前端是一个按钮,调接口把状态改成 3。这里需要注意,已经通过审批的预约不建议随意允许取消,至少应该在取消时判断当前时间距离开始时间是否超过某个阈值(比如 30 分钟)。完全不加限制的取消逻辑在真实场景会被人薅羊毛,答辩时这通常是一个追问点。

5. 部署上线与毕设答辩的 8 个高频追问

项目在本地跑通只完成一半。部署到 Linux 服务器上,以及应对答辩时的追问,需要提前准备。这一章把部署命令、常见报错和答辩话术压缩成可以直接照搬的要点。

5.1 用 uWSGI + Nginx 部署 Django 后端

毕设项目的部署没有统一标准,但对 Django 应用来说,uWSGI + Nginx的组合最常见。你要在服务器上准备好 Python 虚拟环境、MySQL、Nginx,然后按顺序执行下面这套流程。先把整个后端代码上传到服务器,比如放到/srv/meeting/backend,然后创建虚拟环境并安装依赖。

cd /srv/meeting/backend python3 -m venv venv source venv/bin/activate pip install -r requirements.txt # 收集静态文件 python manage.py collectstatic --noinput # 启动 uWSGI 测试配置 uwsgi --http :8000 --module backend.wsgi --static-map /static=/srv/meeting/static &

collectstatic会把 admin 后台的静态文件统一收集到STATIC_ROOT目录,否则 Nginx 无法代理 CSS 和 JS,后台页面是裸的。简单验证 uWSGI 能启动后,把它改写成配置文件方式,再让 Nginx 反向代理到 uWSGI 的 socket。Nginx 配置里两个关键块:location /转给 uWSGI,location /static直接指向本地静态目录。

server { listen 80; server_name your_domain_or_ip; location / { include uwsgi_params; uwsgi_pass unix:/tmp/meeting_uwsgi.sock; } location /static { alias /srv/meeting/static; } }

改完 Nginx 配置后执行nginx -t检查语法,然后systemctl reload nginx。如果页面能打开但 CSS 丢失,问题基本都出在static的 alias 路径没有对齐STATIC_ROOT。另一个常见坑是uwsgi_pass用的是 socket 文件,如果 uWSGI 进程没有权限写 socket,Nginx 会返回 502。把 socket 路径放在/tmp下,并确保 Nginx 工作进程(通常是www-data)能访问它。

5.2 服务器上的常见报错处理

部署过程中最容易遇到的三个报错:MySQL 连不上、静态文件 404、Django 无法解析微信接口的 HTTPS 证书。第一个问题检查 MySQL 用户权限是否允许远程连接,配置里HOST是否是127.0.0.1。第二个问题按上面 Nginx 静态路径排查。第三个问题表现为调用jscode2session时抛 SSL 证书验证错误,有两种解法:给服务器安装ca-certificates,或在requests.get里传verify=False,但后者会有安全隐患。

# 检查 uWSGI 进程是否在跑 ps aux | grep uwsgi # 查看 Nginx 错误日志 tail -f /var/log/nginx/error.log # 查看 MySQL 监听端口 netstat -tlnp | grep 3306

这些命令在答辩现场排错时能派上用场。老师的追问往往不是让你现场改代码,而是看你遇到问题时有没有排查路径。有日志定位意识、有原理层面的解释,比背配置更显专业。

5.3 答辩高频问题与你的回答框架

最后一个阶段,把项目说明文档浓缩成一套回答框架。老师的提问通常围绕系统安全、技术选型和数据一致性展开。不需要背标准答案,但要能用自己的话把做过的实现讲清楚。

第一个问题大概率是"为什么选 Django 而不是 Flask 或 Spring Boot"。这个问题不要只说"Django 大而全",要结合具体场景:会议室预约系统需要后台管理和 API 并行提供,Django admin 自带管理界面,DRF 让 REST API 的开发效率高,认证系统开箱即用。对比 Flask 需要自己整合 SQLAlchemy 和 Flask-Login,Django 天然契合这种"管理端 + 接口端"双重需求。

第二个高频问题是"预约冲突是怎么避免的"。你别只答"查询重叠时间",要补充说明事务边界。上面perform_create里的冲突检测和save之间其实存在一个微小的竞态窗口:两个请求同时进来,都查到没有冲突,然后都插入成功。要彻底解决需要给Reservation表加数据库层的唯一约束,比如用条件唯一索引或者把会议室 ID 和时间范围纳入联合唯一索引。MySQL 5.7 不支持函数索引,常见做法是用一个冗余字段存"时间段哈希值",插入前计算哈希,再联合唯一索引兜底。源码里通常没做这一步,如果你的源码也没做,就老实说"当前应用层检测在单机并发下够用,极端并发需要数据库约束兜底",这个回答已经超过大部分毕设水平。

第三个问题是"token 为什么不用 JWT"。可以回答"JWT 无状态,但存在过期撤销困难的问题;当前 TokenAuthentication 存储在服务端,管理员能直接禁用某个用户,毕设场景更可控"。第四个问题常是"如何测试接口",你只需要说清楚用postmanapifox验证了登录、创建预约、审批三条主链路,并补充用django.test写了冲突检测的单元测试。没写单元测试千万别编,直接说"时间所限只做接口级验证"即可,但建议在答辩前补一个测试文件。

最后一个问题很可能是"项目有哪些可以改进的地方"。主动抛出两点,牢牢掌握话语权:第一,预约冲突加数据库层约束,保证并发安全;第二,引入 Celery 定时任务,用 Redis 作为 broker,在预约开始前通过微信订阅消息提醒用户。这两个改进方向既结合了系统现状,又不会让人觉得你是在画饼——它们基于这个项目的真实短板,老师只会觉得你对自己的项目边界想得很清楚。

至此,整套流程从源码结构、数据模型、API 实现,到小程序联调和部署答辩都有了明确路径。拿到任何一份会议室预约的 Django 毕设源码,照着这条链路走一遍,该知道的细节都能覆盖到。另一个实用的做法是,把每轮报错时终端里出现的堆栈信息截图存好,这本身就是答辩时可以放出来的调试证据。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询