Files
zyt/admin/APPOINTMENT_SYSTEM.md
T
2026-03-04 15:32:30 +08:00

343 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 患者挂号系统实现文档
## 功能概述
实现了基于医生排班的患者挂号系统,支持15分钟间隔的时间段预约,防止重复预约,并根据医生排班状态控制挂号可用性。
## 核心功能
### 1. 时间段管理
- 每个时间段间隔15分钟
- 上午时段:08:00 - 12:0016个时间段)
- 下午时段:14:00 - 18:0016个时间段)
- 每个时间段只能被一个患者预约
### 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
<template>
<appointment-popup ref="appointmentRef" @success="handleSuccess" />
</template>
<script setup>
import AppointmentPopup from './appointment.vue'
const appointmentRef = ref()
const handleAppointment = (patient) => {
appointmentRef.value?.open(patient)
}
const handleSuccess = () => {
// 挂号成功后的处理
console.log('挂号成功')
}
</script>
```
## 权限控制
在诊断列表中,挂号按钮使用权限指令:
```vue
<el-button
v-perms="['tcm.diagnosis/guahao']"
type="success"
link
@click="handleAppointment(row)"
>
挂号
</el-button>
```
需要在后端权限系统中添加对应的权限节点。
## 注意事项
1. **时区处理**:确保前后端时间格式一致
2. **并发控制**:使用数据库唯一索引防止重复预约
3. **事务处理**:创建挂号时使用事务确保数据一致性
4. **缓存策略**:可以对排班数据进行缓存提高性能
5. **通知机制**:挂号成功后可以发送短信/消息通知
## 扩展功能建议
1. **预约提醒**:在预约时间前发送提醒
2. **候补机制**:号源满时支持候补排队
3. **预约改期**:支持修改预约时间
4. **统计报表**:医生预约统计、患者预约历史
5. **评价系统**:就诊后患者评价医生