SpringBoot+Vue医院预约挂号系统开发实战与微信小程序集成 这类医院预约挂号系统最值得关注的不是功能列表而是能不能在普通开发环境下稳定跑起来以及实际部署时挂号流程、数据一致性和微信接口的稳定性。我更建议把第一次搭建拆成三步环境准备、核心流程联调、微信小程序对接。下面按实际落地顺序拆解。1. 先确认技术栈选型和环境依赖医院预约挂号系统对数据一致性和实时性要求比较高技术栈选择直接影响后续开发效率。1.1 SpringBoot 3 Vue 3 的技术组合优势SpringBoot 3 需要 Java 17 或更高版本相比 SpringBoot 2 在性能和安全上有明显提升。Vue 3 的 Composition API 更适合复杂状态管理比如挂号过程中的科室选择、医生排班、时间选择等连锁操作。实际开发时我一般会先确认几个关键依赖版本// package.json 关键依赖 { vue: ^3.3.0, vue-router: ^4.0.0, axios: ^1.0.0, element-plus: ^2.3.0 }!-- pom.xml 关键依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency版本冲突是最常见的启动问题特别是 SpringBoot 3 与一些老版本组件的兼容性。1.2 微信小程序环境准备微信小程序开发需要先注册小程序账号获取 AppID。医院类小程序涉及用户隐私数据审核会比普通小程序更严格。开发工具建议用微信开发者工具稳定版调试基础库版本选择较新的稳定版本如 2.30.0。小程序端需要配置服务器域名包括request 合法域名后端 API 地址uploadFile 合法域名文件上传地址downloadFile 合法域名文件下载地址这些域名必须支持 HTTPS这是微信小程序的强制要求。2. 数据库设计和核心表结构挂号系统的核心是数据一致性特别是号源管理、预约状态和支付状态的一致性。2.1 主要业务表设计科室表departmentCREATE TABLE department ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 科室名称, description TEXT COMMENT 科室描述, status TINYINT DEFAULT 1 COMMENT 状态0-停用 1-启用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );医生表doctorCREATE TABLE doctor ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL COMMENT 医生姓名, department_id BIGINT NOT NULL COMMENT 所属科室, title VARCHAR(20) COMMENT 职称, specialty TEXT COMMENT 擅长领域, avatar VARCHAR(200) COMMENT 头像URL, status TINYINT DEFAULT 1 COMMENT 状态0-停诊 1-坐诊, FOREIGN KEY (department_id) REFERENCES department(id) );排班表scheduleCREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doctor_id BIGINT NOT NULL, work_date DATE NOT NULL COMMENT 工作日期, time_slot TINYINT NOT NULL COMMENT 时间段1-上午 2-下午 3-晚上, total_count INT DEFAULT 30 COMMENT 总号源数, reserved_count INT DEFAULT 0 COMMENT 已预约数, status TINYINT DEFAULT 1 COMMENT 状态0-停诊 1-正常, FOREIGN KEY (doctor_id) REFERENCES doctor(id), UNIQUE KEY uk_doctor_time (doctor_id, work_date, time_slot) );预约表appointmentCREATE TABLE appointment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, patient_id BIGINT NOT NULL COMMENT 患者ID, schedule_id BIGINT NOT NULL COMMENT 排班ID, order_no VARCHAR(32) UNIQUE COMMENT 订单号, status TINYINT NOT NULL COMMENT 状态0-已取消 1-待支付 2-已支付 3-已完成, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME COMMENT 支付时间, cancel_time DATETIME COMMENT 取消时间, FOREIGN KEY (schedule_id) REFERENCES schedule(id) );2.2 号源管理的并发控制挂号最核心的问题是号源超卖。当多个用户同时预约同一个医生的号源时需要保证 reserved_count 的准确更新。我一般用乐观锁解决这个问题// ScheduleRepository.java Modifying Query(UPDATE Schedule s SET s.reservedCount s.reservedCount 1 WHERE s.id :id AND s.reservedCount s.totalCount) int incrementReservedCount(Param(id) Long id); // 业务层使用 Transactional public boolean makeAppointment(Long scheduleId, Long patientId) { int updated scheduleRepository.incrementReservedCount(scheduleId); if (updated 0) { return false; // 号源已满 } // 创建预约记录 appointmentRepository.save(new Appointment(patientId, scheduleId)); return true; }这种方案比先查询再更新更安全避免了并发下的超卖问题。3. 后端 API 设计与关键实现后端采用 RESTful API 设计前后端分离架构。3.1 科室和医生查询接口RestController RequestMapping(/api/departments) public class DepartmentController { Autowired private DepartmentService departmentService; GetMapping public ResultListDepartmentVO getDepartments() { return Result.success(departmentService.getAllDepartments()); } GetMapping(/{deptId}/doctors) public ResultListDoctorVO getDoctorsByDepartment( PathVariable Long deptId, RequestParam(required false) String date) { return Result.success(departmentService.getDoctorsByDepartment(deptId, date)); } }3.2 排班查询和预约接口排班查询需要支持按日期、科室、医生等多维度筛选PostMapping(/schedules/query) public ResultPageResultScheduleVO querySchedules( RequestBody ScheduleQuery query) { return Result.success(scheduleService.querySchedules(query)); } PostMapping(/appointments) public ResultString createAppointment( RequestBody AppointmentCreateRequest request) { try { String orderNo appointmentService.createAppointment(request); return Result.success(orderNo); } catch (BusinessException e) { return Result.error(e.getMessage()); } }3.3 微信支付集成医院挂号通常涉及支付环节微信小程序支付是标配Service public class WechatPayService { public MapString, String createPayment(String orderNo, BigDecimal amount, String openid) { // 调用微信支付统一下单API MapString, String params new HashMap(); params.put(appid, appId); params.put(mch_id, mchId); params.put(nonce_str, generateNonceStr()); params.put(body, 医院挂号费); params.put(out_trade_no, orderNo); params.put(total_fee, amount.multiply(BigDecimal.valueOf(100)).intValue() ); params.put(spbill_create_ip, 127.0.0.1); params.put(notify_url, notifyUrl); params.put(trade_type, JSAPI); params.put(openid, openid); // 生成签名并调用接口 String sign generateSign(params); params.put(sign, sign); // 调用微信支付API解析返回结果 return invokeWechatPayAPI(params); } }支付回调处理要保证幂等性防止重复处理PostMapping(/pay/notify) public String handlePayNotify(RequestBody String notifyData) { // 验证签名 if (!verifySign(notifyData)) { return xmlreturn_code![CDATA[FAIL]]/return_code/xml; } // 解析支付结果 MapString, String result parseNotifyData(notifyData); String orderNo result.get(out_trade_no); // 查询订单状态避免重复处理 Appointment appointment appointmentService.findByOrderNo(orderNo); if (appointment.getStatus() ! AppointmentStatus.PENDING_PAYMENT) { return xmlreturn_code![CDATA[SUCCESS]]/return_code/xml; } // 更新订单状态 appointmentService.handlePaySuccess(orderNo); return xmlreturn_code![CDATA[SUCCESS]]/return_code/xml; }4. Vue 3 前端架构和状态管理前端采用 Vue 3 Vite Element Plus 的技术组合。4.1 项目结构和路由配置src/ ├── api/ # API接口 ├── components/ # 通用组件 ├── views/ # 页面组件 ├── store/ # 状态管理 ├── router/ # 路由配置 └── utils/ # 工具函数路由按功能模块划分// router/index.js const routes [ { path: /, redirect: /home }, { path: /home, component: () import(/views/Home.vue), meta: { title: 首页 } }, { path: /departments, component: () import(/views/DepartmentList.vue), meta: { title: 科室列表 } }, { path: /doctors, component: () import(/views/DoctorList.vue), meta: { title: 医生列表 } }, { path: /appointment, component: () import(/views/Appointment.vue), meta: { title: 预约挂号, requireAuth: true } } ];4.2 状态管理设计使用 Pinia 进行状态管理主要 store 包括// stores/user.js export const useUserStore defineStore(user, { state: () ({ userInfo: null, token: localStorage.getItem(token) || }), actions: { async login(loginData) { const response await api.login(loginData); this.token response.data.token; this.userInfo response.data.userInfo; localStorage.setItem(token, this.token); }, logout() { this.token ; this.userInfo null; localStorage.removeItem(token); } } }); // stores/appointment.js export const useAppointmentStore defineStore(appointment, { state: () ({ currentAppointment: null, appointmentHistory: [] }), actions: { async createAppointment(appointmentData) { const response await api.createAppointment(appointmentData); this.currentAppointment response.data; return response.data; }, async loadAppointmentHistory() { const response await api.getAppointmentHistory(); this.appointmentHistory response.data; } } });4.3 预约流程组件设计预约页面包含科室选择、医生选择、时间选择等多个步骤template div classappointment-container !-- 步骤指示器 -- el-steps :activecurrentStep simple el-step title选择科室 / el-step title选择医生 / el-step title选择时间 / el-step title确认预约 / /el-steps !-- 步骤内容 -- div classstep-content DepartmentSelection v-ifcurrentStep 1 department-selectedhandleDepartmentSelected / DoctorSelection v-ifcurrentStep 2 :department-idselectedDepartmentId doctor-selectedhandleDoctorSelected / TimeSelection v-ifcurrentStep 3 :doctor-idselectedDoctorId time-selectedhandleTimeSelected / AppointmentConfirm v-ifcurrentStep 4 :appointment-dataappointmentData confirmhandleConfirm / /div /div /template script setup import { ref, reactive } from vue; import { useRouter } from vue-router; import { useAppointmentStore } from /stores/appointment; const currentStep ref(1); const selectedDepartmentId ref(null); const selectedDoctorId ref(null); const appointmentData reactive({}); const appointmentStore useAppointmentStore(); const router useRouter(); const handleDepartmentSelected (departmentId) { selectedDepartmentId.value departmentId; currentStep.value 2; }; const handleConfirm async () { try { await appointmentStore.createAppointment(appointmentData); ElMessage.success(预约成功); router.push(/appointment-success); } catch (error) { ElMessage.error(预约失败 error.message); } }; /script5. 微信小程序端开发要点小程序端主要实现用户界面和微信 API 调用。5.1 小程序页面结构pages/ ├── index/ # 首页 ├── departments/ # 科室列表 ├── doctors/ # 医生列表 ├── appointment/ # 预约页面 ├── my/ # 个人中心 └── appointment-detail/ # 预约详情5.2 微信登录和用户信息获取// utils/auth.js export const wxLogin () { return new Promise((resolve, reject) { wx.login({ success: (res) { if (res.code) { // 发送 res.code 到后台换取 openId, sessionKey, unionId resolve(res.code); } else { reject(new Error(登录失败)); } }, fail: reject }); }); }; export const getUserProfile () { return new Promise((resolve, reject) { wx.getUserProfile({ desc: 用于完善会员资料, success: (res) { resolve(res.userInfo); }, fail: reject }); }); };5.3 预约流程实现小程序端预约流程要考虑网络异常和用户操作中断的情况// pages/appointment/appointment.js Page({ data: { currentStep: 1, departmentList: [], doctorList: [], timeSlots: [], selectedData: {} }, onLoad() { this.loadDepartments(); }, // 加载科室列表 async loadDepartments() { try { const res await wx.request({ url: https://your-api.com/api/departments, method: GET }); this.setData({ departmentList: res.data.data }); } catch (error) { wx.showToast({ title: 加载失败, icon: error }); } }, // 选择科室 onDepartmentSelect(e) { const departmentId e.currentTarget.dataset.id; this.setData({ selectedData.departmentId: departmentId, currentStep: 2 }); this.loadDoctors(departmentId); }, // 确认预约 async confirmAppointment() { try { wx.showLoading({ title: 预约中... }); const res await wx.request({ url: https://your-api.com/api/appointments, method: POST, data: this.data.selectedData, header: { Authorization: Bearer getApp().globalData.token } }); wx.hideLoading(); if (res.data.success) { wx.navigateTo({ url: /pages/appointment-success/appointment-success }); } else { wx.showToast({ title: res.data.message, icon: error }); } } catch (error) { wx.hideLoading(); wx.showToast({ title: 网络错误, icon: error }); } } });6. 部署和运维注意事项医院系统对稳定性要求高部署时要考虑多个方面。6.1 后端部署配置SpringBoot 应用部署建议使用 DockerFROM openjdk:17-jdk-slim VOLUME /tmp COPY target/hospital-appointment-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT [java,-jar,/app.jar]生产环境配置# application-prod.yml server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://mysql-server:3306/hospital?useSSLfalseserverTimezoneAsia/Shanghai username: ${DB_USERNAME} password: ${DB_PASSWORD} redis: host: redis-server port: 6379 password: ${REDIS_PASSWORD} logging: level: com.example.hospital: INFO file: name: /logs/hospital-appointment.log6.2 前端和小程序部署Vue 前端部署到 Nginxserver { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://backend-server:8080/api; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }小程序需要提交微信审核审核通过后才能发布。6.3 监控和日志医院系统需要完善的监控应用性能监控接口响应时间、错误率业务监控每日预约量、支付成功率、号源使用率日志收集关键业务操作日志、错误日志Aspect Component Slf4j public class AppointmentMonitorAspect { Around(execution(* com.example.hospital.service.AppointmentService.*(..))) public Object monitorAppointment(ProceedingJoinPoint joinPoint) throws Throwable { String methodName joinPoint.getSignature().getName(); long startTime System.currentTimeMillis(); try { Object result joinPoint.proceed(); long costTime System.currentTimeMillis() - startTime; log.info(Appointment method {} executed successfully, cost: {}ms, methodName, costTime); // 记录指标到监控系统 Metrics.counter(appointment.method.calls, method, methodName).increment(); Metrics.timer(appointment.method.duration, method, methodName) .record(costTime, TimeUnit.MILLISECONDS); return result; } catch (Exception e) { log.error(Appointment method {} failed, methodName, e); Metrics.counter(appointment.method.errors, method, methodName).increment(); throw e; } } }7. 常见问题排查实际开发中会遇到各种问题这里列几个典型的排查思路。7.1 微信登录失败现象小程序端获取不到用户信息或登录状态异常。排查顺序检查小程序 AppID 和 AppSecret 配置是否正确确认服务器域名已配置且支持 HTTPS检查微信登录 code 是否有效5分钟有效期查看后端日志确认微信接口调用是否正常7.2 预约时号源超卖现象同一时间段被多人预约成功超过号源限制。排查顺序检查数据库乐观锁实现是否正确确认事务注解 Transactional 生效检查是否有代码绕过乐观锁检查压力测试验证并发场景下的表现7.3 支付回调处理异常现象用户已支付但订单状态未更新。排查顺序检查支付回调接口是否能正常访问验证签名算法是否正确确认回调处理是幂等的查看支付日志确认微信支付通知是否送达7.4 性能问题现象页面加载慢或接口响应时间长。排查顺序检查数据库查询是否使用索引确认是否合理使用缓存如 Redis分析慢查询日志检查网络带宽和服务器资源使用情况这个方案真正落地时最该盯住的不是功能有多全而是挂号流程的数据一致性、微信接口的稳定性和异常情况的处理能力。如果只是毕业设计演示核心流程跑通即可如果要实际部署使用就要把监控、日志、备份和容灾都考虑进去。