Spring Boot MVC REST API医生可预约时段展示方案咨询
嘿,针对你这个诊所预约系统的医生可预约时段展示需求,我来分享一套经过实践验证的最优方案,从数据库设计到Spring Boot实现再到细节优化都给你梳理清楚:
核心方案设计
1. 数据库表结构设计
首先得搭好基础数据模型,确保数据关联清晰:
- 诊所表(clinics):存储诊所基础信息,字段示例:
id(主键),name,address - 医生表(doctors):关联所属诊所,字段示例:
id(主键),name,clinic_id(外键关联clinics.id),specialty - 预约表(appointments):记录已预约信息,字段示例:
id(主键),doctor_id(外键关联doctors.id),patient_id,appointment_date(日期类型),start_time(时间类型),end_time(时间类型,自动计算为start_time+30分钟),status
2. 核心业务逻辑实现
核心思路是生成当日全量基础时段 → 排除已预约时段 → 返回可用时段:
- 生成基础时段:从早7点到晚6点,每30分钟一个时段(即7:00、7:30...17:30,因为17:30+30分钟刚好到18:00结束)
- 过滤已占用时段:查询该医生当日所有已预约的
start_time,从基础时段中移除这些时间点 - 参数校验:确保医生属于所选诊所、查询日期不为过去日期
3. Spring Boot REST API 实现
代码示例
Service层核心逻辑
@Service public class AppointmentService { @Autowired private AppointmentRepository appointmentRepository; @Autowired private DoctorRepository doctorRepository; public List<SlotDTO> getAvailableSlots(Long clinicId, Long doctorId, LocalDate date) { // 校验医生归属诊所 Doctor doctor = doctorRepository.findById(doctorId) .orElseThrow(() -> new IllegalArgumentException("指定医生不存在")); if (!doctor.getClinicId().equals(clinicId)) { throw new IllegalArgumentException("该医生不属于所选诊所"); } // 生成当日全量基础时段 List<LocalTime> allBaseSlots = new ArrayList<>(); LocalTime currentTime = LocalTime.of(7, 0); LocalTime dailyEndTime = LocalTime.of(18, 0); while (currentTime.isBefore(dailyEndTime)) { allBaseSlots.add(currentTime); currentTime = currentTime.plusMinutes(30); } // 查询已预约时段 List<LocalTime> bookedSlots = appointmentRepository.findBookedStartTimes(doctorId, date); // 过滤可用时段并转换为DTO return allBaseSlots.stream() .filter(slot -> !bookedSlots.contains(slot)) .map(slot -> new SlotDTO(slot, slot.plusMinutes(30))) .collect(Collectors.toList()); } } // 时段DTO定义 public class SlotDTO { private LocalTime startTime; private LocalTime endTime; // 构造器、Getter、Setter省略 }
Repository层查询方法
@Repository public interface AppointmentRepository extends JpaRepository<Appointment, Long> { @Query("SELECT a.startTime FROM Appointment a WHERE a.doctorId = :doctorId AND a.appointmentDate = :date") List<LocalTime> findBookedStartTimes(@Param("doctorId") Long doctorId, @Param("date") LocalDate date); }
Controller层API接口
@RestController @RequestMapping("/api/clinics") public class AppointmentController { @Autowired private AppointmentService appointmentService; @GetMapping("/{clinicId}/doctors/{doctorId}/available-slots") public ResponseEntity<List<SlotDTO>> getAvailableSlots( @PathVariable Long clinicId, @PathVariable Long doctorId, @RequestParam("date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) { // 校验日期合法性 if (date.isBefore(LocalDate.now())) { return ResponseEntity.badRequest().body(Collections.emptyList()); } try { List<SlotDTO> availableSlots = appointmentService.getAvailableSlots(clinicId, doctorId, date); return ResponseEntity.ok(availableSlots); } catch (IllegalArgumentException e) { return ResponseEntity.badRequest().body(Collections.emptyList()); } } }
4. 前端交互建议
- 联动选择:用户选完诊所后,异步加载该诊所的医生列表;选完医生后,弹出日历组件选择日期
- 时段渲染:将API返回的可用时段渲染为可点击按钮,若需展示已预约时段可置灰禁用
- 实时性:预约成功后,立即刷新当前日期的可用时段列表
5. 优化与边界处理
- 缓存优化:用Redis缓存热门医生的可用时段,缓存Key设为
available_slots:{clinicId}:{doctorId}:{date},有效期1小时,有新预约时删除对应缓存 - 医生休息处理:若需支持医生固定休息日/时段,可新增
doctor_rest_days表,在生成基础时段前先校验日期是否为休息日 - 并发防护:预约时需加乐观锁或数据库行锁,防止同一时段被多个用户同时预约
内容的提问来源于stack exchange,提问作者Code reviewer
相关产品推荐
相关产品推荐

