1. 项目背景与核心价值
作为一名经历过毕业设计洗礼的老程序员,我深知一个完整的医院挂号系统对计算机专业学生意味着什么。这不仅仅是一堆代码的堆砌,而是涵盖了前后端分离架构、数据库设计、业务流程实现等全栈技能的实战演练场。
这个基于SpringBoot+Vue+MySQL的线上医院挂号系统,其核心价值在于:
- 真实模拟三甲医院的挂号业务流程(科室选择→医生排班→时段预约→支付→订单管理)
- 采用企业级技术栈组合(SpringBoot后端+Vue前端+MySQL数据层)
- 完整覆盖毕业设计要求的文档体系(论文+部署手册+数据库设计说明书)
我去年指导过5个学生做类似项目,发现最大的痛点不是功能实现,而是如何让各模块有机衔接。比如Vue前端如何优雅地调用SpringBoot接口,MySQL的事务管理怎样保证挂号数据的强一致性,这些才是体现你技术深度的关键点。
2. 技术架构详解
2.1 后端SpringBoot设计要点
采用SpringBoot 2.7.x + MyBatis Plus组合,这是经过验证的稳定方案。关键配置在application.yml中需要特别注意:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/hospital?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
特别注意:MySQL 8.0+必须添加时区配置,否则会出现令人抓狂的时区异常。我在三个不同项目中都踩过这个坑。
核心模块划分建议:
- hospital-common(公共工具类)
- hospital-system(系统管理)
- hospital-scheduling(排班模块)
- hospital-register(挂号核心)
- hospital-payment(支付对接)
2.2 前端Vue工程实践
推荐使用Vue 3 + Element Plus组合,比传统方案更现代化。这些axios拦截器配置能帮你省去大量重复劳动:
javascript复制// request拦截器
service.interceptors.request.use(config => {
if (store.getters.token) {
config.headers['Authorization'] = 'Bearer ' + getToken()
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
Message.error(res.message || 'Error')
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
}
)
2.3 MySQL数据库设计精髓
挂号系统的核心表不超过10张,但关系复杂。这是经过实战检验的ER设计:
sql复制CREATE TABLE `doctor` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
`department_id` bigint NOT NULL COMMENT '科室ID',
`title` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '职称',
`introduction` text COLLATE utf8mb4_general_ci COMMENT '简介',
`avatar` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '头像URL',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `registration` (
`id` bigint NOT NULL AUTO_INCREMENT,
`patient_id` bigint NOT NULL,
`schedule_id` bigint NOT NULL COMMENT '排班ID',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待支付 1-已预约 2-已取消',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`payment_amount` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_schedule` (`schedule_id`),
KEY `idx_patient` (`patient_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
血泪教训:一定要给registration表建立复合索引,否则高峰期并发挂号时会引发锁表现象。我曾亲眼见证某三甲医院系统因此崩溃。
3. 核心业务逻辑实现
3.1 挂号锁号机制
这是系统最复杂的部分,需要处理高并发场景。采用Redis分布式锁+数据库乐观锁双保险:
java复制@Transactional
public String register(RegistrationDTO dto) {
// 1. Redis分布式锁
String lockKey = "reg_lock:" + dto.getScheduleId();
String uuid = UUID.randomUUID().toString();
try {
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, uuid, 30, TimeUnit.SECONDS);
if (!locked) {
throw new RuntimeException("当前号源正在被其他用户锁定");
}
// 2. 查询剩余号源
Schedule schedule = scheduleMapper.selectById(dto.getScheduleId());
if (schedule.getRemain() <= 0) {
throw new RuntimeException("号源已约满");
}
// 3. 乐观锁更新
int updated = scheduleMapper.updateRemain(
schedule.getId(),
schedule.getVersion(),
schedule.getRemain() - 1);
if (updated == 0) {
throw new RuntimeException("号源数量变更,请重试");
}
// 4. 创建挂号记录
Registration reg = new Registration();
BeanUtils.copyProperties(dto, reg);
registrationMapper.insert(reg);
return reg.getId().toString();
} finally {
// 释放锁时要判断是否自己的锁
if (uuid.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
3.2 排班模板设计
医生排班需要支持周期性生成,这是典型的策略模式应用场景:
java复制public interface ScheduleStrategy {
List<Schedule> generateSchedule(Doctor doctor, LocalDate startDate, LocalDate endDate);
}
@Component("weeklyStrategy")
public class WeeklyScheduleStrategy implements ScheduleStrategy {
@Override
public List<Schedule> generateSchedule(Doctor doctor, LocalDate startDate, LocalDate endDate) {
List<Schedule> schedules = new ArrayList<>();
LocalDate date = startDate;
while (!date.isAfter(endDate)) {
if (date.getDayOfWeek() == DayOfWeek.MONDAY
|| date.getDayOfWeek() == DayOfWeek.WEDNESDAY) {
Schedule am = new Schedule(doctor.getId(), date,
LocalTime.of(9, 0), LocalTime.of(12, 0), 20);
Schedule pm = new Schedule(doctor.getId(), date,
LocalTime.of(14, 0), LocalTime.of(17, 0), 20);
schedules.add(am);
schedules.add(pm);
}
date = date.plusDays(1);
}
return schedules;
}
}
4. 部署与调优实战
4.1 多环境配置技巧
使用Spring Profiles实现开发/测试/生产环境隔离:
properties复制# application-dev.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/hospital_dev
# application-prod.properties
server.port=80
spring.datasource.url=jdbc:mysql://prod-db:3306/hospital_prod?useSSL=true
spring.datasource.validationQuery=SELECT 1
spring.datasource.testWhileIdle=true
spring.datasource.timeBetweenEvictionRunsMillis=3600000
启动时通过命令行参数激活profile:
bash复制java -jar hospital.jar --spring.profiles.active=prod
4.2 Nginx前端部署配置
这是经过线上验证的Nginx配置模板:
nginx复制server {
listen 80;
server_name hospital.example.com;
location / {
root /usr/share/nginx/html/hospital;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /usr/share/nginx/html/hospital/static/;
expires 30d;
access_log off;
}
}
4.3 性能优化方案
根据JMeter压测结果,这三个优化最有效:
- 启用MySQL连接池(建议HikariCP配置)
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
- 添加二级缓存(Redis+Caffeine)
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(factory),
this.getRedisCacheConfiguration(3600), // 默认1小时
this.getRedisCacheConfigurationMap()
);
}
}
- 接口响应压缩
yaml复制server:
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,application/json,application/javascript
min-response-size: 1024
5. 毕业设计加分技巧
5.1 论文写作要点
技术选型章节要突出对比分析,例如:
"相比传统的SSM框架,SpringBoot的自动配置特性使本项目启动时间缩短了63%(从4.2秒降至1.5秒)。通过JProfiler分析,主要优化点在于避免了XML解析和大量反射操作..."
系统测试部分建议包含:
- 使用Postman的自动化测试集合
- JMeter并发测试报告(附吞吐量曲线图)
- Selenium UI自动化测试脚本
5.2 答辩常见问题准备
这三个问题被问到的概率超过80%:
-
"如何防止患者重复挂号?"
- 回答要点:数据库唯一索引(患者ID+排班ID)+ 前端防重复提交+ 后端接口幂等设计
-
"系统最大能支持多少并发?"
- 展示你的压测数据:如"在2核4G服务器上,挂号接口TPS达到120时响应时间仍保持在500ms以内"
-
"与现有商业系统相比的优缺点?"
- 客观分析:优点可能是技术新颖、代码结构清晰;缺点可以承认在容灾备份等方面还需完善
5.3 源码管理建议
Git提交记录要体现开发逻辑,例如:
code复制feat: 完成挂号锁号功能
fix: 解决Redis锁过期时间问题
docs: 更新API文档
refactor: 优化ScheduleService代码结构
使用.gitignore过滤掉不必要的文件:
code复制/target/
/node_modules/
/.idea/
*.iml
*.log
我在实际项目部署中发现,把Dockerfile也纳入版本控制能极大简化部署过程:
dockerfile复制FROM openjdk:11-jre
COPY target/hospital-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EXPOSE 8080
