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.3 KiB
PHP
75 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\API;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
|
|
class NotificationController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
try {
|
|
$user = $request->user();
|
|
$unreadCount = $user->unreadNotifications()->count();
|
|
$notifications = $user->notifications()
|
|
->orderByDesc('created_at')
|
|
->paginate(20);
|
|
|
|
$items = $notifications->map(fn($n) => array_merge($n->data, [
|
|
'id' => $n->id,
|
|
'read_at' => $n->read_at?->toISOString(),
|
|
'created_at' => $n->created_at->toISOString(),
|
|
]));
|
|
|
|
return response()->json([
|
|
'status' => true,
|
|
'data' => $items,
|
|
'unread_count' => $unreadCount,
|
|
'meta' => [
|
|
'current_page' => $notifications->currentPage(),
|
|
'last_page' => $notifications->lastPage(),
|
|
'total' => $notifications->total(),
|
|
],
|
|
]);
|
|
} catch (\Throwable) {
|
|
return response()->json(['status' => true, 'data' => [], 'unread_count' => 0, 'meta' => ['current_page' => 1, 'last_page' => 1, 'total' => 0]]);
|
|
}
|
|
}
|
|
|
|
public function unreadCount(Request $request)
|
|
{
|
|
try {
|
|
return response()->json([
|
|
'status' => true,
|
|
'data' => ['count' => $request->user()->unreadNotifications()->count()],
|
|
]);
|
|
} catch (\Throwable) {
|
|
return response()->json(['status' => true, 'data' => ['count' => 0]]);
|
|
}
|
|
}
|
|
|
|
public function markRead(Request $request, string $id)
|
|
{
|
|
$notification = $request->user()->notifications()->findOrFail($id);
|
|
$notification->markAsRead();
|
|
|
|
return response()->json(['status' => true]);
|
|
}
|
|
|
|
public function markAllRead(Request $request)
|
|
{
|
|
$request->user()->unreadNotifications->markAsRead();
|
|
|
|
return response()->json(['status' => true]);
|
|
}
|
|
|
|
public function destroy(Request $request, string $id)
|
|
{
|
|
$notification = $request->user()->notifications()->findOrFail($id);
|
|
$notification->delete();
|
|
|
|
return response()->json(null, 204);
|
|
}
|
|
}
|