# 患者挂号系统实现文档 ## 功能概述 实现了基于医生排班的患者挂号系统,支持15分钟间隔的时间段预约,防止重复预约,并根据医生排班状态控制挂号可用性。 ## 核心功能 ### 1. 时间段管理 - 每个时间段间隔15分钟 - 上午时段:08:00 - 12:00(16个时间段) - 下午时段:14:00 - 18:00(16个时间段) - 每个时间段只能被一个患者预约 ### 2. 排班状态控制 - 出诊(status=1):允许挂号 - 停诊(status=2):不允许挂号 - 休息(status=3):不允许挂号 - 请假(status=4):不允许挂号 ### 3. 号源管理 - 医生排班时设置号源数(quota) - 已预约数量不能超过号源数 - 实时显示可用/已占用状态 ## 文件结构 ``` admin/ ├── src/ │ ├── api/ │ │ └── doctor.ts # 新增挂号相关API │ └── views/ │ ├── doctor/ │ │ └── roster.vue # 医生排班管理(已存在) │ └── tcm/ │ └── diagnosis/ │ ├── index.vue # 诊断列表(已更新) │ └── appointment.vue # 挂号弹窗组件(新增) ``` ## API接口 ### 1. 获取可用时间段 ```typescript GET /doctor.appointment/availableSlots 参数: { doctor_id: number, // 医生ID date: string, // 日期 YYYY-MM-DD period: 'morning' | 'afternoon' // 时段 } 返回: { slots: [ { time: '08:00', // 时间 available: true // 是否可预约 }, ... ] } ``` ### 2. 创建挂号 ```typescript POST /doctor.appointment/create 参数: { patient_id: number, // 患者ID doctor_id: number, // 医生ID appointment_date: string, // 预约日期 YYYY-MM-DD period: 'morning' | 'afternoon', // 时段 appointment_time: string, // 预约时间 HH:mm remark: string // 备注(可选) } ``` ### 3. 取消挂号 ```typescript POST /doctor.appointment/cancel 参数: { id: number // 挂号记录ID } ``` ### 4. 挂号列表 ```typescript GET /doctor.appointment/lists 参数: { page_no: number, page_size: number, patient_id?: number, doctor_id?: number, date?: string } ``` ## 后端实现要点 ### 1. 数据库表设计 ```sql CREATE TABLE `doctor_appointment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `patient_id` int(11) NOT NULL COMMENT '患者ID', `doctor_id` int(11) NOT NULL COMMENT '医生ID', `roster_id` int(11) NOT NULL COMMENT '排班ID', `appointment_date` date NOT NULL COMMENT '预约日期', `period` enum('morning','afternoon') NOT NULL COMMENT '时段', `appointment_time` time NOT NULL COMMENT '预约时间', `status` tinyint(1) DEFAULT '1' COMMENT '状态:1=已预约,2=已取消,3=已完成', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `unique_appointment` (`doctor_id`,`appointment_date`,`appointment_time`,`status`), KEY `idx_patient` (`patient_id`), KEY `idx_doctor_date` (`doctor_id`,`appointment_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生挂号表'; ``` ### 2. 获取可用时间段逻辑 ```php public function availableSlots() { $doctorId = $this->request->get('doctor_id'); $date = $this->request->get('date'); $period = $this->request->get('period'); // 1. 检查医生排班 $roster = DoctorRoster::where([ 'doctor_id' => $doctorId, 'date' => $date, 'period' => $period ])->find(); // 如果没有排班或状态不是出诊,返回空数组 if (!$roster || $roster->status != 1) { return $this->success(['slots' => []]); } // 2. 生成时间段 $slots = []; if ($period == 'morning') { $startHour = 8; $endHour = 12; } else { $startHour = 14; $endHour = 18; } for ($hour = $startHour; $hour < $endHour; $hour++) { for ($minute = 0; $minute < 60; $minute += 15) { $time = sprintf('%02d:%02d', $hour, $minute); $slots[] = [ 'time' => $time, 'available' => true ]; } } // 3. 查询已预约的时间段 $appointments = DoctorAppointment::where([ 'doctor_id' => $doctorId, 'appointment_date' => $date, 'period' => $period, 'status' => 1 // 只查询有效预约 ])->column('appointment_time'); // 4. 标记已占用的时间段 foreach ($slots as &$slot) { if (in_array($slot['time'], $appointments)) { $slot['available'] = false; } } // 5. 检查号源限制 $appointmentCount = count($appointments); if ($appointmentCount >= $roster->quota) { // 如果已达到号源上限,所有未预约的时间段也标记为不可用 foreach ($slots as &$slot) { if ($slot['available']) { $slot['available'] = false; } } } return $this->success(['slots' => $slots]); } ``` ### 3. 创建挂号逻辑 ```php public function create() { $data = $this->request->post(); // 1. 验证参数 $validate = Validate::rule([ 'patient_id|患者ID' => 'require|integer', 'doctor_id|医生ID' => 'require|integer', 'appointment_date|预约日期' => 'require|date', 'period|时段' => 'require|in:morning,afternoon', 'appointment_time|预约时间' => 'require' ]); if (!$validate->check($data)) { return $this->fail($validate->getError()); } // 2. 检查医生排班 $roster = DoctorRoster::where([ 'doctor_id' => $data['doctor_id'], 'date' => $data['appointment_date'], 'period' => $data['period'] ])->find(); if (!$roster) { return $this->fail('该医生当天未排班'); } if ($roster->status != 1) { return $this->fail('该医生当天不出诊'); } // 3. 检查时间段是否已被预约 $exists = DoctorAppointment::where([ 'doctor_id' => $data['doctor_id'], 'appointment_date' => $data['appointment_date'], 'appointment_time' => $data['appointment_time'], 'status' => 1 ])->find(); if ($exists) { return $this->fail('该时间段已被预约'); } // 4. 检查号源是否已满 $appointmentCount = DoctorAppointment::where([ 'doctor_id' => $data['doctor_id'], 'appointment_date' => $data['appointment_date'], 'period' => $data['period'], 'status' => 1 ])->count(); if ($appointmentCount >= $roster->quota) { return $this->fail('该时段号源已满'); } // 5. 创建挂号记录 $appointment = DoctorAppointment::create([ 'patient_id' => $data['patient_id'], 'doctor_id' => $data['doctor_id'], 'roster_id' => $roster->id, 'appointment_date' => $data['appointment_date'], 'period' => $data['period'], 'appointment_time' => $data['appointment_time'], 'remark' => $data['remark'] ?? '', 'status' => 1 ]); return $this->success('挂号成功', $appointment); } ``` ## 前端组件说明 ### appointment.vue 组件 挂号弹窗组件,包含以下功能: 1. **医生选择**:从医生列表中选择 2. **日期选择**:不能选择过去的日期 3. **时段选择**:上午/下午 4. **时间段选择**: - 网格布局显示所有时间段 - 可用时间段:蓝色,可点击 - 已占用时间段:灰色,不可点击 - 已选择时间段:深蓝色高亮 5. **备注输入**:可选的备注信息 ### 使用方式 ```vue ``` ## 权限控制 在诊断列表中,挂号按钮使用权限指令: ```vue 挂号 ``` 需要在后端权限系统中添加对应的权限节点。 ## 注意事项 1. **时区处理**:确保前后端时间格式一致 2. **并发控制**:使用数据库唯一索引防止重复预约 3. **事务处理**:创建挂号时使用事务确保数据一致性 4. **缓存策略**:可以对排班数据进行缓存提高性能 5. **通知机制**:挂号成功后可以发送短信/消息通知 ## 扩展功能建议 1. **预约提醒**:在预约时间前发送提醒 2. **候补机制**:号源满时支持候补排队 3. **预约改期**:支持修改预约时间 4. **统计报表**:医生预约统计、患者预约历史 5. **评价系统**:就诊后患者评价医生