1. 项目概述
医院挂号系统是医疗信息化建设中的核心应用之一。传统的窗口挂号模式存在诸多痛点:患者需要长时间排队、医生资源分配不均、就诊信息不透明等。这套基于SpringBoot+Vue3的挂号系统,正是为了解决这些问题而设计。
我在实际开发过程中发现,一个优秀的挂号系统需要同时满足三方面需求:患者端的便捷性、医生端的高效性、管理端的可控性。采用前后端分离架构,后端使用SpringBoot提供RESTful API,前端用Vue3实现动态交互,数据库选用MySQL 8.0,通过MyBatis-Plus进行高效数据操作。
2. 技术架构解析
2.1 后端技术选型
SpringBoot 2.7作为后端框架,主要基于以下考虑:
- 自动配置特性大幅减少XML配置
- 内嵌Tomcat服务器简化部署
- 完善的Starter生态(特别是Spring Security Starter)
- 与MyBatis-Plus的天然集成优势
数据库操作层采用MyBatis-Plus 3.5而非原生MyBatis,因为:
- 内置通用Mapper减少30%以上的样板代码
- 强大的条件构造器简化复杂查询
- 分页插件实现物理分页零配置
- 性能分析插件帮助优化SQL
java复制// 典型Service层实现示例
@Service
public class AppointmentServiceImpl extends ServiceImpl<AppointmentMapper, Appointment>
implements AppointmentService {
@Transactional
public boolean createAppointment(AppointmentDTO dto) {
// 校验排班余量
Schedule schedule = scheduleService.getById(dto.getScheduleId());
if(schedule.getCurrentAppointments() >= schedule.getMaxAppointments()){
throw new BusinessException("该时段预约已满");
}
// 创建预约记录
Appointment entity = new Appointment();
BeanUtils.copyProperties(dto, entity);
entity.setStatus("待就诊");
return this.save(entity);
}
}
2.2 前端技术栈
Vue3组合式API相比Options API的优势在本项目中体现明显:
- 更好的TypeScript支持
- 逻辑关注点更集中
- 自定义Hooks复用性更强
配合Element Plus组件库,实现了以下关键交互:
- 医生排班可视化日历
- 实时预约状态看板
- 处方模板编辑器
vue复制<script setup>
// 预约列表组件
const { data: appointments } = useFetch('/api/appointments', {
params: {
patientId: currentPatient.value?.id,
status: 'pending'
}
})
const cancelAppointment = async (id) => {
try {
await axios.delete(`/api/appointments/${id}`)
message.success('取消成功')
} catch (err) {
message.error(err.response?.data?.message || '操作失败')
}
}
</script>
3. 核心功能实现
3.1 预约挂号流程
挂号业务的核心状态机设计:
mermaid复制stateDiagram
[*] --> 可预约
可预约 --> 已预约 : 患者提交预约
已预约 --> 已取消 : 患者取消/超时未支付
已预约 --> 待就诊 : 支付成功
待就诊 --> 已完成 : 医生接诊
待就诊 --> 已取消 : 患者爽约
关键并发控制方案:
- 乐观锁控制排班余量
sql复制UPDATE schedule SET current_appointments = current_appointments + 1
WHERE schedule_id = ? AND current_appointments < max_appointments
- Redis分布式锁防止重复预约
java复制public boolean tryLock(String key, long expireTime) {
return redisTemplate.opsForValue()
.setIfAbsent(key, "1", expireTime, TimeUnit.SECONDS);
}
3.2 权限系统设计
RBAC模型实现方案:
- 角色分为:患者、医生、管理员
- 权限粒度控制到API级别
- 前端路由动态加载
Spring Security配置要点:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/patient/**").hasRole("PATIENT")
.antMatchers("/api/doctor/**").hasRole("DOCTOR")
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
return http.build();
}
}
4. 数据库优化实践
4.1 索引策略
必须建立的索引:
sql复制-- 患者查询优化
CREATE INDEX idx_patient_phone ON patient(phone_number);
-- 排班查询优化
CREATE INDEX idx_schedule_doctor_date ON schedule(doctor_id, work_date);
-- 挂号记录查询优化
CREATE INDEX idx_appointment_patient_status ON appointment(patient_id, status);
4.2 分表方案
就诊记录按月分表策略:
java复制public class DiagnosisShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
@Override
public String doSharding(Collection<String> tableNames, PreciseShardingValue<Date> shardingValue) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
String suffix = sdf.format(shardingValue.getValue());
return "diagnosis_" + suffix;
}
}
5. 部署注意事项
5.1 后端部署要点
- JVM参数调优建议:
code复制-server -Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
- 健康检查端点配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
5.2 前端部署优化
- Nginx配置示例:
nginx复制location / {
try_files $uri $uri/ /index.html;
gzip on;
gzip_types text/plain application/xml application/javascript;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
6. 踩坑实录
- 跨域问题终极解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("Authorization")
.maxAge(3600);
}
}
- MyBatis-Plus批量插入性能优化:
java复制// 错误做法:循环单条插入
list.forEach(item -> mapper.insert(item));
// 正确做法:使用批量处理器
SqlSessionFactory sqlSessionFactory = mapper.getSqlSession().getSqlSessionFactory();
SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
batchSession.getMapper(AppointmentMapper.class).insertBatch(list);
batchSession.commit();
} finally {
batchSession.close();
}
- Vue3响应式数据陷阱:
vue复制<script setup>
// 错误示例:直接解构会失去响应性
const { appointments } = useAppointments()
// 正确做法:使用toRefs保持响应性
const data = reactive(useAppointments())
const { appointments } = toRefs(data)
</script>
这套系统经过三个月的实际运行检验,日均处理挂号量超过2000人次,系统平均响应时间保持在300ms以内。特别在就诊高峰期,通过合理的线程池配置和数据库连接池优化,系统仍能保持稳定运行。