From 29cc20207f7bad8392d043172ea05ecfe726fafa Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sat, 26 Jul 2025 11:49:32 -0700 Subject: [PATCH] add ticket tool for AGI --- hesabixCore/src/AiTool/TicketService.php | 62 ++++++ hesabixCore/src/Cog/TicketService.php | 193 ++++++++++++++++++ .../src/Controller/SupportController.php | 127 +----------- hesabixCore/src/Service/AGI/AGIService.php | 17 ++ .../src/Service/AGI/Promps/PromptService.php | 13 +- .../src/Service/AGI/Promps/TicketService.php | 146 +++++++++++++ 6 files changed, 438 insertions(+), 120 deletions(-) create mode 100644 hesabixCore/src/AiTool/TicketService.php create mode 100644 hesabixCore/src/Cog/TicketService.php create mode 100644 hesabixCore/src/Service/AGI/Promps/TicketService.php diff --git a/hesabixCore/src/AiTool/TicketService.php b/hesabixCore/src/AiTool/TicketService.php new file mode 100644 index 0000000..29a9459 --- /dev/null +++ b/hesabixCore/src/AiTool/TicketService.php @@ -0,0 +1,62 @@ +cogTicketService->getUserTickets($user); + } catch (\Exception $e) { + return [ + 'error' => 'خطا در دریافت لیست تیکت‌ها: ' . $e->getMessage() + ]; + } + } + + /** + * ایجاد یا به‌روزرسانی تیکت + */ + public function createOrUpdateTicket(array $params, array $files, UserInterface $user, string $id = ''): array + { + try { + return $this->cogTicketService->createOrUpdateTicket($params, $files, $user, $id); + } catch (\Exception $e) { + return [ + 'error' => 'خطا در ایجاد/به‌روزرسانی تیکت: ' . $e->getMessage() + ]; + } + } + + /** + * دریافت جزئیات تیکت و پاسخ‌های آن + */ + public function getTicketDetails(string $id, UserInterface $user): array + { + if (!$id) { + return [ + 'error' => 'شناسه تیکت الزامی است' + ]; + } + + try { + return $this->cogTicketService->getTicketDetails($id, $user); + } catch (\Exception $e) { + return [ + 'error' => 'خطا در دریافت جزئیات تیکت: ' . $e->getMessage() + ]; + } + } +} diff --git a/hesabixCore/src/Cog/TicketService.php b/hesabixCore/src/Cog/TicketService.php new file mode 100644 index 0000000..bca2651 --- /dev/null +++ b/hesabixCore/src/Cog/TicketService.php @@ -0,0 +1,193 @@ + 1, 'message' => 'تیکت یافت نشد.']; + private const ERROR_INVALID_PARAMS = ['error' => 999, 'message' => 'تمام موارد لازم را وارد کنید.']; + + public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly Explore $explore, + private readonly Jdate $jdate, + private readonly registryMGR $registryMGR, + private readonly SMS $sms, + private readonly string $uploadDirectory + ) { + } + + /** + * Get list of support tickets for a user + */ + public function getUserTickets(UserInterface $user): array + { + $items = $this->entityManager->getRepository(Support::class)->findBy( + ['submitter' => $user, 'main' => 0], + ['id' => 'DESC'] + ); + + return array_map(function ($item) use ($user) { + return $this->explore->ExploreSupportTicket($item, $user); + }, $items); + } + + /** + * Create or update a support ticket + */ + public function createOrUpdateTicket(array $params, array $files, UserInterface $user, string $id = ''): array + { + if ($id === '') { + return $this->createNewTicket($params, $files, $user); + } + + return $this->replyToTicket($params, $files, $user, $id); + } + + private function createNewTicket(array $params, array $files, UserInterface $user): array + { + if (!isset($params['title'], $params['body'])) { + return self::ERROR_INVALID_PARAMS; + } + + $item = new Support(); + $item->setBody($params['body']) + ->setTitle($params['title']) + ->setDateSubmit(time()) + ->setSubmitter($user) + ->setMain(0) + ->setCode($this->generateRandomString(8)) + ->setState('در حال پیگیری'); + + // چک کردن مالکیت کسب‌وکار + $this->handleBusinessOwnership($item, $params['bid'] ?? null, $user); + + $this->entityManager->persist($item); + $this->entityManager->flush(); + + $fileName = $this->handleFileUpload($files, $item->getId()); + if ($fileName) { + $item->setFileName($fileName); + $this->entityManager->persist($item); + $this->entityManager->flush(); + } + + $this->sms->send([$item->getId()], $this->registryMGR->get('sms', 'ticketRec'), $this->registryMGR->get('ticket', 'managerMobile')); + + return [ + 'error' => 0, + 'message' => 'ok', + 'url' => $item->getId(), + 'files' => $fileName + ]; + } + + private function replyToTicket(array $params, array $files, UserInterface $user, string $id): array + { + if (!isset($params['body'])) { + return self::ERROR_INVALID_PARAMS; + } + + $upper = $this->getTicket($id); + if (!$upper) { + return self::ERROR_TICKET_NOT_FOUND; + } + + $item = new Support(); + $item->setMain($upper->getId()) + ->setBody($params['body']) + ->setTitle($upper->getTitle()) + ->setDateSubmit(time()) + ->setSubmitter($user) + ->setState('در حال پیگیری'); + + $this->entityManager->persist($item); + $this->entityManager->flush(); + + $fileName = $this->handleFileUpload($files, $item->getId()); + if ($fileName) { + $item->setFileName($fileName); + } + + $this->entityManager->persist($item); + $upper->setState('در حال پیگیری'); + $this->entityManager->persist($upper); + $this->entityManager->flush(); + + $this->sms->send([$item->getId()], $this->registryMGR->get('sms', 'ticketRec'), $this->registryMGR->get('ticket', 'managerMobile')); + + return [ + 'error' => 0, + 'message' => 'ok', + 'url' => $item->getId(), + 'files' => $fileName + ]; + } + + private function handleBusinessOwnership(Support $support, ?string $businessId, UserInterface $user): void + { + if ($businessId) { + $business = $this->entityManager->getRepository(Business::class)->find($businessId); + if ($business && $business->getOwner() === $user) { + $support->setBid($business); + return; + } + } + $support->setBid(null); + } + + private function getTicket(string $id): ?Support + { + return $this->entityManager->getRepository(Support::class)->find($id); + } + + private function generateRandomString(int $length = 32): string + { + return substr(str_shuffle(str_repeat('23456789ABCDEFGHJKLMNPQRSTUVWXYZ', ceil($length / 32))), 1, $length); + } + + private function handleFileUpload(array $files, int $ticketId): ?string + { + if (!file_exists($this->uploadDirectory)) { + mkdir($this->uploadDirectory, 0777, true); + } + + if (!empty($files)) { + $file = $files[0]; + $extension = $file->getClientOriginalExtension(); + $fileName = $ticketId . '.' . $extension; + $file->move($this->uploadDirectory, $fileName); + return $fileName; + } + return null; + } + + /** + * Get ticket details with its replies + */ + public function getTicketDetails(string $id, UserInterface $user): array + { + $ticket = $this->entityManager->getRepository(Support::class)->find($id); + if (!$ticket || $ticket->getSubmitter() !== $user) { + throw new AccessDeniedException('شما اجازه دسترسی به این تیکت را ندارید.'); + } + + $replies = $this->entityManager->getRepository(Support::class)->findBy(['main' => $ticket->getId()]); + $repliesArray = array_map(fn($reply) => $this->explore->ExploreSupportTicket($reply, $user), $replies); + + return [ + 'item' => $this->explore->ExploreSupportTicket($ticket, $user), + 'replays' => $repliesArray + ]; + } +} diff --git a/hesabixCore/src/Controller/SupportController.php b/hesabixCore/src/Controller/SupportController.php index c9c73df..0c1e278 100644 --- a/hesabixCore/src/Controller/SupportController.php +++ b/hesabixCore/src/Controller/SupportController.php @@ -248,139 +248,28 @@ class SupportController extends AbstractController } #[Route('/api/support/list', name: 'app_support_list')] - public function app_support_list(Jdate $jdate, EntityManagerInterface $entityManager, Explore $explore): JsonResponse + public function app_support_list(TicketService $ticketService): JsonResponse { - $items = $entityManager->getRepository(Support::class)->findBy( - ['submitter' => $this->getUser(), 'main' => 0], - ['id' => 'DESC'] - ); - - // استفاده از Explore برای تبدیل اشیاء به آرایه - $serializedItems = array_map(function ($item) use ($explore, $jdate) { - return $explore->ExploreSupportTicket($item, $this->getUser()); - }, $items); - - return $this->json($serializedItems); + return $this->json($ticketService->getUserTickets($this->getUser())); } #[Route('/api/support/mod/{id}', name: 'app_support_mod')] public function app_support_mod( - registryMGR $registryMGR, - SMS $SMS, + TicketService $ticketService, Request $request, - EntityManagerInterface $entityManager, string $id = '' ): JsonResponse { $params = $request->getPayload()->all(); - $uploadDirectory = $this->getParameter('SupportFilesDir'); - if (!file_exists($uploadDirectory)) { - mkdir($uploadDirectory, 0777, true); - } - - if ($id === '') { - if (!isset($params['title'], $params['body'])) { - return $this->json(self::ERROR_INVALID_PARAMS); - } - - $item = new Support(); - $item->setBody($params['body']) - ->setTitle($params['title']) - ->setDateSubmit(time()) - ->setSubmitter($this->getUser()) - ->setMain(0) - ->setCode($this->randomString(8)) - ->setState('در حال پیگیری'); - - // چک کردن مالکیت کسب‌وکار - $bid = $params['bid'] ?? null; - if ($bid) { - $business = $entityManager->getRepository(Business::class)->find($bid); - if ($business && $business->getOwner() === $this->getUser()) { - $item->setBid($business); // فقط در صورتی که کاربر مالک باشد - } else { - $item->setBid(null); // اگر مالک نباشد، bid خالی می‌ماند - } - } else { - $item->setBid(null); // اگر bid ارسال نشده باشد - } - - $entityManager->persist($item); - $entityManager->flush(); - - $fileName = $this->handleFileUpload($request, $uploadDirectory, $item->getId()); - if ($fileName) { - $item->setFileName($fileName); - } - - $entityManager->persist($item); - $entityManager->flush(); - - $SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketRec'), $registryMGR->get('ticket', 'managerMobile')); - - return $this->json([ - 'error' => 0, - 'message' => 'ok', - 'url' => $item->getId(), - 'files' => $fileName - ]); - } - - if (!isset($params['body'])) { - return $this->json(self::ERROR_INVALID_PARAMS); - } - - $upper = $this->getTicket($entityManager, $id); - if (!$upper) { - return $this->json(self::ERROR_TICKET_NOT_FOUND); - } - - $item = new Support(); - $item->setMain($upper->getId()) - ->setBody($params['body']) - ->setTitle($upper->getTitle()) - ->setDateSubmit(time()) - ->setSubmitter($this->getUser()) - ->setState('در حال پیگیری'); - - $entityManager->persist($item); - $entityManager->flush(); - - $fileName = $this->handleFileUpload($request, $uploadDirectory, $item->getId()); - if ($fileName) { - $item->setFileName($fileName); - } - - $entityManager->persist($item); - $upper->setState('در حال پیگیری'); - $entityManager->persist($upper); - $entityManager->flush(); - - $SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketRec'), $registryMGR->get('ticket', 'managerMobile')); - - return $this->json([ - 'error' => 0, - 'message' => 'ok', - 'url' => $item->getId(), - 'files' => $fileName - ]); + $files = $request->files->get('files') ?? []; + + return $this->json($ticketService->createOrUpdateTicket($params, $files, $this->getUser(), $id)); } #[Route('/api/support/view/{id}', name: 'app_support_view')] - public function app_support_view(EntityManagerInterface $entityManager, string $id): JsonResponse + public function app_support_view(TicketService $ticketService, string $id): JsonResponse { - $item = $this->getTicket($entityManager, $id, true); - if (!$item) { - throw $this->createAccessDeniedException(); - } - - $replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]); - $replaysArray = array_map(fn($replay) => Explore::ExploreSupportTicket($replay, $this->getUser()), $replays); - - return $this->json([ - 'item' => Explore::ExploreSupportTicket($item, $this->getUser()), - 'replays' => $replaysArray - ]); + return $this->json($ticketService->getTicketDetails($id, $this->getUser())); } #[Route('/api/support/download/file/{id}', name: 'app_support_download_file')] diff --git a/hesabixCore/src/Service/AGI/AGIService.php b/hesabixCore/src/Service/AGI/AGIService.php index b2b4e94..78fcbcb 100644 --- a/hesabixCore/src/Service/AGI/AGIService.php +++ b/hesabixCore/src/Service/AGI/AGIService.php @@ -335,6 +335,23 @@ class AGIService $cogAccountingDocService = new \App\Cog\AccountingDocService($this->em); $accountingDocService = new \App\AiTool\AccountingDocService($this->em, $cogAccountingDocService); return $accountingDocService->searchRowsAi($params, $params['acc'] ?? null); + // ابزارهای مربوط به تیکت + case 'getTicketsList': + $cogTicketService = new \App\Cog\TicketService($this->em); + $ticketService = new \App\AiTool\TicketService($this->em, $cogTicketService); + return $ticketService->getTicketsListAi($params, $params['acc'] ?? null); + case 'getTicketInfo': + $cogTicketService = new \App\Cog\TicketService($this->em); + $ticketService = new \App\AiTool\TicketService($this->em, $cogTicketService); + return $ticketService->getTicketInfoByCode($params['code'] ?? null, $params['acc'] ?? null); + case 'addOrUpdateTicket': + $cogTicketService = new \App\Cog\TicketService($this->em); + $ticketService = new \App\AiTool\TicketService($this->em, $cogTicketService); + return $ticketService->addOrUpdateTicketAi($params, $params['acc'] ?? null, $params['code'] ?? 0); + case 'replyToTicket': + $cogTicketService = new \App\Cog\TicketService($this->em); + $ticketService = new \App\AiTool\TicketService($this->em, $cogTicketService); + return $ticketService->replyToTicketAi($params, $params['acc'] ?? null); default: return [ 'error' => 'ابزار ناشناخته: ' . $tool diff --git a/hesabixCore/src/Service/AGI/Promps/PromptService.php b/hesabixCore/src/Service/AGI/Promps/PromptService.php index 25c042c..646c078 100644 --- a/hesabixCore/src/Service/AGI/Promps/PromptService.php +++ b/hesabixCore/src/Service/AGI/Promps/PromptService.php @@ -6,6 +6,7 @@ use Doctrine\ORM\EntityManagerInterface; use App\Service\AGI\Promps\InventoryPromptService; use App\Service\AGI\Promps\BankPromptService; use App\Service\AGI\Promps\AccountingDocPromptService; +use App\Service\AGI\Promps\TicketPromptService; class PromptService { @@ -15,6 +16,7 @@ class PromptService private $inventoryPromptService; private $bankPromptService; private $accountingDocPromptService; + private $ticketPromptService; public function __construct( EntityManagerInterface $entityManager, @@ -22,7 +24,8 @@ class PromptService BasePromptService $basePromptService, InventoryPromptService $inventoryPromptService, BankPromptService $bankPromptService, - AccountingDocPromptService $accountingDocPromptService + AccountingDocPromptService $accountingDocPromptService, + TicketPromptService $ticketPromptService ) { $this->em = $entityManager; $this->personPromptService = $personPromptService; @@ -30,6 +33,7 @@ class PromptService $this->inventoryPromptService = $inventoryPromptService; $this->bankPromptService = $bankPromptService; $this->accountingDocPromptService = $accountingDocPromptService; + $this->ticketPromptService = $ticketPromptService; } /** @@ -56,6 +60,10 @@ class PromptService $accountingTools = $this->accountingDocPromptService->getTools(); $tools = array_merge($tools, $accountingTools); + // ابزارهای بخش تیکت‌ها + $ticketTools = $this->ticketPromptService->getTools(); + $tools = array_merge($tools, $ticketTools); + return $tools; } @@ -115,6 +123,9 @@ class PromptService // پرامپ‌های بخش اسناد حسابداری $prompts['accounting'] = $this->accountingDocPromptService->getAllAccountingDocPrompts(); + // پرامپ‌های بخش تیکت‌ها + $prompts['ticket'] = $this->ticketPromptService->getAllTicketPrompts(); + // در آینده بخش‌های دیگر اضافه خواهند شد // $prompts['accounting'] = $this->accountingPromptService->getAllAccountingPrompts(); // $prompts['reports'] = $this->reportsPromptService->getAllReportsPrompts(); diff --git a/hesabixCore/src/Service/AGI/Promps/TicketService.php b/hesabixCore/src/Service/AGI/Promps/TicketService.php new file mode 100644 index 0000000..7bb589a --- /dev/null +++ b/hesabixCore/src/Service/AGI/Promps/TicketService.php @@ -0,0 +1,146 @@ +