療掛號系統(tǒng)設計與實現(xiàn))
1. 醫(yī)療掛號管理系統(tǒng)概述醫(yī)療掛號管理系統(tǒng)是醫(yī)療機構信息化建設的基礎模塊它解決了傳統(tǒng)人工掛號模式效率低下、資源分配不均的問題。這個基于SpringBootVue的前后端分離系統(tǒng)采用了當前企業(yè)級開發(fā)的主流技術棧既能滿足實際醫(yī)院運營需求又非常適合作為計算機專業(yè)學生的畢業(yè)設計或課程設計選題。我在三甲醫(yī)院信息化建設項目中參與過類似系統(tǒng)的開發(fā)這類系統(tǒng)最核心的價值在于實現(xiàn)了三流合一患者流、醫(yī)生工作流和數(shù)據(jù)流的統(tǒng)一管理。通過線上掛號、分診、叫號等功能的數(shù)字化改造能使醫(yī)院門診效率提升40%以上同時減少患者平均等待時間。2. 系統(tǒng)架構設計2.1 技術選型解析后端技術棧SpringBoot 2.7.x簡化了傳統(tǒng)SSM框架的復雜配置內(nèi)置Tomcat服務器starter機制讓依賴管理更簡單MyBatis-Plus 3.5.x增強版ORM框架提供代碼生成器和豐富CRUD接口Redis 6.x用于緩存熱門科室信息和號源數(shù)據(jù)減輕數(shù)據(jù)庫壓力JWT實現(xiàn)無狀態(tài)認證適合分布式場景前端技術棧Vue 3.x組合式API開發(fā)更靈活配合TypeScript提升代碼質(zhì)量Element Plus提供豐富的UI組件加速界面開發(fā)Axios處理HTTP請求內(nèi)置請求攔截器ECharts 5.x可視化展示掛號量、科室流量等數(shù)據(jù)提示技術選型時特別注意版本兼容性例如SpringBoot 2.7.x與JDK 17的匹配Vue 3.x需要配套使用Vue CLI 5.x2.2 系統(tǒng)模塊劃分醫(yī)療掛號管理系統(tǒng) ├── 患者端功能 │ ├── 微信小程序掛號 │ ├── 科室醫(yī)生查詢 │ ├── 預約掛號 │ ├── 掛號記錄查詢 │ └── 就診評價 ├── 醫(yī)生端功能 │ ├── 排班管理 │ ├── 叫號系統(tǒng) │ ├── 病歷調(diào)閱 │ └── 處方開具 └── 管理端功能 ├── 科室管理 ├── 醫(yī)生管理 ├── 號源分配 ├── 數(shù)據(jù)統(tǒng)計 └── 系統(tǒng)監(jiān)控3. 數(shù)據(jù)庫設計與實現(xiàn)3.1 核心表結構患者表(patient)CREATE TABLE patient ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵, openid varchar(64) DEFAULT NULL COMMENT 微信openid, name varchar(32) NOT NULL COMMENT 姓名, id_card varchar(18) NOT NULL COMMENT 身份證號, phone varchar(11) NOT NULL COMMENT 手機號, avatar varchar(255) DEFAULT NULL COMMENT 頭像, status tinyint DEFAULT 1 COMMENT 狀態(tài)(0:禁用 1:正常), create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_id_card (id_card), UNIQUE KEY uk_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT患者信息;號源表(schedule)CREATE TABLE schedule ( id bigint NOT NULL AUTO_INCREMENT, dept_id bigint NOT NULL COMMENT 科室ID, doctor_id bigint NOT NULL COMMENT 醫(yī)生ID, work_date date NOT NULL COMMENT 排班日期, time_slot tinyint NOT NULL COMMENT 時段(1:上午 2:下午 3:晚上), total_num int NOT NULL DEFAULT 0 COMMENT 號源總數(shù), available_num int NOT NULL DEFAULT 0 COMMENT 剩余號源, fee decimal(10,2) NOT NULL COMMENT 掛號費, status tinyint DEFAULT 1 COMMENT 狀態(tài)(0:停診 1:正常), PRIMARY KEY (id), KEY idx_dept_doctor (dept_id,doctor_id), KEY idx_work_date (work_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT排班號源表;3.2 關鍵業(yè)務表關系主要業(yè)務關系患者與掛號記錄一對多醫(yī)生與排班一對多科室與醫(yī)生一對多掛號記錄與排班多對一4. 核心功能實現(xiàn)4.1 預約掛號流程RestController RequestMapping(/api/registration) public class RegistrationController { Autowired private ScheduleService scheduleService; Autowired private RegistrationService registrationService; PostMapping public Result register(RequestBody RegistrationDTO dto) { // 1. 校驗號源是否可用 Schedule schedule scheduleService.getById(dto.getScheduleId()); if (schedule null || schedule.getAvailableNum() 0) { return Result.fail(號源已約滿); } // 2. 分布式鎖防止超賣 String lockKey reg_lock: dto.getScheduleId(); try { boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (!locked) { return Result.fail(當前預約人數(shù)過多請重試); } // 3. 創(chuàng)建掛號記錄 Registration registration new Registration(); BeanUtils.copyProperties(dto, registration); registration.setStatus(0); // 待支付 registration.setOutTradeNo(IdUtil.simpleUUID()); registrationService.save(registration); // 4. 扣減庫存 scheduleService.deductAvailableNum(dto.getScheduleId()); return Result.ok(registration); } finally { redisTemplate.delete(lockKey); } } }4.2 叫號系統(tǒng)實現(xiàn)前端關鍵代碼(Vue3 WebSocket)// 建立WebSocket連接 const socket new WebSocket(wss://${location.host}/api/call) // 監(jiān)聽叫號消息 socket.onmessage (event) { const data JSON.parse(event.data) if (data.type CALL_NEXT) { // 更新叫號顯示 currentNumber.value data.number // 播放語音提示 playAudio(請${data.number}號到${data.room}就診) } } // 醫(yī)生點擊下一位按鈕 const callNext () { socket.send(JSON.stringify({ doctorId: doctor.value.id, deptId: doctor.value.deptId })) }后端WebSocket處理ServerEndpoint(/api/call) Component public class CallEndpoint { private static ConcurrentHashMapString, Session sessions new ConcurrentHashMap(); OnOpen public void onOpen(Session session) { String doctorId session.getRequestParameterMap().get(doctorId).get(0); sessions.put(doctorId, session); } OnMessage public void onMessage(String message, Session session) { JSONObject json JSON.parseObject(message); String doctorId json.getString(doctorId); String deptId json.getString(deptId); // 查詢下一個待就診患者 Registration next registrationService.getNextRegistration(deptId, doctorId); if (next ! null) { // 廣播叫號信息 broadcast(next.getQueueNumber(), next.getRoomNumber()); // 更新狀態(tài)為就診中 registrationService.updateStatus(next.getId(), 2); } } private void broadcast(String number, String room) { JSONObject message new JSONObject(); message.put(type, CALL_NEXT); message.put(number, number); message.put(room, room); sessions.values().forEach(session - { try { session.getBasicRemote().sendText(message.toJSONString()); } catch (IOException e) { log.error(發(fā)送消息失敗, e); } }); } }5. 典型問題與解決方案5.1 號源超賣問題問題現(xiàn)象高并發(fā)場景下同一號源被多個患者同時預約成功解決方案使用Redis分布式鎖如代碼示例所示數(shù)據(jù)庫樂觀鎖Update(update schedule set available_num available_num - 1 where id #{scheduleId} and available_num 0) int deductAvailableNum(Param(scheduleId) Long scheduleId);前端限制提交按鈕防重復點擊5.2 定時放號任務需求背景每天凌晨自動釋放未來7天的號源SpringBoot定時任務實現(xiàn)Slf4j Component public class ScheduleReleaseJob { Autowired private ScheduleService scheduleService; // 每天0點執(zhí)行 Scheduled(cron 0 0 0 * * ?) public void releaseSchedules() { log.info(開始執(zhí)行號源釋放任務); LocalDate startDate LocalDate.now().plusDays(1); LocalDate endDate LocalDate.now().plusDays(7); // 批量生成未來7天的號源 ListSchedule schedules new ArrayList(); ListDoctor doctors doctorService.listActiveDoctors(); for (LocalDate date startDate; !date.isAfter(endDate); date date.plusDays(1)) { for (Doctor doctor : doctors) { // 上午號源 schedules.add(buildSchedule(doctor, date, 1, 20)); // 下午號源 schedules.add(buildSchedule(doctor, date, 2, 15)); } } scheduleService.saveBatch(schedules); log.info(號源釋放完成共生成{}條記錄, schedules.size()); } private Schedule buildSchedule(Doctor doctor, LocalDate date, int timeSlot, int totalNum) { Schedule schedule new Schedule(); schedule.setDeptId(doctor.getDeptId()); schedule.setDoctorId(doctor.getId()); schedule.setWorkDate(date); schedule.setTimeSlot(timeSlot); schedule.setTotalNum(totalNum); schedule.setAvailableNum(totalNum); schedule.setFee(doctor.getRegistrationFee()); schedule.setStatus(1); return schedule; } }5.3 跨域問題處理Vue開發(fā)環(huán)境配置// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }SpringBoot跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .maxAge(3600); } }6. 項目部署指南6.1 后端部署Docker部署示例# Dockerfile FROM openjdk:17-jdk-slim VOLUME /tmp COPY target/medical-registration-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT [java,-jar,/app.jar]關鍵啟動參數(shù)java -jar -Dspring.profiles.activeprod \ -Dserver.port8080 \ -Dspring.datasource.urljdbc:mysql://mysql:3306/medical?useSSLfalse \ -Dspring.datasource.usernameroot \ -Dspring.datasource.password123456 \ app.jar6.2 前端部署Nginx配置示例server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }7. 畢設擴展建議智能推薦科室基于患者癥狀描述使用NLP技術推薦合適科室候診時間預測根據(jù)歷史數(shù)據(jù)預測當前患者的預計等待時間人臉識別簽到對接人臉識別API實現(xiàn)刷臉簽到醫(yī)保對接實現(xiàn)與醫(yī)保系統(tǒng)的對接需模擬接口大數(shù)據(jù)分析使用Spark分析掛號數(shù)據(jù)發(fā)現(xiàn)就診規(guī)律我在實際開發(fā)中發(fā)現(xiàn)掛號系統(tǒng)的并發(fā)控制是最容易出問題的環(huán)節(jié)特別是在上午8-10點的掛號高峰期。建議在畢設演示時使用JMeter模擬至少100并發(fā)用戶進行壓力測試這能充分體現(xiàn)系統(tǒng)的健壯性。