1. 项目背景与核心价值
医院挂号难一直是困扰患者就医体验的痛点问题。传统线下挂号需要患者或家属提前到医院排队,耗时耗力;电话预约又常常面临占线、信息不明确等问题。基于微信小程序的医院预约挂号系统正是为了解决这些痛点而生。
这个毕设项目的核心价值在于:
- 利用微信小程序的便捷性,患者无需下载额外APP,通过微信即可完成挂号全流程
- 将线下排队场景数字化,减少患者等待时间,优化就医体验
- 为医院提供高效的分诊管理工具,提升医疗资源利用率
- 采用SpringBoot+小程序的技术栈,既保证了后端服务的稳定性,又提供了良好的移动端用户体验
2. 系统架构设计
2.1 技术选型分析
前端技术栈:
- 微信小程序:选择微信小程序而非原生APP开发,主要考虑以下几点:
- 微信生态的用户覆盖率极高,无需额外安装
- 开发成本低,迭代速度快
- 微信提供的支付、登录等原生能力可直接调用
- 跨平台特性,一套代码适配iOS和Android
后端技术栈:
- SpringBoot 2.x:相比传统SSM框架,SpringBoot具有以下优势:
- 自动配置简化了初始搭建和开发过程
- 内嵌Tomcat,无需额外部署
- 丰富的starter依赖,快速集成各种组件
- 完善的监控机制(Actuator)
- MySQL 5.7:关系型数据库,存储用户信息、医生排班、预约记录等结构化数据
- Redis:缓存热点数据(如医生排班信息),减轻数据库压力
2.2 系统模块划分
系统主要分为以下核心模块:
- 用户模块:患者注册/登录、个人信息管理
- 医生模块:医生信息管理、排班设置
- 挂号模块:科室选择、医生选择、时间选择、预约确认
- 支付模块:挂号费支付(集成微信支付)
- 订单模块:预约记录查询、取消预约
- 后台管理:数据统计、系统配置
3. 数据库设计关键点
3.1 核心表结构
用户表(user)
sql复制CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`openid` varchar(64) NOT NULL COMMENT '微信openid',
`nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`real_name` varchar(32) DEFAULT NULL COMMENT '真实姓名',
`id_card` varchar(18) DEFAULT NULL COMMENT '身份证号',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
医生表(doctor)
sql复制CREATE TABLE `doctor` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '医生ID',
`name` varchar(32) NOT NULL COMMENT '医生姓名',
`department_id` int(11) NOT NULL COMMENT '所属科室ID',
`title` varchar(32) DEFAULT NULL COMMENT '职称',
`specialty` varchar(255) DEFAULT NULL COMMENT '专长',
`introduction` text DEFAULT NULL COMMENT '简介',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(0-停诊,1-出诊)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生表';
排班表(schedule)
sql复制CREATE TABLE `schedule` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '排班ID',
`doctor_id` bigint(20) NOT NULL COMMENT '医生ID',
`work_date` date NOT NULL COMMENT '出诊日期',
`work_time` tinyint(4) NOT NULL COMMENT '时间段(1-上午,2-下午,3-晚上)',
`total` int(11) NOT NULL COMMENT '总号源数',
`available` int(11) NOT NULL COMMENT '剩余号源数',
`fee` decimal(10,2) NOT NULL COMMENT '挂号费',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_doctor_date_time` (`doctor_id`,`work_date`,`work_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生排班表';
3.2 关键设计考虑
-
号源控制:采用预分配号源机制,避免超卖
- 排班表(total/available)字段控制号源总量
- 使用乐观锁(version)或分布式锁保证并发安全
-
索引优化:
- 高频查询字段建立索引(如openid、doctor_id等)
- 联合索引注意最左前缀原则
-
数据一致性:
- 挂号成功需要同时更新排班表和订单表,使用事务保证原子性
- 考虑引入消息队列处理异步操作(如短信通知)
4. 核心功能实现细节
4.1 微信登录集成
小程序端调用wx.login获取code,传给后端换取openid:
java复制@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private WxService wxService;
@PostMapping("/login")
public Result login(@RequestParam String code) {
// 调用微信接口获取openid
String openid = wxService.getOpenid(code);
// 查询或创建用户
User user = userService.getOrCreate(openid);
// 生成JWT token
String token = JwtUtil.generateToken(user.getId());
return Result.success(token);
}
}
注意:实际项目中应该将敏感信息(如AppSecret)放在配置中心,不要硬编码在代码中
4.2 挂号业务流程
- 获取可预约医生列表:
java复制@GetMapping("/doctors")
public Result listDoctors(@RequestParam Long departmentId,
@RequestParam String date) {
// 查询当天有排班的医生
List<DoctorVO> doctors = doctorService.listAvailableDoctors(departmentId, date);
return Result.success(doctors);
}
- 选择医生后获取可预约时间段:
java复制@GetMapping("/schedules")
public Result listSchedules(@RequestParam Long doctorId,
@RequestParam String date) {
// 查询医生当天的排班情况
List<ScheduleVO> schedules = scheduleService.listByDoctorAndDate(doctorId, date);
return Result.success(schedules);
}
- 提交预约请求:
java复制@PostMapping("/appointments")
@Transactional
public Result createAppointment(@RequestBody AppointmentDTO dto) {
// 1. 校验号源是否可用
Schedule schedule = scheduleService.getById(dto.getScheduleId());
if (schedule.getAvailable() <= 0) {
throw new BusinessException("该时段号源已约满");
}
// 2. 扣减号源
boolean success = scheduleService.reduceAvailable(dto.getScheduleId());
if (!success) {
throw new BusinessException("号源不足");
}
// 3. 创建预约记录
Appointment appointment = new Appointment();
// 设置各种属性...
appointmentService.save(appointment);
// 4. 生成支付订单
Payment payment = paymentService.createPayment(appointment);
return Result.success(payment.getId());
}
4.3 微信支付集成
- 小程序端调用wx.requestPayment发起支付:
javascript复制wx.requestPayment({
timeStamp: '',
nonceStr: '',
package: '',
signType: 'MD5',
paySign: '',
success(res) {
// 支付成功处理
},
fail(err) {
// 支付失败处理
}
})
- 后端统一下单接口:
java复制@PostMapping("/pay/unifiedorder")
public Result unifiedOrder(@RequestBody PaymentDTO dto) {
// 1. 校验订单信息
Payment payment = paymentService.getById(dto.getPaymentId());
if (payment == null || payment.getStatus() != PaymentStatus.UNPAID) {
throw new BusinessException("订单状态异常");
}
// 2. 调用微信支付统一下单接口
Map<String, String> params = new HashMap<>();
params.put("body", "医院挂号费");
params.put("out_trade_no", payment.getOrderNo());
params.put("total_fee", String.valueOf(payment.getAmount()));
params.put("openid", payment.getOpenid());
// 其他必要参数...
Map<String, String> result = wxPayService.unifiedOrder(params);
// 3. 返回小程序支付所需参数
Map<String, String> data = new HashMap<>();
data.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
data.put("nonceStr", RandomUtil.randomString(32));
data.put("package", "prepay_id=" + result.get("prepay_id"));
data.put("signType", "MD5");
data.put("paySign", generateSign(data));
return Result.success(data);
}
5. 项目部署与测试
5.1 环境准备
开发环境:
- JDK 1.8+
- Maven 3.6+
- MySQL 5.7+
- Redis 5.0+
- 微信开发者工具
生产环境建议:
- Nginx:反向代理、静态资源服务
- 阿里云ECS或腾讯云CVM
- 云数据库RDS
- 云Redis
5.2 部署步骤
- 后端部署:
bash复制# 打包
mvn clean package -DskipTests
# 上传jar包到服务器
scp target/hospital-booking.jar root@your-server:/app/
# 启动服务(生产环境建议使用nohup或配置为systemd服务)
java -jar hospital-booking.jar --spring.profiles.active=prod
- 小程序部署:
- 在微信公众平台申请小程序账号
- 配置服务器域名(需HTTPS)
- 使用微信开发者工具上传代码
- 提交审核发布
5.3 测试要点
-
功能测试:
- 微信登录流程
- 医生排班查询
- 预约挂号流程
- 支付流程
- 订单查询与取消
-
性能测试:
- 高并发挂号场景(使用JMeter模拟)
- 数据库连接池配置优化
- Redis缓存命中率监控
-
安全测试:
- SQL注入防护
- XSS防护
- 接口权限验证
- 敏感数据加密
6. 常见问题与解决方案
6.1 微信登录失败
问题现象:获取openid失败,返回40029错误码
排查步骤:
- 检查appid和secret是否正确
- 确认code未被重复使用(code有效期为5分钟)
- 检查服务器时间是否准确(误差超过5分钟会导致失败)
- 确认网络请求未被拦截
6.2 号源超卖问题
解决方案:
- 数据库乐观锁:
java复制@Update("UPDATE schedule SET available = available - 1, version = version + 1 " +
"WHERE id = #{id} AND available > 0 AND version = #{version}")
int reduceAvailableWithVersion(@Param("id") Long id, @Param("version") Integer version);
- Redis分布式锁:
java复制public boolean tryLock(String key, long expireTime) {
String value = UUID.randomUUID().toString();
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
6.3 支付回调处理
关键点:
- 验证签名防止伪造请求
- 处理幂等性(同一笔订单可能收到多次回调)
- 异步更新订单状态并通知用户
java复制@PostMapping("/pay/notify")
public String payNotify(HttpServletRequest request) {
// 1. 验证签名
if (!wxPayService.verifySign(request)) {
return "fail";
}
// 2. 解析回调参数
String orderNo = request.getParameter("out_trade_no");
String transactionId = request.getParameter("transaction_id");
// 3. 处理业务逻辑(注意幂等性)
paymentService.handlePaySuccess(orderNo, transactionId);
return "success";
}
7. 项目扩展方向
- 智能推荐:基于用户历史挂号记录推荐相关科室和医生
- 候诊队列:实时显示当前就诊进度,减少患者等待焦虑
- 报告查询:对接医院LIS/PACS系统,提供检查报告查询
- 健康档案:建立个人电子健康档案,记录就诊历史
- 在线问诊:扩展图文/视频问诊功能
- 药品配送:对接药房系统,支持药品配送到家
在实际开发中,我建议采用迭代开发的方式,先实现核心挂号功能,再逐步扩展其他模块。对于毕业设计而言,重点展示技术实现的完整性和解决问题的思路,不必追求大而全的功能。
