add upload file to support ticket

This commit is contained in:
Hesabix 2025-03-01 17:17:11 +00:00
parent b935fdf6b2
commit 4c5908c5c8
4 changed files with 238 additions and 141 deletions

View file

@ -8,6 +8,7 @@ parameters:
archiveTempMediaDir: '%kernel.project_dir%/../hesabixArchive/temp'
avatarDir: '%kernel.project_dir%/../hesabixArchive/avatars'
sealDir: '%kernel.project_dir%/../hesabixArchive/seal'
SupportFilesDir: '%kernel.project_dir%/../hesabixArchive/support'
services:
# default configuration for services in *this* file
_defaults:

View file

@ -5,6 +5,7 @@ namespace App\Controller;
use App\Entity\Settings;
use App\Entity\Support;
use App\Entity\User;
use App\Service\Access;
use App\Service\Explore;
use App\Service\Extractor;
use App\Service\Jdate;
@ -14,205 +15,284 @@ use App\Service\registryMGR;
use App\Service\SMS;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class SupportController extends AbstractController
{
// ثابت‌ها برای مدیریت خطاها
private const ERROR_TICKET_NOT_FOUND = ['error' => 1, 'message' => 'تیکت یافت نشد.'];
private const ERROR_NO_FILE = ['error' => 2, 'message' => 'فایلی برای این تیکت وجود ندارد.'];
private const ERROR_FILE_NOT_FOUND = ['error' => 3, 'message' => 'فایل در سرور یافت نشد.'];
private const ERROR_ACCESS_DENIED = ['error' => 4, 'message' => 'شما اجازه دسترسی به این فایل را ندارید.'];
private const ERROR_INVALID_PARAMS = ['error' => 999, 'message' => 'تمام موارد لازم را وارد کنید.'];
/**
* function to generate random strings
* @param int $length number of characters in the generated string
* @return string a new string is created with random characters of the desired length
* Generate a random string
*/
private function RandomString($length = 32)
private function randomString(int $length = 32): string
{
return substr(str_shuffle(str_repeat($x = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
return substr(str_shuffle(str_repeat('23456789ABCDEFGHJKLMNPQRSTUVWXYZ', ceil($length / 32))), 1, $length);
}
/**
* Fetch ticket by ID and check ownership
*/
private function getTicket(EntityManagerInterface $entityManager, string $id, bool $checkOwnership = false): ?Support
{
$ticket = $entityManager->getRepository(Support::class)->find($id);
if (!$ticket || ($checkOwnership && $ticket->getSubmitter() !== $this->getUser())) {
return null;
}
return $ticket;
}
#[Route('/api/admin/support/list', name: 'app_admin_support_list')]
public function app_admin_support_list(Extractor $extractor, EntityManagerInterface $entityManager): JsonResponse
{
$items = $entityManager->getRepository(Support::class)->findBy(['main' => 0], ['id' => 'DESC']);
$res = [];
foreach ($items as $item) {
$res[] = Explore::ExploreSupportTicket($item, $this->getUser());
}
$res = array_map(fn($item) => Explore::ExploreSupportTicket($item, $this->getUser()), $items);
return $this->json($extractor->operationSuccess($res));
}
#[Route('/api/admin/support/view/{id}', name: 'app_admin_support_view')]
public function app_admin_support_view(Extractor $extractor, Jdate $jdate, EntityManagerInterface $entityManager, string $id = ''): JsonResponse
public function app_admin_support_view(Extractor $extractor, EntityManagerInterface $entityManager, string $id): JsonResponse
{
$item = $entityManager->getRepository(Support::class)->find($id);
if (!$item)
$item = $this->getTicket($entityManager, $id);
if (!$item) {
throw $this->createNotFoundException();
$replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]);
$res = [];
foreach ($replays as $replay) {
if ($replay->getSubmitter() == $this->getUser())
$replay->setState(1);
else
$replay->setState(0);
$res[] = Explore::ExploreSupportTicket($replay, $this->getUser());
}
return $this->json(
$extractor->operationSuccess([
'item' => Explore::ExploreSupportTicket($item, $this->getUser()),
'replays' => $res
])
);
}
#[Route('/api/admin/support/mod/{id}', name: 'app_admin_support_mod')]
public function app_admin_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, Notification $notifi, string $id = ''): JsonResponse
{
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
$item = $entityManager->getRepository(Support::class)->find($id);
if (!$item)
$this->createNotFoundException();
if (array_key_exists('body', $params)) {
$support = new Support();
$support->setDateSubmit(time());
$support->setTitle('0');
$support->setBody($params['body']);
$support->setState('0');
$support->setMain($item->getId());
$support->setSubmitter($this->getUser());
$entityManager->persist($support);
$entityManager->flush();
$item->setState('پاسخ داده شده');
$entityManager->persist($support);
$entityManager->flush();
//send sms to customer
if ($item->getSubmitter()->getMobile()) {
$SMS->send(
[$item->getId()],
$registryMGR->get('sms', 'ticketReplay'),
$item->getSubmitter()->getMobile()
);
}
//send notification to user
$settings = $entityManager->getRepository(Settings::class)->findAll()[0];
$url = '/profile/support-view/' . $item->getId();
$notifi->insert("به درخواست پشتیبانی پاسخ داده شد", $url, null, $item->getSubmitter());
return $this->json([
'error' => 0,
'message' => 'successful'
]);
$replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]);
$res = array_map(function ($replay) {
$replay->setState($replay->getSubmitter() === $this->getUser() ? 1 : 0);
return Explore::ExploreSupportTicket($replay, $this->getUser());
}, $replays);
return $this->json($extractor->operationSuccess([
'item' => Explore::ExploreSupportTicket($item, $this->getUser()),
'replays' => $res
]));
}
#[Route('/api/admin/support/mod/{id}', name: 'app_admin_support_mod')]
public function app_admin_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, Notification $notifi, string $id): JsonResponse
{
$params = $request->getPayload()->all();
$item = $this->getTicket($entityManager, $id);
if (!$item) {
throw $this->createNotFoundException();
}
if (!isset($params['body'])) {
return $this->json(self::ERROR_INVALID_PARAMS);
}
$support = new Support();
$support->setDateSubmit(time())
->setTitle('0')
->setBody($params['body'])
->setState('0')
->setMain($item->getId())
->setSubmitter($this->getUser());
// ذخیره موقت برای گرفتن ID
$entityManager->persist($support);
$entityManager->flush();
// مدیریت فایل با متد handleFileUpload
$fileName = $this->handleFileUpload($request, $this->getParameter('SupportFilesDir'), $support->getId());
if ($fileName) {
$support->setFileName($fileName);
}
$entityManager->persist($support);
$item->setState('پاسخ داده شده');
$entityManager->persist($item);
$entityManager->flush();
// بررسی سوئیچ ارسال SMS
$sendSms = filter_var($params['sendSms'] ?? true, FILTER_VALIDATE_BOOLEAN); // پیش‌فرض true
if ($sendSms && ($mobile = $item->getSubmitter()->getMobile())) {
$SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketReplay'), $mobile);
}
$settings = $entityManager->getRepository(Settings::class)->findAll()[0];
$notifi->insert("به درخواست پشتیبانی پاسخ داده شد", "/profile/support-view/{$item->getId()}", null, $item->getSubmitter());
return $this->json([
'error' => 999,
'message' => 'تمام موارد لازم را وارد کنید.'
'error' => 0,
'message' => 'successful',
'file' => $fileName
]);
}
/**
* Handle file upload and return filename
*/
private function handleFileUpload(Request $request, string $uploadDirectory, int $ticketId): ?string
{
if (!file_exists($uploadDirectory)) {
mkdir($uploadDirectory, 0777, true);
}
$files = $request->files->get('files');
if ($files && !empty($files)) {
$file = $files[0]; // فقط اولین فایل
$extension = $file->getClientOriginalExtension();
$fileName = $ticketId . '.' . $extension;
$file->move($uploadDirectory, $fileName);
return $fileName;
}
return null;
}
#[Route('/api/support/list', name: 'app_support_list')]
public function app_support_list(Jdate $jdate, EntityManagerInterface $entityManager): JsonResponse
{
$items = $entityManager->getRepository(Support::class)->findBy(
[
'submitter' => $this->getUser(),
'main' => 0
],
[
'id' => 'DESC'
]
['submitter' => $this->getUser(), 'main' => 0],
['id' => 'DESC']
);
foreach ($items as $item) {
$item->setDateSubmit($jdate->jdate('Y/n/d', $item->getDateSubmit()));
}
return $this->json($items);
}
#[Route('/api/support/mod/{id}', name: 'app_support_mod')]
public function app_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, string $id = ''): JsonResponse
{
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
$params = $request->getPayload()->all();
$uploadDirectory = $this->getParameter('SupportFilesDir');
if (!file_exists($uploadDirectory)) {
mkdir($uploadDirectory, 0777, true);
}
if ($id == '') {
if (array_key_exists('title', $params) && array_key_exists('body', $params)) {
$item = new Support();
$item->setBody($params['body']);
$item->setTitle($params['title']);
$item->setDateSubmit(time());
$item->setSubmitter($this->getUser());
$item->setMain(0);
$item->setCode($this->RandomString(8));
$item->setState('در حال پیگیری');
$entityManager->persist($item);
$entityManager->flush();
//send sms to manager
$SMS->send(
[$item->getId()],
$registryMGR->get('sms', 'ticketRec'),
$registryMGR->get('ticket', 'managerMobile')
);
return $this->json([
'error' => 0,
'message' => 'ok',
'url' => $item->getId()
]);
if ($id === '') {
if (!isset($params['title'], $params['body'])) {
return $this->json(self::ERROR_INVALID_PARAMS);
}
} else {
if (array_key_exists('body', $params)) {
$item = new Support();
$upper = $entityManager->getRepository(Support::class)->find($id);
if ($upper)
$item->setMain($upper->getid());
$item->setBody($params['body']);
$item->setTitle($upper->getTitle());
$item->setDateSubmit(time());
$item->setSubmitter($this->getUser());
$item->setState('در حال پیگیری');
$entityManager->persist($item);
$entityManager->flush();
$upper->setState('در حال پیگیری');
$entityManager->persist($upper);
$entityManager->flush();
//send sms to manager
$SMS->send(
[$item->getId()],
$registryMGR->get('sms', 'ticketRec'),
$registryMGR->get('ticket', 'managerMobile')
);
return $this->json([
'error' => 0,
'message' => 'ok',
'url' => $item->getId()
]);
$item = new Support();
$item->setBody($params['body'])
->setTitle($params['title'])
->setDateSubmit(time())
->setSubmitter($this->getUser())
->setMain(0)
->setCode($this->randomString(8))
->setState('در حال پیگیری');
$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' => 999,
'message' => 'تمام موارد لازم را وارد کنید.'
'error' => 0,
'message' => 'ok',
'url' => $item->getId(),
'files' => $fileName
]);
}
#[Route('/api/support/view/{id}', name: 'app_support_view')]
public function app_support_view(Jdate $jdate, EntityManagerInterface $entityManager, string $id = ''): JsonResponse
public function app_support_view(EntityManagerInterface $entityManager, string $id): JsonResponse
{
$item = $entityManager->getRepository(Support::class)->find($id);
if (!$item)
throw $this->createNotFoundException();
if ($item->getSubmitter() != $this->getUser())
$item = $this->getTicket($entityManager, $id, true);
if (!$item) {
throw $this->createAccessDeniedException();
$replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]);
$replaysArray = [];
foreach ($replays as $replay) {
$replaysArray[] = Explore::ExploreSupportTicket($replay, $this->getUser());
}
$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
]);
}
#[Route('/api/support/download/file/{id}', name: 'app_support_download_file')]
public function app_support_download_file(EntityManagerInterface $entityManager, string $id): Response
{
$ticket = $this->getTicket($entityManager, $id);
if (!$ticket) {
return $this->json(self::ERROR_TICKET_NOT_FOUND, 404);
}
$currentUser = $this->getUser();
if (!$currentUser || (!$this->isGranted('ROLE_ADMIN') && $ticket->getSubmitter() !== $currentUser)) {
return $this->json(self::ERROR_ACCESS_DENIED, 403);
}
$fileName = $ticket->getFileName();
if (!$fileName) {
return $this->json(self::ERROR_NO_FILE, 404);
}
$filePath = $this->getParameter('SupportFilesDir') . '/' . $fileName;
if (!file_exists($filePath)) {
return $this->json(self::ERROR_FILE_NOT_FOUND, 404);
}
return new BinaryFileResponse($filePath, 200, [
'Content-Disposition' => ResponseHeaderBag::DISPOSITION_ATTACHMENT . '; filename="' . $fileName . '"'
]);
}
}

View file

@ -38,6 +38,9 @@ class Support
#[ORM\Column(length: 255, nullable: true)]
private ?string $code = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $fileName = null;
public function getId(): ?int
{
return $this->id;
@ -126,4 +129,16 @@ class Support
return $this;
}
public function getFileName(): ?string
{
return $this->fileName;
}
public function setFileName(?string $fileName): static
{
$this->fileName = $fileName;
return $this;
}
}

View file

@ -564,6 +564,7 @@ class Explore
$res['submitter'] = self::ExploreUser($support->getSubmitter());
$res['main'] = $support->getMain();
$res['owner'] = true;
$res['fileName'] = $support->getFileName();
if ($user->getId() != $support->getSubmitter()->getId()) {
$res['owner'] = false;
}