9.0 KiB
9.0 KiB
患者挂号系统实现文档
功能概述
实现了基于医生排班的患者挂号系统,支持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. 获取可用时间段
GET /doctor.appointment/availableSlots
参数:
{
doctor_id: number, // 医生ID
date: string, // 日期 YYYY-MM-DD
period: 'morning' | 'afternoon' // 时段
}
返回:
{
slots: [
{
time: '08:00', // 时间
available: true // 是否可预约
},
...
]
}
2. 创建挂号
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. 取消挂号
POST /doctor.appointment/cancel
参数:
{
id: number // 挂号记录ID
}
4. 挂号列表
GET /doctor.appointment/lists
参数:
{
page_no: number,
page_size: number,
patient_id?: number,
doctor_id?: number,
date?: string
}
后端实现要点
1. 数据库表设计
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. 获取可用时间段逻辑
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. 创建挂号逻辑
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 组件
挂号弹窗组件,包含以下功能:
- 医生选择:从医生列表中选择
- 日期选择:不能选择过去的日期
- 时段选择:上午/下午
- 时间段选择:
- 网格布局显示所有时间段
- 可用时间段:蓝色,可点击
- 已占用时间段:灰色,不可点击
- 已选择时间段:深蓝色高亮
- 备注输入:可选的备注信息
使用方式
<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>
权限控制
在诊断列表中,挂号按钮使用权限指令:
<el-button
v-perms="['tcm.diagnosis/guahao']"
type="success"
link
@click="handleAppointment(row)"
>
挂号
</el-button>
需要在后端权限系统中添加对应的权限节点。
注意事项
- 时区处理:确保前后端时间格式一致
- 并发控制:使用数据库唯一索引防止重复预约
- 事务处理:创建挂号时使用事务确保数据一致性
- 缓存策略:可以对排班数据进行缓存提高性能
- 通知机制:挂号成功后可以发送短信/消息通知
扩展功能建议
- 预约提醒:在预约时间前发送提醒
- 候补机制:号源满时支持候补排队
- 预约改期:支持修改预约时间
- 统计报表:医生预约统计、患者预约历史
- 评价系统:就诊后患者评价医生