03f8caf3e9
後端 - 新增 6 個 Notification class(預約建立/確認/拒絕/取消/完成、收到評價),database + mail 雙 channel - 新增 NotificationController(list / unread-count / markRead / markAllRead / destroy) - 整合通知觸發至 MemberBookingController、ProviderBookingController、CompleteFinishedBookings、ReviewController - 新增 notifications / jobs / failed_jobs migration - Docker Compose 加入 queue-worker、mailpit service - DivingOffer 補上 provider() 關聯 前端 - 新增 notificationStore(Polling 30s/60s 自適應 + Page Visibility API) - 新增 NotificationBell(未讀 Badge)、NotificationDrawer(側邊通知中心) - main.js:auth store init 前置於 router.use(),修正 beforeEach guard 時序問題 - notificationAxios:依路徑動態選擇 member/coach token - NotificationDrawer:改用 new URL().pathname 提取 action_url 路徑 OpenSpec - 歸檔 notification-system change - 同步 notification-core / notification-email / notification-triggers specs 至主規格 - 更新 booking-lifecycle / review-lifecycle spec(補充通知觸發 requirement) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.7 KiB
PHP
75 lines
2.7 KiB
PHP
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use App\Models\Booking;
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Notifications\Messages\MailMessage;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class BookingCancelledNotification extends Notification implements ShouldQueue
|
||
{
|
||
use Queueable;
|
||
|
||
public int $tries = 3;
|
||
|
||
public function __construct(
|
||
public readonly Booking $booking,
|
||
public readonly string $cancelledBy,
|
||
) {}
|
||
|
||
public function via(object $notifiable): array
|
||
{
|
||
return ['database', 'mail'];
|
||
}
|
||
|
||
public function toArray(object $notifiable): array
|
||
{
|
||
$offer = $this->booking->schedule->divingOffer;
|
||
$date = $this->booking->schedule->scheduled_date->toDateString();
|
||
|
||
if ($this->cancelledBy === 'member') {
|
||
return [
|
||
'type' => 'booking_cancelled',
|
||
'title' => '學員取消了預約',
|
||
'body' => "學員已取消《{$offer->title}》的預約(時段:{$date})",
|
||
'action_url' => config('app.frontend_url') . '/coach/bookings',
|
||
'related_id' => $this->booking->id,
|
||
'related_type' => 'booking',
|
||
];
|
||
}
|
||
|
||
return [
|
||
'type' => 'booking_cancelled',
|
||
'title' => '教練取消了你的預約',
|
||
'body' => "教練已取消你的《{$offer->title}》預約(時段:{$date}),如有疑問請聯繫教練",
|
||
'action_url' => config('app.frontend_url') . '/my-bookings',
|
||
'related_id' => $this->booking->id,
|
||
'related_type' => 'booking',
|
||
];
|
||
}
|
||
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
$offer = $this->booking->schedule->divingOffer;
|
||
$date = $this->booking->schedule->scheduled_date->toDateString();
|
||
|
||
if ($this->cancelledBy === 'member') {
|
||
return (new MailMessage)
|
||
->subject('預約已取消 — CFDivePlatform')
|
||
->greeting('通知')
|
||
->line("學員已取消《{$offer->title}》的預約(時段:{$date})。")
|
||
->action('查看所有預約', config('app.frontend_url') . '/coach/bookings')
|
||
->salutation('CFDivePlatform 團隊');
|
||
}
|
||
|
||
return (new MailMessage)
|
||
->subject('你的預約已取消 — CFDivePlatform')
|
||
->greeting('通知')
|
||
->line("教練已取消你的《{$offer->title}》預約(時段:{$date})。如有疑問,請聯繫課程教練。")
|
||
->action('查看預約', config('app.frontend_url') . '/my-bookings')
|
||
->salutation('CFDivePlatform 團隊');
|
||
}
|
||
}
|