# 挂号预约排班状态过滤修复 ## 问题描述 在挂号预约时,系统显示了所有时间段(9:00-18:00),但没有检查医生的排班状态。即使医生休息或停诊,系统仍然显示可预约时段。 ### 问题截图说明 从截图可以看到: - 周三 03-04 下午显示"休息" - 但系统仍然可能显示下午的预约时段 ## 排班状态定义 根据数据库模型 `Roster`,排班状态定义如下: | 状态值 | 状态名称 | 说明 | |--------|---------|------| | 1 | 出诊 | 医生正常出诊,可以预约 | | 2 | 停诊 | 医生停诊,不可预约 | | 3 | 休息 | 医生休息,不可预约 | | 4 | 请假 | 医生请假,不可预约 | ## 排班时段定义 | 时段值 | 时段名称 | 时间范围 | |--------|---------|---------| | 1 | 上午 | 09:00-12:00 | | 2 | 下午 | 14:00-18:00 | ## 问题根源 ### 原来的逻辑 ```php public static function getAvailableSlots(array $params) { // 1. 直接生成 9:00-18:00 的所有时间段 for ($hour = 9; $hour < 18; $hour++) { for ($minute = 0; $minute < 60; $minute += 15) { $slots[] = [ 'time' => sprintf('%02d:%02d', $hour, $minute), 'available' => true, 'quota' => 1 ]; } } // 2. 只检查已预约的时间段 // ❌ 没有检查医生的排班状态 } ``` ### 问题 1. 不检查医生是否有排班 2. 不检查排班状态(出诊/休息/停诊) 3. 不区分上午/下午时段 4. 即使医生休息也显示可预约 ## 解决方案 ### 修复后的逻辑 ```php public static function getAvailableSlots(array $params) { // 1. 先检查医生在该日期是否有出诊排班 $rosters = Roster::where([ 'doctor_id' => $doctorId, 'date' => $date, 'status' => 1 // ✅ 只查询出诊状态 ])->select()->toArray(); // 如果没有出诊排班,返回空数组 if (empty($rosters)) { return ['slots' => []]; } // 2. 根据排班时段生成时间段 foreach ($rosters as $roster) { if ($roster['period'] == 1) { // 上午:9:00-12:00 $startHour = 9; $endHour = 12; } elseif ($roster['period'] == 2) { // 下午:14:00-18:00 $startHour = 14; $endHour = 18; } // 生成15分钟间隔的时间段 for ($hour = $startHour; $hour < $endHour; $hour++) { for ($minute = 0; $minute < 60; $minute += 15) { $slots[] = [ 'time' => sprintf('%02d:%02d', $hour, $minute), 'available' => true, 'quota' => 1, 'period' => $roster['period'] ]; } } } // 3. 查询已预约的时间段并标记 // ... } ``` ## 修复效果 ### 场景 1:医生上午出诊,下午休息 **排班数据:** ``` date: 2026-03-04 period: 1 (上午) status: 1 (出诊) ``` **预约时段:** - ✅ 显示:09:00, 09:15, 09:30, ..., 11:45 - ❌ 不显示:14:00-18:00 的时段 ### 场景 2:医生全天休息 **排班数据:** ``` date: 2026-03-04 period: 1 (上午) status: 3 (休息) date: 2026-03-04 period: 2 (下午) status: 3 (休息) ``` **预约时段:** - ❌ 不显示任何时段 - 💡 提示:该医生暂无可预约时段 ### 场景 3:医生全天出诊 **排班数据:** ``` date: 2026-03-04 period: 1 (上午) status: 1 (出诊) date: 2026-03-04 period: 2 (下午) status: 1 (出诊) ``` **预约时段:** - ✅ 显示:09:00-11:45 (上午) - ✅ 显示:14:00-17:45 (下午) ### 场景 4:医生上午停诊,下午出诊 **排班数据:** ``` date: 2026-03-04 period: 1 (上午) status: 2 (停诊) date: 2026-03-04 period: 2 (下午) status: 1 (出诊) ``` **预约时段:** - ❌ 不显示:09:00-12:00 的时段 - ✅ 显示:14:00-17:45 (下午) ## 时间段生成规则 ### 上午时段(period = 1) ``` 开始时间:09:00 结束时间:12:00 间隔:15分钟 生成时段: 09:00, 09:15, 09:30, 09:45, 10:00, 10:15, 10:30, 10:45, 11:00, 11:15, 11:30, 11:45 ``` ### 下午时段(period = 2) ``` 开始时间:14:00 结束时间:18:00 间隔:15分钟 生成时段: 14:00, 14:15, 14:30, 14:45, 15:00, 15:15, 15:30, 15:45, 16:00, 16:15, 16:30, 16:45, 17:00, 17:15, 17:30, 17:45 ``` ## 前端显示逻辑 前端已经正确处理了排班状态的过滤: ```typescript // 加载医生排班信息 const loadDoctorRoster = async () => { const res = await rosterLists({ doctor_id: selectedDoctorId.value, start_date: startDate, end_date: endDate, status: 1 // ✅ 只查询出诊状态 }) // 提取有排班的日期 if (res?.lists && res.lists.length > 0) { doctorRosterDates.value = [...new Set(res.lists.map(item => item.date))].sort() } } ``` ## 完整流程 ### 1. 选择医生 ``` 用户选择医生 → 加载医生排班 → 只显示有出诊排班的日期 ``` ### 2. 选择日期 ``` 用户选择日期 → 加载该日期的可用时段 → 只显示出诊时段的时间 ``` ### 3. 选择时段 ``` 用户选择时段 → 检查是否已被预约 → 创建预约 ``` ## 数据库查询 ### 查询医生出诊排班 ```sql SELECT * FROM zyt_doctor_roster WHERE doctor_id = 1 AND date = '2026-03-04' AND status = 1 -- 只查询出诊状态 ORDER BY period ASC; ``` ### 查询已预约时段 ```sql SELECT appointment_time FROM zyt_doctor_appointment WHERE doctor_id = 1 AND appointment_date = '2026-03-04' AND status = 1 -- 只查询有效预约 ``` ## 测试用例 ### 测试 1:医生全天出诊 ```sql -- 插入排班 INSERT INTO zyt_doctor_roster (doctor_id, date, period, status, quota, max_patients) VALUES (1, '2026-03-10', 1, 1, 20, 20), -- 上午出诊 (1, '2026-03-10', 2, 1, 20, 20); -- 下午出诊 ``` **预期结果:** - 显示上午时段:09:00-11:45 - 显示下午时段:14:00-17:45 ### 测试 2:医生上午出诊,下午休息 ```sql INSERT INTO zyt_doctor_roster (doctor_id, date, period, status, quota, max_patients) VALUES (1, '2026-03-11', 1, 1, 20, 20), -- 上午出诊 (1, '2026-03-11', 2, 3, 0, 0); -- 下午休息 ``` **预期结果:** - 显示上午时段:09:00-11:45 - 不显示下午时段 ### 测试 3:医生全天休息 ```sql INSERT INTO zyt_doctor_roster (doctor_id, date, period, status, quota, max_patients) VALUES (1, '2026-03-12', 1, 3, 0, 0), -- 上午休息 (1, '2026-03-12', 2, 3, 0, 0); -- 下午休息 ``` **预期结果:** - 该日期不出现在可选日期列表中 - 如果强制访问,不显示任何时段 ### 测试 4:医生上午停诊,下午出诊 ```sql INSERT INTO zyt_doctor_roster (doctor_id, date, period, status, quota, max_patients) VALUES (1, '2026-03-13', 1, 2, 0, 0), -- 上午停诊 (1, '2026-03-13', 2, 1, 20, 20); -- 下午出诊 ``` **预期结果:** - 不显示上午时段 - 显示下午时段:14:00-17:45 ## 已修改的文件 - ✅ `server/app/adminapi/logic/doctor/AppointmentLogic.php` ## 优势 1. **准确性**:只显示医生实际出诊的时段 2. **用户体验**:避免用户选择无效时段 3. **数据一致性**:预约数据与排班数据保持一致 4. **灵活性**:支持上午/下午独立设置状态 5. **可维护性**:逻辑清晰,易于理解和维护 ## 注意事项 1. **排班状态**:只有 `status = 1`(出诊)的排班才会生成时段 2. **时段区分**:上午和下午时段独立判断 3. **时间格式**:统一使用 `HH:MM` 格式(如:09:00) 4. **已预约检查**:在生成时段后,还需要检查已预约情况 5. **空数组处理**:如果没有出诊排班,返回空数组而不是错误 --- **修复日期:** 2024-03-04 **状态:** ✅ 完成 **影响范围:** 挂号预约功能