laravel-zap:基于 Laravel 的日程与调度管理包

The missing calendar management for Laravel

分支1Tags27
文件最后提交记录最后更新时间
5 个月前
11 个月前
5 个月前
8 个月前
6 个月前
2 个月前
2 个月前
11 个月前
11 个月前
1 年前
11 个月前
3 个月前
6 个月前
1 年前
8 个月前
Zap Logo

现代化 Laravel 应用的灵活日程管理工具

PHP Version Laravel Version License Total Downloads Why PHP

官方网站文档支持


目录


🎯 What is Zap?

Zap 是一款适用于 Laravel 的日历和日程安排包。为任何资源(医生、房间、员工等)定义可用时间预约屏蔽时间自定义日程

应用场景: 预约预订、医疗资源管理、员工排班、共享空间预订。


📦 Installation

系统要求: PHP ≥8.5 • Laravel ≥13.0

composer require laraveljutsu/zap
php artisan vendor:publish --provider="Zap\ZapServiceProvider"

UUID/ULID: 如果您的应用使用非整数主键,请在迁移之前阅读自定义模型支持。您可能需要修改迁移和配置。

php artisan migrate

让模型支持日程安排: 添加 HasSchedules trait。

use Zap\Models\Concerns\HasSchedules;

class Doctor extends Model
{
    use HasSchedules;
}

🧩 核心概念

类型 用途 重叠规则
Availability 资源可被预订的时间 允许重叠
Appointment 已预订 / 已安排的事件 互斥
Blocked 禁止预订的时间 互斥
Custom 自定义规则(重叠等) 自定义

🚀 快速开始

use Zap\Facades\Zap;

// 1. Working hours
Zap::for($doctor)
    ->named('Office Hours')
    ->availability()
    ->forYear(2025)
    ->addPeriod('09:00', '12:00')
    ->addPeriod('14:00', '17:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
    ->save();

// 2. Block lunch
Zap::for($doctor)
    ->named('Lunch Break')
    ->blocked()
    ->forYear(2025)
    ->addPeriod('12:00', '13:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
    ->save();

// 3. Create an appointment
Zap::for($doctor)
    ->named('Patient A - Consultation')
    ->appointment()
    ->from('2025-01-15')
    ->addPeriod('10:00', '11:00')
    ->withMetadata(['patient_id' => 1, 'type' => 'consultation'])
    ->save();

// 4. Get bookable slots (60 min, 15 min buffer)
$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

// 5. Next available slot
$nextSlot = $doctor->getNextBookableSlot('2025-01-15', 60, 15);

💡 如果您偏好,可以使用 zap() 辅助函数替代门面:zap()->for($doctor)->...


📅 日程模式

重复周期概览

模式 方法 / 示例
每日 daily()
每周(指定星期几) weekly(['monday', 'friday'])
每周 + 时间段 weekDays(['monday', 'friday'], '09:00', '17:00')
单/双周 weeklyOdd()weeklyEven()(+ weekOddDays / weekEvenDays
每两周 biweekly(['tuesday'], $startsOn?)
每月(指定日期) monthly(['days_of_month' => [1, 15]])
每两月 / 每季度 / 每半年 / 每年 bimonthly()quarterly()semiannually()annually() + 配置
指定序数的星期几 firstWednesdayOfMonth()secondFridayOfMonth()lastMondayOfMonth()
每 N 周 everyThreeWeeks(),… everyFiftyTwoWeeks()
每 N 月 everyFourMonths(),… everyElevenMonths()

重复周期示例

每日和每周

$schedule->daily()->from('2025-01-01')->to('2025-12-31');
$schedule->weekly(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->weekDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025);
$schedule->weeklyOdd(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->weeklyEven(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->biweekly(['tuesday', 'thursday'], '2025-01-07')->from('2025-01-07')->to('2025-03-31');

每月(按每月的日期)

$schedule->monthly(['days_of_month' => [1, 15]])->forYear(2025);
$schedule->bimonthly(['days_of_month' => [5, 20], 'start_month' => 2])->from('2025-01-05')->to('2025-06-30');
$schedule->quarterly(['days_of_month' => [7, 21], 'start_month' => 2])->from('2025-02-15')->to('2025-11-15');
$schedule->semiannually(['days_of_month' => [10], 'start_month' => 3])->from('2025-03-10')->to('2025-12-10');
$schedule->annually(['days_of_month' => [1, 15], 'start_month' => 4])->from('2025-04-01')->to('2026-04-01');

每月序数周几(每月的第 1、第 2、第 3、第 4 个或最后一个周几)

$schedule->firstWednesdayOfMonth()->forYear(2025);   // Every 1st Wednesday
$schedule->secondFridayOfMonth()->forYear(2025);     // Every 2nd Friday
$schedule->lastMondayOfMonth()->forYear(2025);      // Every last Monday
// Also: thirdTuesdayOfMonth(), fourthSaturdayOfMonth(), lastSundayOfMonth(), etc.

动态间隔

$schedule->everyThreeWeeks(['monday', 'friday'])->from('2025-01-06')->to('2025-12-31');
$schedule->everyFourWeeks(['tuesday'], '2025-01-06')->from('2025-01-13');
$schedule->everyFourMonths(['day_of_month' => 15])->forYear(2025);
$schedule->everyFiveMonths(['days_of_month' => [1, 15], 'start_month' => 2])->forYear(2025);

日期范围

$schedule->from('2025-01-15');                           // Start
$schedule->on('2025-01-15');                             // Alias for from()
$schedule->from('2025-01-01')->to('2025-12-31');        // Range
$schedule->between('2025-01-01', '2025-12-31');          // Same
$schedule->forYear(2025);                                // Full year

时间段

$schedule->addPeriod('09:00', '17:00');
$schedule->addPeriod('09:00', '12:00');
$schedule->addPeriod('14:00', '17:00');

🔍 查询与可用性检查

需求 方法
今天是否有可预订时段? $model->isBookableAt('2025-01-15', 60)
时间段是否可预订? $model->isBookableAtTime('2025-01-15', '09:00', '09:30')
时间段是否可预订(自定义时段)? $model->isBookableAtTime('2025-01-15', '09:30', '10:00', null, 30)
时间段是否可预订(自定义时段 + 缓冲时间)? $model->isBookableAtTime('2025-01-15', '09:45', '10:15', null, 30, 15)
列出可预订时段 $model->getBookableSlots('2025-01-15', 60, 15)
下一个可预订时段 $model->getNextBookableSlot('2025-01-15', 60, 15)
日程冲突查询 Zap::findConflicts($schedule) / Zap::hasConflicts($schedule)
指定日期的日程 $model->schedulesForDate('2025-01-15')->get()
日期范围内的日程 $model->schedulesForDateRange('2025-01-01', '2025-01-31')->get()
按类型筛选 $model->appointmentSchedules(), availabilitySchedules(), blockedSchedules()
日程类型检查 $schedule->isAvailability(), isAppointment(), isBlocked()

⚠️ isAvailableAt() 已弃用。建议使用 isBookableAt()isBookableAtTime()getBookableSlots()


💼 实际应用示例

医生

Zap::for($doctor)->named('Office Hours')->availability()->forYear(2025)
    ->addPeriod('09:00', '12:00')->addPeriod('14:00', '17:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])->save();

Zap::for($doctor)->named('Lunch Break')->blocked()->forYear(2025)
    ->addPeriod('12:00', '13:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])->save();

Zap::for($doctor)->named('Patient A - Checkup')->appointment()
    ->from('2025-01-15')->addPeriod('10:00', '11:00')->withMetadata(['patient_id' => 1])->save();

$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

会议室

Zap::for($room)->named('Conference Room A')->availability()
    ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '08:00', '18:00')
    ->forYear(2025)->save();

Zap::for($room)->named('Board Meeting')->appointment()
    ->from('2025-03-15')->addPeriod('09:00', '11:00')
    ->withMetadata(['organizer' => 'john@company.com'])->save();

员工(含休假)

Zap::for($employee)->named('Regular Shift')->availability()
    ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '09:00', '17:00')
    ->forYear(2025)->save();

Zap::for($employee)->named('Vacation Leave')->blocked()
    ->between('2025-06-01', '2025-06-15')->addPeriod('00:00', '23:59')->save();

⚙️ 配置

发布资源:

php artisan vendor:publish --tag=zap-migrations
php artisan vendor:publish --tag=zap-config

config/zap.php 中的重要键:time_slots.buffer_minutesdefault_rules.no_overlapconflict_detectionvalidation


🛡️ 高级功能

自定义日程安排和规则

Zap::for($user)->named('Custom Event')->custom()
    ->from('2025-01-15')->addPeriod('15:00', '16:00')->noOverlap()->save();

元数据

->withMetadata(['patient_id' => 1, 'type' => 'consultation', 'notes' => 'Follow-up'])

验证规则: noOverlap()allowOverlap()workingHoursOnly('09:00', '17:00')maxDuration(120)noWeekends()

自定义模型支持(UUIDs)

如果您的应用使用UUIDs/ULIDs作为主键:

  1. 模型 — 扩展 Zap\Models\ScheduleZap\Models\SchedulePeriod,添加 Laravel 的 HasUuids trait。同时,将 HasUuids 添加到您的可调度模型(例如 Doctor)中。
  2. 配置 — 在 config/zap.php 中,将 models.schedulemodels.schedule_period 设置为您扩展的类。
  3. 迁移 — 发布迁移后,在 schedules 和 schedule_periods 表中,将 id() 改为 uuid('id')->primary(),将 morphs('schedulable') 改为 uuidMorphs('schedulable'),并将 foreignId('schedule_id') 改为 foreignUuid('schedule_id')

请在运行迁移之前执行此操作。


🧠 AI 代理支持

Zap 提供 Laravel Boost 2.0 技能。安装 Boost 后,代理将获得有关 API 的准确知识。

技能 内容
zap-schedules 类型、构建器 API、验证、冲突检测
zap-availability 可预订时段、可用性检查、查询
zap-recurrence 所有重复模式(每日、每周、单双周、每月、指定周几、动态)

无需额外配置。


🤝 贡献

欢迎贡献。请遵循 PSR-12 标准并添加测试。

git clone https://github.com/ludoguenet/laravel-zap.git
cd laravel-zap
composer install
composer pest

📄 许可证

MIT 许可证

🔒 安全

请将问题报告发送至 ludo@epekta.com(请勿使用公共问题跟踪器)。


Ludovic Guénet 满怀 💛 为 Laravel 社区打造

项目介绍

为Laravel提供的迅捷如闪电般的日程管理方案【此简介由AI生成】

定制我的领域
181.46 K110访问 GitHub