1. 医疗预约系统项目概述
医疗预约系统是医疗机构数字化转型的核心组件,它解决了传统挂号模式中排队时间长、号源分配不均、信息不对称等痛点。这个基于Java实现的毕业设计项目,采用Spring Boot框架构建,完整实现了从患者端预约到医生端管理的全流程功能。
我在三甲医院信息化部门工作期间,亲眼见证了纸质挂号向线上预约的转型过程。一个设计良好的预约系统能提升30%以上的门诊效率,减少50%的窗口排队人数。这个毕设项目虽然规模不大,但完整包含了患者注册、科室选择、医生排班、预约时段锁定等核心业务逻辑,代码结构清晰,非常适合作为Java Web开发的练手项目。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术栈选型
采用经典的三层架构:
- 前端:Thymeleaf模板引擎 + Bootstrap
- 后端:Spring Boot 2.7 + Spring Security
- 数据库:MySQL 8.0
- 辅助工具:Lombok、PageHelper
选择Spring Boot而非传统SSM框架的原因:
- 内嵌Tomcat简化部署,特别适合毕设演示场景
- 自动配置机制减少XML配置工作量
- Starter依赖管理让项目更轻量化
- 与Spring Security天然集成,方便实现RBAC
2.2 数据库设计
核心表结构设计要点:
sql复制CREATE TABLE `doctor` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`dept_id` int NOT NULL COMMENT '所属科室',
`title` varchar(10) DEFAULT NULL COMMENT '职称',
`schedule` json DEFAULT NULL COMMENT '排班表',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `appointment` (
`id` bigint NOT NULL AUTO_INCREMENT,
`patient_id` int NOT NULL,
`doctor_id` int NOT NULL,
`time_slot` datetime NOT NULL COMMENT '预约时段',
`status` tinyint DEFAULT '0' COMMENT '0待确认 1已预约 2已取消',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `udx_doctor_time` (`doctor_id`,`time_slot`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
特别注意:
- 使用json类型存储医生动态排班信息
- 建立医生+时间段的唯一索引防止重复预约
- 时间字段精确到分钟粒度(如09:00-09:30)
3. 核心功能实现
3.1 预约冲突检测
关键算法实现:
java复制public boolean checkTimeSlotConflict(Integer doctorId, LocalDateTime startTime) {
// 获取医生该时段现有预约
List<Appointment> exists = appointmentMapper.selectByDoctorAndTime(
doctorId,
startTime,
startTime.plusMinutes(30));
// 检查排班规则
Doctor doctor = doctorService.getById(doctorId);
List<LocalTime> availableSlots = JSON.parseArray(
doctor.getSchedule().getString("times"),
LocalTime.class);
return !exists.isEmpty() ||
!availableSlots.contains(startTime.toLocalTime());
}
3.2 定时放号功能
使用Spring Scheduler实现每天8:00自动释放7天后号源:
java复制@Scheduled(cron = "0 0 8 * * ?")
public void autoReleaseSlots() {
LocalDate targetDate = LocalDate.now().plusDays(7);
List<Doctor> doctors = doctorService.list();
doctors.forEach(doctor -> {
JsonObject schedule = doctor.getSchedule();
List<LocalTime> times = parseTimeSlots(schedule);
times.forEach(time -> {
LocalDateTime fullDateTime = targetDate.atTime(time);
for(int i = 0; i < schedule.getInteger("maxPatient"); i++) {
Appointment slot = new Appointment();
slot.setDoctorId(doctor.getId());
slot.setTimeSlot(fullDateTime);
slot.setStatus(0);
appointmentMapper.insert(slot);
}
});
});
}
4. 安全与性能优化
4.1 防刷单机制
- 预约频率限制:
java复制@RateLimiter(value = 1, key = "#patientId")
public Appointment createAppointment(Integer patientId, Integer doctorId,
LocalDateTime timeSlot) {
// 业务逻辑
}
- 验证码校验:
html复制<div class="form-group">
<img th:src="@{/captcha}" onclick="refreshCaptcha()">
<input type="text" class="form-control" name="captcha" required>
</div>
4.2 高并发处理
- 使用Redis分布式锁:
java复制public boolean lockTimeSlot(String lockKey) {
String requestId = UUID.randomUUID().toString();
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
- 数据库乐观锁:
java复制@Update("update appointment set status=#{status}, version=version+1
where id=#{id} and version=#{version}")
int updateWithLock(Appointment appointment);
5. 部署与测试
5.1 环境配置建议
推荐使用Docker Compose快速搭建环境:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: medical
ports:
- "3306:3306"
redis:
image: redis:alpine
ports:
- "6379:6379"
5.2 压力测试方案
使用JMeter模拟并发预约:
- 配置200线程组,循环100次
- 添加HTTP请求头:
- Content-Type: application/json
- Authorization: Bearer ${token}
- 测试脚本示例:
json复制{
"doctorId": 1,
"timeSlot": "2023-12-01T09:00:00"
}
6. 项目扩展方向
- 微信小程序接入:
java复制@GetMapping("/wx/login")
public String wechatLogin(@RequestParam String code) {
String url = "https://api.weixin.qq.com/sns/jscode2session?"
+ "appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";
Map<String, String> params = new HashMap<>();
params.put("appid", wxConfig.getAppId());
params.put("secret", wxConfig.getSecret());
params.put("code", code);
return restTemplate.getForObject(url, String.class, params);
}
- 智能推荐算法:
python复制# 使用协同过滤推荐医生
def recommend_doctors(patient_id):
history = get_history_orders(patient_id)
similar_patients = find_similar_users(history)
return aggregate_recommendations(similar_patients)
7. 常见问题排查
- 时区问题:
在application.properties中添加:
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
- MyBatis映射异常:
java复制// 实体类添加注解
@Data
@TableName(autoResultMap = true)
public class Doctor {
@TableField(typeHandler = FastjsonTypeHandler.class)
private JsonObject schedule;
}
- 跨域配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3600);
}
}
这个项目我在实际部署时发现,医生排班表的JSON处理需要特别注意字段类型转换。建议使用Alibaba Fastjson的TypeReference来处理复杂嵌套结构:
java复制List<LocalTime> times = JSON.parseObject(
jsonString,
new TypeReference<List<LocalTime>>(){});
