去年帮一个学弟收拾“Django基于大数据技术的互联网问诊系统”的时候,我第一眼看到的是:博客能跑,注册能走,页面一查,麦说大数据的地方只有一个写着“超大数据中心”的静态图片。我问他的大数据指什么,他想了半天,说数据库里能存几万条患者记录。我没批评他,因为照着网传的速成课赶完的作品,十有八九都是这样。真正伤脑筋的是答辩那关,评阅老师随便问一句“数据从哪来、往哪去、分析之后干了什么”,整个项目就露馅了。
那天起我意识到,这类标题里带着“源码+全套教程打包送”的毕设项目,核心套路并不复杂,但要把每一步讲清楚、做扎实,让系统不仅能跑、能查、能推,还能在答辩现场经得住提问,就值得认真复盘一遍。这篇东西我按完整项目实操来写,从选型、模型、查询、实时推送,到大数据分析怎么落地、源码拿到手怎么改,再到答辩现场最容易被追问的问题,全部过一遍。不管是第一次摸Django的新手,还是已经能写几个页面的同学,这条路径都能直接套用。
1. 项目设计与思路拆解
1.1 为什么在线问诊系统用 Django 最合适
毕设选型首先要回答一个问题:技术栈能不能用最少的时间把业务做完,同时让答辩有得讲。
在线问诊系统不是纯展示型网站,它有真实的业务流:患者注册、医生入驻、预约问诊、填写病历、开处方、诊后评价、消息提醒,还要叠加一部分运营数据看板。这种“快速多张业务表+后台管理+接口交互”的项目,Django几乎是现成的答案。
Django自带Admin后台,这意味着你不用从零写一套管理端界面,医生资料、科室分类、问诊记录都能直接在后台维护。这一点在毕设排期里是巨大的时间红利。其次是Django的ORM,它把数据库操作封装成对象方式,新手不必手写SQL,只需要定义数据模型,就能完成建表和查询。模型相当于数据库表的结构说明,跑一次迁移命令,表就自动生成了。
另一个隐藏优势是Django生态里有大量现成模块:用户认证、权限分组、表单验证、分页、消息框架,几乎覆盖了一个课程设计百分之七十的基本需求。与其开着Spring Boot的配置地狱,或者用Flask自己组装中间件,不如一套Django全拎走,把精力放到业务和大数据链路上。
如果你愿意更进一步,用Django Unfold这类后台美化方案,还能给管理界面加分。很多免费源码项目只给你一个默认的Django admin,稍显朴素。答辩时老师看到清爽的定制后台,印象分会明显不一样。
1.2 大数据不是一个概念,而是一条数据链路
“基于大数据技术”这几个字,经常被人当成一句装饰。实际上在毕设里,它应该是完整的数据处理链路:采集、存储、清洗、分析、可视化、辅助决策。
放到问诊系统里,具体是这样:
- 采集层:用户有哪些行为?什么时候登录、搜索了什么症状、浏览了哪个医生主页、咨询了多长时间、提交了哪些表单。
- 存储层:业务数据进MySQL或PostgreSQL,行为日志和行为明细可以单独建表,方便后续分析。
- 清洗层:把空字段、异常值、重复提交记录处理掉,比如把时间戳统一格式,把“未知性别”归一化。
- 分析层:用Pandas做快速统计,或者用Spark跑离线任务,计算出高峰时段、热门科室、医生负载、病情分布等指标。
- 展示层:把分析结果用ECharts或Grafana做成图表,放进数据大屏页面。
你无需真的搭三台节点的Hadoop集群,那对本科毕设来说是巨大的运维负担。更合理的做法是把“大数据技术原理”用在合适的位置:用Spark做一次离线分析,用Pandas做数据预处理,用Redis做缓存和实时推送的消息中间件。答辩的核心是讲清楚链路,而不是吹集群规模。
这也是我后来帮他重构时反复说的:数据链路通了,技术的价值就显出来了。你不需要每一次查询都走Spark,但你要让评委看到你知道分析型任务和事务型任务的区别。
1.3 先把功能边界拆出来
不要再纠结“这个系统到底还缺什么功能”,先画清边界。
我的建议是拆成6个模块:
| 模块 | 功能点 | 说明 |
|---|---|---|
| 用户管理 | 注册、登录、角色区分 | 患者、医生、管理员三种角色 |
| 医生管理 | 科室、职称、排班、擅长领域 | 后台可维护,前端展示 |
| 预约与问诊 | 在线预约、文字问诊、上传图片 | 核心业务流 |
| 病历处方 | 病历记录、处方开药、查看历史 | 外键关系复杂,重点设计 |
| 消息通知 | 预约成功、新问诊提醒、医生回复 | WebSocket实时推送 |
| 数据看板 | 访问量、热门科室、高峰时段、医生负载 | 大数据分析结果展示 |
这种边界划分会直接指导后面的数据表数量。业务不臃肿,才能写得完。很多同学的误区是一上来就加“在线支付、视频问诊、AI辅助诊断”,这些功能一旦接第三方SaaS或者训练模型,就会占用大量时间,最后连基础流程都没跑顺。
毕设项目评判标准不是功能越多越好,而是链路完整、逻辑清晰、能自圆其说。
2. Django 项目骨架与 ORM 实操
2.1 从“django创建app”到第一张业务表
创建项目的标准流程并不复杂,但很多人卡在第一步:把代码写进错误的位置。
我是这么做的:
python -m venv venv source venv/bin/activate # Windows环境用 venv\Scripts\activate pip install django channels channels-redis daphne django-admin startproject medical_platform cd medical_platform python manage.py startapp users python manage.py startapp doctor python manage.py startapp consult python manage.py startapp analysis这里的前提是项目名叫medical_platform,下面拆了四个app。你要注意,app的名字一定别用太通用的模块名,比如test、util,容易和第三方库冲突,迁移时也容易乱。
“django创建app”看似是背命令,但背后有设计逻辑:每一个app负责一个领域。不要在users里写医生的全部逻辑,也不要把问诊记录塞进analysis模块。领域拆得清楚,后面维护成本会成倍下降。
每次建完app,记得在settings.py的INSTALLED_APPS里注册。这一步漏掉的话,数据表不会生成,很多新手第一次跑迁移没建表,多半就是漏了注册。
完成之后,先建第一张模型感受一下:
from django.db import models class Department(models.Model): name = models.CharField(max_length=100, verbose_name="科室名称") description = models.TextField(blank=True, verbose_name="科室介绍") sort_order = models.IntegerField(default=0, verbose_name="排序") class Meta: db_table = "sys_department" verbose_name = "科室" ordering = ["sort_order", "id"] def __str__(self): return self.name这个表很简单,但要注意两个细节:一是db_table显式指定表名,避免Django默认生成的“app名_类名”在不同环境里不一致;二是Meta里设定了ordering,这样后台列表页和API查询都默认有序,省掉很多手工排序。
2.2 模型关系设计:患者、医生、问诊记录与权限
在线问诊系统的数据表设计,核心是一个轴心业务表“问诊记录”,它连接所有参与者。
假设你有这些字段:
from django.db import models from django.contrib.auth.models import User class PatientProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="patient_profile") real_name = models.CharField(max_length=50) gender = models.CharField(max_length=10, choices=[("M", "男"), ("F", "女"), ("O", "其他")]) birth_date = models.DateField(null=True, blank=True) phone = models.CharField(max_length=20, blank=True) class DoctorProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="doctor_profile") department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True) title = models.CharField(max_length=50) specialty = models.TextField(blank=True) is_online = models.BooleanField(default=False) class ConsultationRecord(models.Model): patient = models.ForeignKey(PatientProfile, on_delete=models.CASCADE, related_name="records") doctor = models.ForeignKey(DoctorProfile, on_delete=models.PROTECT, related_name="records") status = models.CharField(max_length=20, choices=[ ("pending", "待接诊"), ("ongoing", "问诊中"), ("finished", "已结束"), ("canceled", "已取消") ], default="pending") chief_complaint = models.TextField(verbose_name="主诉") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)这里故意用了三种不同的外键策略,值得单独讲:
- PatientProfile关联User用了CASCADE,意思是用户被删除,患者资料跟着删。这个逻辑符合账号注销场景。
- DoctorProfile关联Department用了SET_NULL,科室被删除时医生不删除,只是科室置空。这样科室调整不会影响医生。
- ConsultationRecord关联医生用了PROTECT,医生有关联问诊记录时禁止删除。这能保护医疗数据完整性,不会因为删一个医生实体把问诊记录全带走。
系统中还应该有“角色权限”。Django的auth自带User和Group,但问诊系统用通用很多。最简单的角色划分方式是:在Profile表中加role字段,或者在Django中给用户设分组。复杂一点的做法是把RBAC做出来,包括用户-角色-权限三级模型,后台里给不同角色分配菜单和操作权限。
毕设里呈现到RBAC这种程度,答辩老师会觉得你对权限理解到位了。实现也不难:
from django.contrib.auth.models import Group, Permission from django.contrib.auth.decorators import login_required, user_passes_test def is_doctor(user): return user.groups.filter(name="医生").exists()在视图上加装饰器,就能实现“患者只能访问自己的问诊记录”“医生只能访问分配给自己的患者问诊单”这样的控制。
2.3 django执行查询-删除对象:最容易被忽略的三个坑
搜关键词的时候,“django执行查询-删除对象”这个短语频繁出现,说明很多人就是在这里踩了坑。
第一个坑是QuerySet的“惰性求值”。ORM的查询集并不会在定义时立刻访问数据库,而是在迭代、取值或调用print之前,它都是一个“待执行”状态。
records = ConsultationRecord.objects.filter(status="pending") # 到这里还没有查库 # 一旦执行 list(records) 或者 first() 才会有SQL执行这个特性本身不是坏事,但新手容易在多次调用.count()和.filter()时忘了Django会重新触发SQL,导致性能看起来不稳定。解决方式是把需要复用的结果查询集直接转成list缓存,或者搞清楚自己只需要哪一条、哪一个范围。
第二个坑是删除对象的级联问题。很多人看到删除成功,没想到病历也被默认带走了。
patient = PatientProfile.objects.get(id=1) patient.delete() # 如果这个患者还有问诊记录,记录也会被CASCADE删掉如果是真实业务,绝对不应该这么做。问诊记录属于医疗数据,通常要保存几年以上。正确做法是“软删除”:在模型里加一个is_active字段或is_deleted字段,删除时只改状态,不真正移除数据。
class PatientProfile(models.Model): is_active = models.BooleanField(default=True) def soft_delete(self): self.is_active = False self.save(update_fields=["is_active"])第三个坑是批量删除时没有条件保护。比如你想清理三个月前的测试数据,写成了ConsultationRecord.objects.all().delete(),直接把线上数据全清掉。每个项目中,批量删除都必须先打印出受影响数量,再用事务包裹。
from django.db import transaction with transaction.atomic(): qs = ConsultationRecord.objects.filter(created_at__lt="2025-01-01") print("将会删除", qs.count(), "条") qs.delete()事务的好处是万一删错了还能回滚。这种代码写进项目里,比“删库跑路”的教育意义强太多。
3. 实时推送模块实现:后台有数据,前端立刻更新
3.1 医院场景里为什么非要上 WebSocket
问诊系统的业务天然适合实时推送:医生登录后台,患者刚提交问诊申请,页面要是需要反复手动刷新才知道,体验会很糟糕。同样,患者在看诊过程中,医生回复了消息,前端也需要立刻弹出提示。
常规轮询方案是前端每隔几秒钟发起一次HTTP请求去拉数据。这个方法能跑,但会造成大量无效请求,尤其在问诊高峰期,几十个用户同时轮询,数据库压力不小。WebSocket的优势在于建立一次长连接,后台有数据就主动推送,前端不需要反复问。
正则表达式的比喻是“打电话”和“翻朋友圈”的区别。HTTP轮询像你反复去朋友圈刷新看别人有没有更新,WebSocket像对方主动给你打电话通知。
在项目里用Django实现WebSocket,最稳定的配套是channels。它给Django带来了异步处理能力,让Django可以在长连接场景下工作。
3.2 Channels + Redis 实现消息推送的完整链路
完整链路分为四层:配置文件、ASGI路由、消息消费者、前台浏览器收消息。
第一步,在settings.py里注册channels,并配置Redis作为channel layer:
INSTALLED_APPS = [ ... "daphne", "channels", ] ASGI_APPLICATION = "medical_platform.asgi.application" CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("127.0.0.1", 6379)], }, }, }注意INSTALLED_APPS里要有daphne,否则Django默认用自带ASGI服务器,兼容性会差一些。
第二步,修改asgi.py:
import os from django.core.asgi import get_asgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "medical_platform.settings") from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from django.urls import path from consult.consumers import ConsultationConsumer django_asgi_app = get_asgi_application() application = ProtocolTypeRouter({ "http": django_asgi_app, "websocket": AuthMiddlewareStack( URLRouter([ path("ws/consult/<int:record_id>/", ConsultationConsumer.as_asgi()), ]) ), })第三步,写consumers.py:
import json from channels.generic.websocket import AsyncWebsocketConsumer class ConsultationConsumer(AsyncWebsocketConsumer): async def connect(self): self.record_id = self.scope["url_route"]["kwargs"]["record_id"] self.group_name = f"consult_{self.record_id}" await self.channel_layer.group_add(self.group_name, self.channel_name) await self.accept() await self.channel_layer.group_send( self.group_name, {"type": "notify.message", "message": "已进入问诊会话"} ) async def disconnect(self, close_code): await self.channel_layer.group_discard(self.group_name, self.channel_name) async def notify_message(self, event): await self.send(text_data=json.dumps({ "message": event["message"], "sender": event.get("sender", "system"), }, ensure_ascii=False))这段代码做了三件事:用户连接时加入名为consult_记录ID的消息组;断开时退出;收到群消息时推给当前浏览器。
第四步,后台某个视图或Celery任务处理完数据后,调用channel_layer推送:
from channels.layers import get_channel_layer from asgiref.sync import async_to_sync channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( "consult_5", { "type": "notify.message", "message": "医生已经接收您的问诊,请查看回复", "sender": "doctor", } )这样护士或系统在后台改一条记录、转诊一个患者,浏览器立即能弹通知。标题里那句“后台有数据,前端推送”,代码上就是这么闭环的。
3.3 网关层与部署阶段的常见翻车点
本地跑的时候,你必须用daphne而不是runserver才能完整处理WebSocket:
daphne -b 0.0.0.0 -p 8000 medical_platform.asgi:application如果测试时发现WebSocket一直连接不上,先检查三件事:
- Redis进程是否启动,用
redis-cli ping验证连通性。 - 浏览器控制台请求地址是否写成了ws://而不是wss://。
- 连接路径是否和asgi.py里配置的路由一致,多一个斜杠都不行。
生产部署时还会遇到超时和跨域问题。如果你的项目用了Nginx反向代理,300秒超时对WebSocket来说是一个不小的障碍。一定要单独给/ws/路径设置更长的超时时间,或者在Nginx配置里允许Upgrade。另外,如果前端和后端分离,注意跨域,connect阶段被浏览器拦截,页面日志不会报明显错误。
部署阶段不用追求高可用,但Redis重启策略、日志输出、异常重连机制要能讲出来。答辩问到“断网怎么办”,你说“客户端监听onclose,并做重连”就比“不知道”强很多。
4. 大数据技术在问诊系统里的落地路径
4.1 采集层:埋点、日志与用户行为表
大数据分析的第一步不是写算法,而是先把数据采集上来。很多毕设在这块是空的,评委问“数据怎么来的”,只能说“手动造的”。手动造不是不行,但你要造得合理,还要有采集逻辑支撑。
我建议在项目里增加一个用户行为埋点。最简单的方案是写一个中间件:
import time from django.utils import timezone class BehaviorLogMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): start = time.time() response = self.get_response(request) duration = int((time.time() - start) * 1000) if request.user.is_authenticated: from analysis.models import UserBehaviorLog UserBehaviorLog.objects.create( user=request.user, path=request.path, method=request.method, duration_ms=duration, created_at=timezone.now(), ) return response这样一个中间件能把每次请求的路径、耗时、访问时间都记录下来。数据量不大,但足够支撑“访问趋势”“最热页面”等分析。
更进一步,可以在搜索症状、查看医生主页、提交问诊申请这些关键动作里手动插入行为记录:
# 视图里 UserBehaviorLog.objects.create( user=request.user, path=request.path, action="search:doctor", detail=keyword, )给行为记录加action字段,可以区分页面浏览和按钮触发。这样数据采集层就不再是空话。
4.2 分析层:Pandas 快速分析,Spark 讲得清原理
有了行为表,分析层就开始变得有意义。我常用Pandas先把明细数据统计出结果,再用Spark做一轮更大体量的模拟数据分析,保证答辩时有“离线分析”的环节。
Pandas能做的分析:
import pandas as pd df = pd.read_csv("behavior_logs.csv", parse_dates=["created_at"]) df["hour"] = df["created_at"].dt.hour hour_group = df.groupby("hour").size().reset_index(name="count") print("访问高峰时段:", hour_group.sort_values("count", ascending=False).head(3))Spark版本的逻辑类似,但数据处理量更大,加了一个分布式计算的仪式感:
from pyspark.sql import SparkSession from pyspark.sql.functions import hour, count spark = SparkSession.builder.appName("consultation-analysis").getOrCreate() df = spark.read.csv("behavior_logs.csv", header=True, inferSchema=True) df.withColumn("hour", hour(df.created_at)) \ .groupBy("hour").count() \ .orderBy("count", ascending=False) \ .show(5)这个阶段的参数选择可以讲得很细:比如为什么用窗口函数统计医生负载,为什么要在非高峰时间跑批量任务。问诊系统里,分析任务不需要实时响应,凌晨用Spark跑一次前一天的全量数据,写入汇总表,白天的大屏和报表直接读取汇总结果。这是典型的Lambda架构简化版:事务数据走业务库,分析数据走离线批处理。
4.3 展示层:把分析结果变成答辩能看的可视化
光有数据表没有图表,无法直观给评委传达“我做了大数据分析”。展示层选用ECharts比较合适,它有Django模板可以直接嵌入的版本。
我做过一个简单的数据大屏,包含四个核心指标:
- 今日问诊量折线图,横轴小时,纵轴订单数。
- 热门科室Top5横向柱状图。
- 医生负载排行表。
- 患者地域来源分布饼图。
后端只需提供一个API让前端拿汇总数据:
from django.http import JsonResponse from analysis.models import DoctorLoadStat, HourlyConsultStat def dashboard_data(request): hour_data = list(HourlyConsultStat.objects.order_by("hour").values_list("hour", "consult_count")) top_doctors = list(DoctorLoadStat.objects.order_by("-consult_count")[:5].values("doctor_name", "consult_count")) return JsonResponse({"hour": hour_data, "top_doctors": top_doctors})前端用ECharts在页面里把这些数据渲染出来。答辩时这套流程就叫“数据链路闭环”:埋点采集、离线分析、结果存储、前端展示。数据量也许不算大,但分析思维已经体现了。
5. 源码救场:怎么把“免费项目”改成自己的作品
5.1 拿到源码先做三件套检查
先做三件套检查。第一看项目结构和依赖,是否有requirements.txt,是否用了虚拟环境;第二看数据库迁移文件,是否有完整的migrations;第三看README,是否写了启动步骤。缺任何一样,都要提高警惕。
免费的Django毕设源码千篇一律,你花一小时下载,可能还要花两天填坑。下面几个信号直接决定它的质量:
| 检查点 | 好源码的标志 | 烂源码的翻车现场 |
|---|---|---|
| requirements.txt | 版本号清晰 | 只有django,没有版本 |
| migrations目录 | 每张表都有迁移文件 | 完全没有,跑完迁移后模型对不上 |
| 静态文件 | 路径清晰 | 图片和CSS硬编码到C盘 |
| 配置文件 | settings用os.environ读取 | 把数据库密码写死在代码里 |
| 文档 | 有启动步骤 | 只有一个空README或广告 |
| 代码注释 | 关键逻辑有解释 | 代码缩写混乱,没有注释 |
| 数据库 | 有迁移或SQL备份 | 数据库文件有误或打不开 |
如果是你从网上下载的“万套教程打包送”,不要迷信压缩包的大小。先解压看代码,很多打包资源是几十个项目塞在一起,复用意义不大,能挑出一个可改造的骨架就是胜利。
5.2 我处理过的翻车现场和对应修法
翻车现场一:依赖冲突。旧项目用Django 2.2,你机器上是Python 3.11,一启动就报ImportError: cannot import name 'force_text' from 'django.utils.encoding'。解决方案不是硬降Python版本,而是先更新到Django 3.2或4.2兼容分支,再把force_text改成force_str。绝大部分老项目升级迁移只需要改那几个废弃API。
翻车现场二:数据库连接错误。源码默认配置是MySQL,但你连不上远程库。别直接改settings,先把数据库改成SQLite跑通本地:
DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3", } }本地跑通后,再考虑要不要上MySQL。很多同学一上来就踩MySQL认证插件版本问题,浪费大量时间。毕设优先跑通,再谈部署。
翻车现场三:静态文件全部404。模板能打开但没有样式,多半是STATIC_URL配置和静态目录不一致。打开settings检查:
STATIC_URL = "/static/" STATICFILES_DIRS = [BASE_DIR / "static"]模板里也要正确加载:
{% load static %} <link rel="stylesheet" href="{% static 'css/base.css' %}">如果还不行,用python manage.py collectstatic把静态文件收集到指定目录,再迁移部署流程。
5.3 顺手给后台换皮肤:Django Unfold 与 RBAC 权限
如果你拿到的源码默认后台长着Django自带的面孔,想提升观感,用Django Unfold是一个性价比很高的方案。它能快速把admin装饰成现代化卡片式界面,不需要改太多业务代码。
pip install django-unfold安装后在INSTALLED_APPS顶部加入unfold,并继承Unfold的ModelAdmin:
from django.contrib import admin from unfold.admin import ModelAdmin from consult.models import ConsultationRecord @admin.register(ConsultationRecord) class ConsultationRecordAdmin(ModelAdmin): list_display = ["id", "patient", "doctor", "status", "created_at"] list_filter = ["status", "created_at"]页面会立刻从“管理后台”变成“运营平台”的观感。这个加分项不用动业务逻辑,成本极低。
RBAC权限也可以在后台配合使用:创建“医生组”“患者组”“管理员组”,把模型的操作权限细分到不同角色。Django的Group和Permission本身就支持这个,你只需要在admin里创建并分配。
6. 实战问题排查与答辩避坑
6.1 高频故障排查速查表
长期做Django项目的人会怀念那些“跑一下就知道”的报错,但新手总是卡在同样几个地方。整理了一份高频故障表:
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| 迁移后表结构不对 | 忘记注册app | INSTALLED_APPS检查 |
| 页面样式全无 | 静态文件路径错误 | STATIC_URL与模板加载标签 |
| 登录后没权限 | 未分配组或session问题 | 检查数据库session表 |
| 多个对象同时更新异常 | 并发覆盖 | 用select_for_update()锁行 |
| Redis连接失败 | Redis未启动或密码错误 | redis-cli ping |
| WebSocket连不上 | asgi路由、端口或代理配置错误 | 控制台看握手状态 |
| 查询特别慢 | 缺少索引或没用select_related | 看SQL日志 |
| 批量删除误操作 | 没有事务保护 | 先统计count再删 |
| 后台管理样式旧 | 默认Django admin | 换Django Unfold |
实操心得:遇到问题不要急着改代码,先看控制台完整报错,尤其在Django项目里,日志和反向链路往往能直接定位整个问题的最关键上下文。
6.2 答辩现场常被追问的五道题
第一道:“你这个大数据分析用到哪些技术?”回答模板:采集层用了Django中间件和日志记录,分析层用了Pandas做数据清洗和聚合,同时用Spark模拟离线分析场景,结果写入汇总表供给可视化。重点强调“理解数据从哪来、清洗逻辑是什么、分析结果如何使用”。
第二道:“如果数据量增加到千万级,现在的方案能支撑吗?”不要硬说“能”。正确回答是:当前方案满足课程设计数据量级;如果要支撑千万级,会引入分区表、索引优化、消息队列异步写入、Spark集群化。评委会觉得你有扩展思维。
第三道:“WebSocket和HTTP请求的区别是什么?”对照HTTP短连接的特点,先说明轮询浪费资源,再说明WebSocket建立长连接、服务端主动推送,给出完整实现路径。
第四道:“删除一个患者,问诊记录会怎么样?”你说出CASCADE、PROTECT、SET_NULL三种策略的区别,并指出医疗记录应该避免物理删除。这道题是隐藏的送分题。
第五道:“你如何验证项目的可靠性?”可以提事务保护、软删除、日志审计、权限控制。如果再把代码里用过的select_for_update()、事务回滚逻辑讲出来,比背十页理论都有效。
6.3 用好数据表审计,让毕设项目“有灵魂”
毕设项目想从“工具”升级成“系统”,要害之一是审计。在线问诊涉及医患数据,操作留痕是必须的。
我在项目里加了三张审计表:
- 登录日志表:记录登录时间、IP、User-Agent。
- 操作审计表:记录谁在什么时间创建、更新、删除了哪条记录。
- 问诊流转表:记录从待接诊到结束的每一个状态变更。
状态流转表设计大概是:
class ConsultationFlow(models.Model): record = models.ForeignKey(ConsultationRecord, on_delete=models.CASCADE) from_status = models.CharField(max_length=20) to_status = models.CharField(max_length=20) operator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) remark = models.CharField(max_length=200, blank=True) created_at = models.DateTimeField(auto_now_add=True)每次问诊状态改变,就插入一条流转记录。这样答辩时,你可以现场演示一条记录从“待接诊”变为“问诊中”,后台审计表立刻多一条历史记录。评委看完之后,会对你的项目产生“能落地”的判断。
最后再分享一个经验:答辩前一夜,不要急着背稿子,而是把项目从零启动一遍,注册两个账号,扮演医生和患者,跑通一次完整的问诊流程。很多项目表面上有一堆功能,演示时却在最基础的登录环节翻车。你亲手把这条链路走通一遍,比任何花哨的新技术都更让人踏实。真到了现场,你就知道一个跑得顺的主流程,才是毕业设计最扎实的底气。