347 lines
7.7 KiB
Markdown
347 lines
7.7 KiB
Markdown
# 挂号预约排班状态过滤修复
|
||
|
||
## 问题描述
|
||
|
||
在挂号预约时,系统显示了所有时间段(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
|
||
**状态:** ✅ 完成
|
||
**影响范围:** 挂号预约功能
|