You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel医院床位分配:可用床位校验与事件监听器实现问询

医院床位分配系统实现方案(基于Laravel)

看起来你已经搭建好了基础的数据库结构,接下来我会一步步帮你实现床位的可用性检查、分配,以及用Laravel的事件监听器来串联整个流程。

一、先完善模型关联(必备基础)

首先,在Laravel中,模型关联能让我们更优雅地查询数据。你需要给三个模型添加关联关系:

Room 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Room extends Model
{
    // 一个房间对应多个床位
    public function beds()
    {
        return $this->hasMany(Bed::class);
    }
}

Bed 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Bed extends Model
{
    // 一个床位属于一个房间
    public function room()
    {
        return $this->belongsTo(Room::class);
    }

    // 一个床位对应多个预订记录
    public function reservations()
    {
        return $this->hasMany(Reservation::class);
    }
}

Reservation 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Reservation extends Model
{
    // 一个预订属于一个床位
    public function bed()
    {
        return $this->belongsTo(Bed::class);
    }

    // 一个预订属于一个患者
    public function patient()
    {
        return $this->belongsTo(Patient::class);
    }
}

二、检查与获取可用床位

我们需要两个核心方法:一个用来批量获取某日期的可用床位,另一个用来单独验证某个床位在指定日期区间内是否可用。

1. 获取可用床位列表

在 Bed 模型中添加静态方法,用来查询指定日期的所有可用床位:

use Carbon\Carbon;

public static function getAvailableBeds($date = null)
{
    // 默认检查当天的可用情况,也可以传入指定日期
    $targetDate = $date ?? Carbon::today();

    return self::where('status', true) // 先筛选自身状态为可用的床位
        ->whereDoesntHave('reservations', function ($query) use ($targetDate) {
            // 排除掉有有效且日期重叠的预订记录的床位
            $query->where('status', true)
                  ->where('date_in', '<=', $targetDate)
                  ->where('date_out', '>=', $targetDate);
        })
        ->with('room') // 关联查询房间信息,方便前端展示房间详情
        ->get();
}

2. 验证单个床位的可用性

同样在 Bed 模型中添加方法,用来判断床位在指定日期区间内是否可用:

public function isAvailable($dateIn, $dateOut)
{
    // 首先检查床位自身是否处于可用状态
    if (!$this->status) {
        return false;
    }

    // 检查是否存在与指定日期区间重叠的有效预订
    return !$this->reservations()
        ->where('status', true)
        ->where(function ($query) use ($dateIn, $dateOut) {
            // 覆盖三种日期重叠的情况
            $query->whereBetween('date_in', [$dateIn, $dateOut])
                  ->orWhereBetween('date_out', [$dateIn, $dateOut])
                  ->orWhere(function ($q) use ($dateIn, $dateOut) {
                      $q->where('date_in', '<=', $dateIn)
                        ->where('date_out', '>=', $dateOut);
                  });
        })
        ->exists();
}

3. 分配床位时的可用性校验

在你的 ReservationController 的 store 方法中,结合事务和上述方法确保分配的安全性:

use App\Models\Bed;
use App\Models\Reservation;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;

public function store(Request $request)
{
    // 先验证输入数据
    $validated = $request->validate([
        'patient_id' => 'required|exists:patients,id',
        'bed_id' => 'required|exists:beds,id',
        'date_in' => 'required|date',
        'date_out' => 'required|date|after:date_in',
    ]);

    // 开启数据库事务,确保操作原子性
    DB::beginTransaction();

    try {
        $bed = Bed::findOrFail($validated['bed_id']);
        
        // 检查床位在指定日期内是否可用
        if (!$bed->isAvailable($validated['date_in'], $validated['date_out'])) {
            return back()->withErrors(['bed_id' => '该床位在指定日期区间内已被占用']);
        }

        // 创建预订记录
        $reservation = Reservation::create([
            'patient_id' => $validated['patient_id'],
            'bed_id' => $validated['bed_id'],
            'date_in' => $validated['date_in'],
            'date_out' => $validated['date_out'],
            'status' => true,
        ]);

        // 触发床位分配事件(后面会讲)
        event(new \App\Events\BedAssigned($reservation));

        DB::commit();

        return redirect()->route('reservations.show', $reservation)->with('success', '床位分配成功');
    } catch (\Exception $e) {
        DB::rollBack();
        return back()->withErrors(['message' => '分配失败:' . $e->getMessage()]);
    }
}

三、用Laravel事件与监听器实现自动化流程

事件监听器能让你把床位分配后的后续操作(比如发送通知、更新床位状态)解耦,让代码更清晰。

1. 创建BedAssigned事件

运行Artisan命令生成事件:

php artisan make:event BedAssigned

然后修改 app/Events/BedAssigned.php,传入预订实例:

namespace App\Events;

use App\Models\Reservation;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class BedAssigned
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $reservation;

    public function __construct(Reservation $reservation)
    {
        $this->reservation = $reservation;
    }
}

2. 创建监听器

我们可以创建多个监听器来处理不同的后续操作,比如发送通知、更新床位状态。

监听器1:发送患者通知

运行命令生成监听器:

php artisan make:listener SendBedAssignmentNotification --event=BedAssigned

修改 app/Listeners/SendBedAssignmentNotification.php:

namespace App\Listeners;

use App\Events\BedAssigned;
use Illuminate\Support\Facades\Mail;
use App\Mail\BedAssignedMail;

class SendBedAssignmentNotification
{
    public function handle(BedAssigned $event)
    {
        $reservation = $event->reservation;
        $patient = $reservation->patient;

        // 发送邮件通知患者
        Mail::to($patient->email)->send(new BedAssignedMail($reservation));

        // 同时记录操作日志
        \Log::info("床位分配日志:患者 {$patient->name} 成功分配到 {$reservation->bed->room->name} 房间的 {$reservation->bed->name} 床位");
    }
}

监听器2:更新床位状态(可选)

如果你需要把床位状态改为“已占用”,可以再创建一个监听器:

php artisan make:listener UpdateBedStatusListener --event=BedAssigned

修改 app/Listeners/UpdateBedStatusListener.php:

namespace App\Listeners;

use App\Events\BedAssigned;

class UpdateBedStatusListener
{
    public function handle(BedAssigned $event)
    {
        $bed = $event->reservation->bed;
        // 将床位状态改为不可用
        $bed->update(['status' => false]);
    }
}

3. 创建邮件模板

运行命令生成邮件类:

php artisan make:mail BedAssignedMail

修改 app/Mail/BedAssignedMail.php:

namespace App\Mail;

use App\Models\Reservation;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class BedAssignedMail extends Mailable
{
    use Queueable, SerializesModels;

    public $reservation;

    public function __construct(Reservation $reservation)
    {
        $this->reservation = $reservation;
    }

    public function envelope()
    {
        return new Envelope(subject: '床位分配成功通知');
    }

    public function content()
    {
        return new Content(view: 'emails.bed-assigned');
    }

    public function attachments()
    {
        return [];
    }
}

然后创建邮件视图 resources/views/emails/bed-assigned.blade.php:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>床位分配成功</title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6;">
    <h2>尊敬的 {{ $reservation->patient->name }}:</h2>
    <p>您好!您的床位已成功分配,详情如下:</p>
    <ul>
        <li>房间:{{ $reservation->bed->room->name }}({{ $reservation->bed->room->floor }}楼)</li>
        <li>床位编号:{{ $reservation->bed->name }}</li>
        <li>入住日期:{{ $reservation->date_in->format('Y年m月d日') }}</li>
        <li>退房日期:{{ $reservation->date_out->format('Y年m月d日') }}</li>
    </ul>
    <p>请您按时到医院办理入住手续,如有疑问请联系医护人员。</p>
    <p>祝您早日康复!</p>
</body>
</html>

4. 注册事件与监听器

打开 app/Providers/EventServiceProvider.php,在 $listen 数组中注册事件和对应的监听器:

protected $listen = [
    \App\Events\BedAssigned::class => [
        \App\Listeners\SendBedAssignmentNotification::class,
        \App\Listeners\UpdateBedStatusListener::class,
    ],
];

这样,当你在控制器中触发 event(new BedAssigned($reservation)) 时,两个监听器会自动执行对应的操作。


内容的提问来源于stack exchange,提问作者Asmaa Muhammed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:50:52