上周帮一个学弟看他的毕业设计,他选了一个“医疗预约系统”,用 PHP 做后端,VUE 做前端。他兴冲冲地给我看他的开题报告,里面列了十几个功能模块,从用户注册到后台统计,一应俱全。但当我问他:“你打算怎么处理号源冲突?如果医生临时停诊,已经预约的用户怎么通知?后台的排班数据量大了,页面卡顿怎么办?”他愣了一下,说这些还没想。
这不是个例。很多计算机专业的同学在做毕业设计时,容易陷入一个误区:把“功能列表”等同于“系统设计”。他们花大量时间在界面上拖拽组件,在数据库里建表,却很少去思考,一个真正能跑起来、逻辑自洽、并且能应对一些边界情况的系统,核心难点到底在哪里。
今天,我们就以这个典型的“PHP+VUE医疗预约系统”为例,抛开那些华而不实的表面功能,深入到代码和逻辑背后,聊聊如何把一个毕业设计从“纸上谈兵”的Demo,变成一个能让答辩老师眼前一亮、甚至能体现你工程思维的“准生产级”项目。你会发现,真正决定你毕设质量的,往往不是用了多炫的技术,而是你是否想清楚了那几个关键的业务闭环和异常处理。
1. 重新定义“医疗预约系统”:它不只是CRUD
当你拿到“医疗预约系统”这个题目时,第一反应可能是:用户表、医生表、科室表、预约表,然后就是增删改查(CRUD)。这没错,但这只是骨架。一个有用的预约系统,血肉在于其业务规则和状态流转。
1.1 核心业务实体与关系:比数据库表更重要的逻辑模型
在动手建表之前,先用纸笔画清楚以下几个核心实体的生命周期和它们之间的关系:
- 号源:这是整个系统的稀缺资源。它不是简单的一条数据库记录。一个号源应该包含:所属医生、所属科室、日期、时间段(如上午9:00-9:30)、总数量、剩余数量、状态(如“可预约”、“已约满”、“已停诊”)。
- 预约订单:用户对一个号源的锁定。关键字段包括:订单号、用户ID、号源ID、预约状态(如“待支付”、“已预约”、“已取消”、“已就诊”、“已过期”)、创建时间、支付时间(如果涉及)、取消时间。
- 医生排班:这是号源的生成器。排班规则可能包括:医生每周几出诊、每天出诊时段、每个时段放多少号、提前多少天放号。
它们之间的关系是动态的:
- 管理员根据医生排班规则,批量生成未来一段时间(如一周)的号源。
- 用户查看并锁定某个号源,系统生成一个状态为“待支付”或“已预约”的预约订单。
- 订单状态的变化(如取消、过期)会触发号源“剩余数量”的回滚或状态更新。
graph TD A[医生排班规则] -->|批量生成| B(未来号源池); B --> C{用户发起预约}; C -->|锁定号源| D[创建预约订单]; D --> E[订单状态: 待支付/已预约]; E -->|用户取消或超时未支付| F[释放号源回池]; E -->|支付成功或确认| G[订单状态: 预约成功]; G -->|就诊完成后| H[订单状态: 已完成];为什么这个模型重要?因为它直接决定了你后端接口的设计和前端交互的流程。比如,查询可预约号源时,你联查的不仅仅是doctor和schedule表,更重要的是source表中“状态可用且剩余数量>0”的记录。创建订单时,你必须在一个数据库事务内完成:1) 检查号源是否可用,2) 减少号源剩余数量,3) 创建订单记录。这三步必须原子性,否则就会出现超卖(一个号被两个人约)。
1.2 状态机:让系统逻辑变得清晰且健壮
订单和号源的状态不能随意更改。引入状态机思维是提升项目严谨性的好方法。
对于预约订单,状态流转可以这样设计:
待支付 --(用户支付/系统确认)--> 已预约 待支付 --(用户取消)--> 已取消 待支付 --(超时未支付,如15分钟)--> 已过期 已预约 --(用户就诊前取消)--> 已取消 已预约 --(用户完成就诊)--> 已完成 已预约 --(医生停诊,系统操作)--> 已取消(并触发退款或通知)关键实现:在PHP后端,不要简单使用UPDATE orders SET status='canceled'。你应该有一个OrderService类,里面提供cancelOrder($orderId, $reason)方法。这个方法内部会:
- 根据当前状态判断是否允许取消(例如,“已完成”的订单不能取消)。
- 执行状态变更。
- 如果取消成功且订单处于“已预约”状态,需要调用
SourceService来回滚对应号源的库存。 - 记录操作日志(谁、在何时、将订单从何状态改为何状态、原因是什么)。这张
order_logs表在答辩时是你考虑周全的有力证据。
1.3 容易被忽略的“非功能需求”
这些点不会出现在功能列表里,但却是区分普通设计和优秀设计的关键:
- 号源库存的并发控制:这是核心难点。当多个用户同时预约最后一个号源时,怎么保证不超卖?除了上文提到的数据库事务,还可以在事