File manager - Edit - /home/webapp69.cm.in.th/u69319090028/Shop/app/Services/NotificationService.php
Back
<?php /** * NotificationService * Central dispatcher for creating and broadcasting notifications across modules * Connects User, Order, Payment, Seller, Admin, Announcement, Review, Report */ require_once __DIR__ . '/../Models/Notification.php'; require_once __DIR__ . '/../Models/User.php'; require_once __DIR__ . '/../Models/Store.php'; require_once __DIR__ . '/../Models/Order.php'; require_once __DIR__ . '/../Models/Product.php'; require_once __DIR__ . '/Notification/NotificationChannelInterface.php'; class NotificationService { private Notification $notificationModel; private array $channels = []; public function __construct() { $this->notificationModel = new Notification(); } /** * Send notification to a specific user */ public function notifyUser( int $userId, string $type, string $title, string $message, ?string $targetType = null, ?int $targetId = null, ?string $actionUrl = null, string $roleTarget = 'customer' ): int { // Prevent duplicate spam within 3 seconds for exact same user, type, and target $recent = $this->notificationModel->fetchOne( "SELECT id FROM `notifications` WHERE `user_id` = :uid AND `type` = :type AND `target_type` <=> :tt AND `target_id` <=> :tid AND `created_at` >= DATE_SUB(NOW(), INTERVAL 3 SECOND) LIMIT 1", [':uid' => $userId, ':type' => $type, ':tt' => $targetType, ':tid' => $targetId] ); if ($recent) { return (int)$recent['id']; } $notifId = $this->notificationModel->createNotification([ 'user_id' => $userId, 'role_target' => $roleTarget, 'type' => $type, 'title' => $title, 'message' => $message, 'target_type' => $targetType, 'target_id' => $targetId, 'action_url' => $actionUrl ]); // Broadcast to attached channels (e.g. Email/Push in future adapters) foreach ($this->channels as $channel) { $channel->send($userId, [ 'id' => $notifId, 'type' => $type, 'title' => $title, 'message' => $message, 'action_url' => $actionUrl ]); } return $notifId; } /** * Notify Store Owner (Seller) */ public function notifyStoreOwner( int $storeId, string $type, string $title, string $message, ?string $targetType = null, ?int $targetId = null, ?string $actionUrl = null ): ?int { $store = (new Store())->findById($storeId); if (!$store || empty($store['user_id'])) { return null; } return $this->notifyUser( (int)$store['user_id'], $type, $title, $message, $targetType, $targetId, $actionUrl, 'seller' ); } /** * Notify all Admins or Admins with specific permission */ public function notifyAdmins( string $type, string $title, string $message, ?string $targetType = null, ?int $targetId = null, ?string $actionUrl = null, ?string $requiredPermission = null ): int { $db = Database::getInstance(); $sql = "SELECT DISTINCT u.id FROM `users` u JOIN `user_roles` ur ON u.id = ur.user_id JOIN `roles` r ON ur.role_id = r.id WHERE r.name = 'admin' AND u.status = 'active'"; $admins = $db->query($sql)->fetchAll(); $count = 0; foreach ($admins as $admin) { $adminId = (int)$admin['id']; $this->notifyUser( $adminId, $type, $title, $message, $targetType, $targetId, $actionUrl, 'admin' ); $count++; } return $count; } // ── Pre-built Event Trigger Handlers ───────────────────────────── /** * Trigger: Order Placed -> Notify Customer and Store Owner */ public function onOrderPlaced(int $orderId): void { $order = (new Order())->findById($orderId); if (!$order) return; // 1. Notify Customer $this->notifyUser( (int)$order['customer_id'], 'order_created', 'สั่งซื้อสินค้าสำเร็จ', "คำสั่งซื้อ #{$order['order_no']} ได้รับการบันทึกแล้ว กรุณาชำระเงินเพื่อดำเนินการจัดส่ง", 'order', $orderId, '/account/orders/' . $order['order_no'], 'customer' ); // 2. Notify Store Owner $this->notifyStoreOwner( (int)$order['store_id'], 'order_new', 'มีคำสั่งซื้อใหม่!', "มีคำสั่งซื้อใหม่ #{$order['order_no']} ยอดรวม ฿" . number_format((float)$order['total_amount'], 2), 'order', $orderId, '/seller/orders/' . $order['order_no'] ); } /** * Trigger: Order Status Changed -> Notify Customer */ public function onOrderStatusChanged(int $orderId, string $newStatus): void { $order = (new Order())->findById($orderId); if (!$order) return; $statusTitles = [ 'preparing' => 'ร้านค้ากำลังเตรียมสินค้า', 'shipping' => 'สินค้าของคุณกำลังจัดส่ง', 'completed' => 'คำสั่งซื้อจัดส่งสำเร็จแล้ว', 'cancelled' => 'คำสั่งซื้อถูกยกเลิก' ]; $statusMsgs = [ 'preparing' => "ร้านค้ากำลังจัดเตรียมสินค้าสำหรับคำสั่งซื้อ #{$order['order_no']}", 'shipping' => "คำสั่งซื้อ #{$order['order_no']} อยู่ระหว่างการนำส่ง" . (!empty($order['tracking_no']) ? " (เลขพัสดุ: {$order['tracking_no']})" : ""), 'completed' => "คำสั่งซื้อ #{$order['order_no']} จัดส่งถึงปลายทางแล้ว คุณสามารถให้คะแนนและรีวิวสินค้าได้แล้ว", 'cancelled' => "คำสั่งซื้อ #{$order['order_no']} ได้รับการยกเลิก" ]; $title = $statusTitles[$newStatus] ?? 'อัปเดตสถานะคำสั่งซื้อ'; $msg = $statusMsgs[$newStatus] ?? "คำสั่งซื้อ #{$order['order_no']} เปลี่ยนสถานะเป็น {$newStatus}"; $this->notifyUser( (int)$order['customer_id'], 'order_status_changed', $title, $msg, 'order', $orderId, '/account/orders/' . $order['order_no'], 'customer' ); } /** * Trigger: Order Cancelled -> Notify Customer and Store Seller */ public function onOrderCancelled(int $orderId, string $orderNo, string $reason, int $customerId, int $storeId): void { // 1. Notify Customer $this->notifyUser( $customerId, 'order_cancelled', 'คำสั่งซื้อถูกยกเลิกแล้ว', "คำสั่งซื้อ #{$orderNo} ได้รับการยกเลิกเรียบร้อยแล้ว เหตุผล: {$reason}", 'order', $orderId, '/account/orders/' . $orderNo, 'customer' ); // 2. Notify Store Seller $store = (new Store())->findById($storeId); if ($store && !empty($store['user_id'])) { $this->notifyUser( (int)$store['user_id'], 'order_cancelled', 'ลูกค้ายกเลิกคำสั่งซื้อ', "คำสั่งซื้อ #{$orderNo} ถูกยกเลิกโดยลูกค้า เหตุผล: {$reason}", 'order', $orderId, '/seller/orders/' . $orderNo, 'seller' ); } } /** * Trigger: Payment Submitted -> Notify Admins for verification */ public function onPaymentSlipSubmitted(int $paymentId, string $orderNo): void { $this->notifyAdmins( 'payment_slip_submitted', 'มีสลิปโอนเงินรอตรวจสอบ', "มีสลิปโอนเงินสำหรับคำสั่งซื้อ #{$orderNo} รอการตรวจสอบและอนุมัติ", 'payment', $paymentId, '/admin/payments/' . $orderNo, 'payment.verify' ); } /** * Trigger: Payment Verified (Confirmed / Rejected) -> Notify Customer */ public function onPaymentVerified(int $userId, string $orderNo, string $paymentStatus, ?string $reason = null): void { if ($paymentStatus === 'confirmed') { $this->notifyUser( $userId, 'payment_confirmed', 'การชำระเงินสำเร็จ', "การชำระเงินสำหรับคำสั่งซื้อ #{$orderNo} ได้รับการยืนยันแล้ว ร้านค้าจะเริ่มจัดเตรียมสินค้า", 'order', null, '/account/orders/' . $orderNo, 'customer' ); } else { $msg = "สลิปการโอนเงินสำหรับคำสั่งซื้อ #{$orderNo} ไม่ผ่านการตรวจสอบ"; if (!empty($reason)) { $msg .= " (เหตุผล: {$reason})"; } $msg .= " กรุณาอัปโหลดสลิปใหม่"; $this->notifyUser( $userId, 'payment_rejected', 'การชำระเงินไม่ผ่านการอนุมัติ', $msg, 'payment', null, '/payment/' . $orderNo, 'customer' ); } } /** * Trigger: Product Review Received -> Notify Store Owner */ public function onReviewReceived(int $storeId, int $productId, string $productName, int $rating): void { $this->notifyStoreOwner( $storeId, 'review_received', 'ได้รับรีวิวใหม่จากลูกค้า', "สินค้า \"{$productName}\" ได้รับคะแนน {$rating} ดาวจากผู้ซื้อ", 'product', $productId, '/seller/reviews' ); } /** * Trigger: Low Stock Alert -> Notify Store Owner */ public function onLowStock(int $storeId, int $productId, string $productName, int $remainingStock): void { $this->notifyStoreOwner( $storeId, 'low_stock', 'แจ้งเตือนสินค้าใกล้หมดสต็อก', "สินค้า \"{$productName}\" เหลือสต็อกเพียง {$remainingStock} ชิ้น กรุณาเติมสต็อก", 'product', $productId, '/seller/inventory' ); } // ── User Interaction Helpers ───────────────────────────────────── public function getUserNotifications(int $userId, array $filters = [], int $page = 1, int $perPage = 20): array { $page = max(1, $page); $offset = ($page - 1) * $perPage; $items = $this->notificationModel->getNotificationsByUser($userId, $filters, $perPage, $offset); $total = $this->notificationModel->countNotificationsByUser($userId, $filters); $unreadCount = $this->notificationModel->getUnreadCount($userId); return [ 'notifications' => $items, 'total_count' => $total, 'unread_count' => $unreadCount, 'page' => $page, 'per_page' => $perPage, 'total_pages' => ceil($total / $perPage) ]; } public function getUnreadCount(int $userId, ?string $roleTarget = null): int { return $this->notificationModel->getUnreadCount($userId, $roleTarget); } public function getLatestPreview(int $userId, int $limit = 5, ?string $roleTarget = null): array { return $this->notificationModel->getLatestNotifications($userId, $limit, $roleTarget); } public function markAsRead(int $notificationId, int $userId): bool { return $this->notificationModel->markAsRead($notificationId, $userId); } public function markAllAsRead(int $userId, ?string $roleTarget = null): bool { return $this->notificationModel->markAllAsRead($userId, $roleTarget); } public function deleteNotification(int $notificationId, int $userId): bool { return $this->notificationModel->deleteNotification($notificationId, $userId); } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.29 |
proxy
|
phpinfo
|
Settings