ensureCanViewOrder($order); return $this->storeMessage( $request, $fileService, $notificationService, $order, null, 'chat/orders/' . $order->id, ); } public function storeForReclamation( Request $request, Reclamation $reclamation, FileService $fileService, NotificationService $notificationService, ): RedirectResponse { $this->ensureCanViewReclamation($reclamation); return $this->storeMessage( $request, $fileService, $notificationService, null, $reclamation, 'chat/reclamations/' . $reclamation->id, ); } private function storeMessage( Request $request, FileService $fileService, NotificationService $notificationService, ?Order $order, ?Reclamation $reclamation, string $filePath, ): RedirectResponse { $isPrivileged = hasRole(Role::ADMIN . ',' . Role::MANAGER); $validated = $request->validate([ 'message' => 'nullable|string', 'notification_type' => 'nullable|string|in:none,responsibles,all,user', 'target_user_id' => 'nullable|integer|exists:users,id,deleted_at,NULL', 'target_user_ids' => 'nullable|array', 'target_user_ids.*' => 'integer|exists:users,id,deleted_at,NULL', 'attachments' => 'nullable|array|max:5', 'attachments.*' => 'file|max:10240', ]); $notificationType = $validated['notification_type'] ?? ChatMessage::NOTIFICATION_NONE; if (!$isPrivileged) { $notificationType = ChatMessage::NOTIFICATION_NONE; } $messageText = trim((string) ($validated['message'] ?? '')); $attachments = $request->file('attachments', []); if ($messageText === '' && empty($attachments)) { return redirect()->back()->with(['danger' => 'Нужно указать сообщение или добавить файл.']); } $recipientIds = []; $targetUserId = null; if ($notificationType === ChatMessage::NOTIFICATION_USER) { $targetUserId = (int) ($validated['target_user_id'] ?? 0); if ($targetUserId < 1) { return redirect()->back()->with(['danger' => 'Нужно выбрать пользователя для уведомления.']); } $recipientIds = [$targetUserId]; } if (in_array($notificationType, [ ChatMessage::NOTIFICATION_RESPONSIBLES, ChatMessage::NOTIFICATION_ALL, ], true)) { $recipientIds = $this->resolveTargetUserIds( $notificationType, $validated['target_user_ids'] ?? [], $order, $reclamation, (int) auth()->id(), ); if (empty($recipientIds)) { return redirect()->back()->with(['danger' => 'Нужно выбрать хотя бы одного получателя уведомления.']); } } if (!in_array($notificationType, [ChatMessage::NOTIFICATION_USER], true)) { $targetUserId = null; } try { $chatMessage = ChatMessage::query()->create([ 'order_id' => $order?->id, 'reclamation_id' => $reclamation?->id, 'user_id' => (int) auth()->id(), 'target_user_id' => $targetUserId, 'notification_type' => $notificationType, 'message' => $messageText !== '' ? $messageText : null, ]); if (!empty($recipientIds)) { $chatMessage->notifiedUsers()->syncWithoutDetaching($recipientIds); } $files = []; foreach ($attachments as $attachment) { $files[] = $fileService->saveUploadedFile($filePath, $attachment); } if (!empty($files)) { $chatMessage->files()->syncWithoutDetaching(collect($files)->pluck('id')->all()); } if ($notificationType !== ChatMessage::NOTIFICATION_NONE) { $notificationService->notifyChatMessage($chatMessage->fresh([ 'user', 'targetUser', 'files', 'order.user', 'order.brigadier', 'reclamation.order', 'reclamation.user', 'reclamation.brigadier', ]), $recipientIds); } } catch (Throwable $exception) { report($exception); return redirect()->back()->with(['error' => 'Не удалось отправить сообщение в чат.']); } return redirect()->back()->with(['success' => 'Сообщение отправлено.']); } private function ensureCanViewOrder(Order $order): void { if (hasRole(Role::BRIGADIER)) { $canView = (int) $order->brigadier_id === (int) auth()->id() && in_array((int) $order->order_status_id, Order::visibleStatusIdsForBrigadier(), true); if (!$canView) { abort(403); } } } private function ensureCanViewReclamation(Reclamation $reclamation): void { if (hasRole(Role::BRIGADIER)) { $canView = (int) $reclamation->brigadier_id === (int) auth()->id() && in_array((int) $reclamation->status_id, Reclamation::visibleStatusIdsForBrigadier(), true); if (!$canView) { abort(403); } } } private function resolveTargetUserIds( string $notificationType, array $targetUserIds, ?Order $order, ?Reclamation $reclamation, int $senderId, ): array { $selectedIds = array_values(array_unique(array_map(static fn ($id) => (int) $id, $targetUserIds))); $allowedIds = match ($notificationType) { ChatMessage::NOTIFICATION_RESPONSIBLES => $this->chatResponsibleRecipientIds($order, $reclamation), ChatMessage::NOTIFICATION_ALL => User::query()->pluck('id')->map(static fn ($id) => (int) $id)->all(), default => [], }; $selectedIds = array_values(array_intersect($selectedIds, $allowedIds)); return array_values(array_diff($selectedIds, [$senderId])); } private function chatResponsibleRecipientIds(?Order $order, ?Reclamation $reclamation): array { $adminIds = User::query() ->where('role', Role::ADMIN) ->pluck('id') ->map(static fn ($id) => (int) $id) ->all(); if ($order) { return array_values(array_unique(array_filter(array_merge($adminIds, [ $order->user_id ? (int) $order->user_id : null, $order->brigadier_id ? (int) $order->brigadier_id : null, ])))); } if ($reclamation) { return array_values(array_unique(array_filter(array_merge($adminIds, [ $reclamation->user_id ? (int) $reclamation->user_id : null, $reclamation->brigadier_id ? (int) $reclamation->brigadier_id : null, ])))); } return $adminIds; } }