add chat system

This commit is contained in:
Hesabix 2025-08-04 13:31:07 +00:00
parent 6a4254050d
commit 532ca041f6
6 changed files with 1392 additions and 101 deletions

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241220000000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add memberCount field to chat_channel table';
}
public function up(Schema $schema): void
{
// Add memberCount column to chat_channel table
$this->addSql('ALTER TABLE chat_channel ADD member_count INT NOT NULL DEFAULT 0');
// Update existing channels with correct member count
$this->addSql('
UPDATE chat_channel c
SET member_count = (
SELECT COUNT(*)
FROM chat_channel_member m
WHERE m.channel_id = c.id AND m.is_active = 1
)
');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chat_channel DROP member_count');
}
}

View file

@ -54,6 +54,7 @@ class ChatController extends AbstractController
'isPublic' => $channel->isPublic(),
'avatar' => $channel->getAvatar(),
'messageCount' => $channel->getMessageCount(),
'memberCount' => $channel->getMemberCount(),
'lastMessageAt' => $channel->getLastMessageAt()?->format('Y-m-d H:i:s'),
'createdAt' => $channel->getCreatedAt()->format('Y-m-d H:i:s'),
'isAdmin' => $this->chatService->isUserAdmin($channel, $user),
@ -128,7 +129,7 @@ class ChatController extends AbstractController
'description' => $channel->getDescription(),
'isPublic' => $channel->isPublic(),
'messageCount' => $channel->getMessageCount(),
'memberCount' => $this->chatService->getChannelStats($channel)['memberCount'],
'memberCount' => $channel->getMemberCount(),
'lastMessageAt' => $channel->getLastMessageAt()?->format('Y-m-d H:i:s'),
];
}
@ -387,10 +388,13 @@ class ChatController extends AbstractController
], Response::HTTP_FORBIDDEN);
}
$limit = (int) $request->query->get('limit', 50);
$limit = (int) $request->query->get('limit', 30);
$offset = (int) $request->query->get('offset', 0);
$messages = $this->chatService->getChannelMessages($channel, $limit, $offset);
// Get total message count for pagination info
$totalMessages = $this->chatService->getChannelMessageCount($channel);
$data = [];
foreach ($messages as $message) {
@ -418,7 +422,13 @@ class ChatController extends AbstractController
return $this->json([
'success' => true,
'data' => $data
'data' => $data,
'pagination' => [
'limit' => $limit,
'offset' => $offset,
'total' => $totalMessages,
'hasMore' => ($offset + $limit) < $totalMessages
]
]);
}
@ -478,6 +488,11 @@ class ChatController extends AbstractController
'id' => $message->getSender()->getId(),
'fullName' => $message->getSender()->getFullName(),
],
'quotedMessage' => $message->getQuotedMessage() ? [
'id' => $message->getQuotedMessage()->getId(),
'content' => $message->getQuotedMessage()->getContent(),
'sender' => $message->getQuotedMessage()->getSender()->getFullName(),
] : null,
]
]);
} catch (\Exception $e) {
@ -564,6 +579,44 @@ class ChatController extends AbstractController
}
}
#[Route('/messages/{messageId}/reactions', name: 'chat_remove_reaction', methods: ['DELETE'])]
public function removeReaction(int $messageId, Request $request): JsonResponse
{
$message = $this->messageRepository->find($messageId);
if (!$message) {
return $this->json([
'success' => false,
'message' => 'پیام یافت نشد'
], Response::HTTP_NOT_FOUND);
}
$data = json_decode($request->getContent(), true);
if (!isset($data['emoji']) || empty($data['emoji'])) {
return $this->json([
'success' => false,
'message' => 'ایموجی الزامی است'
], Response::HTTP_BAD_REQUEST);
}
/** @var User $user */
$user = $this->security->getUser();
$success = $this->chatService->removeReaction($message, $user, $data['emoji']);
if ($success) {
return $this->json([
'success' => true,
'message' => 'واکنش حذف شد'
]);
} else {
return $this->json([
'success' => false,
'message' => 'خطا در حذف واکنش'
], Response::HTTP_BAD_REQUEST);
}
}
#[Route('/users/search', name: 'chat_search_users', methods: ['GET'])]
public function searchUsers(Request $request): JsonResponse
{

View file

@ -52,6 +52,9 @@ class ChatChannel
#[ORM\Column]
private int $messageCount = 0;
#[ORM\Column]
private int $memberCount = 0;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $lastMessageAt = null;
@ -236,6 +239,17 @@ class ChatChannel
return $this;
}
public function getMemberCount(): int
{
return $this->memberCount;
}
public function setMemberCount(int $memberCount): static
{
$this->memberCount = $memberCount;
return $this;
}
public function getLastMessageAt(): ?\DateTimeImmutable
{
return $this->lastMessageAt;

View file

@ -130,13 +130,28 @@ class ChatMessageRepository extends ServiceEntityRepository
->getResult();
}
/**
* Get total message count for a channel
*/
public function getChannelMessageCount(ChatChannel $channel): int
{
return $this->createQueryBuilder('m')
->select('COUNT(m.id)')
->where('m.channel = :channel')
->andWhere('m.isDeleted = :isDeleted')
->setParameter('channel', $channel)
->setParameter('isDeleted', false)
->getQuery()
->getSingleScalarResult();
}
/**
* Get message statistics for a channel
*/
public function getChannelMessageStats(ChatChannel $channel): array
{
$qb = $this->createQueryBuilder('m')
->select('COUNT(m.id) as total, COUNT(CASE WHEN m.messageType = :emoji THEN 1 END) as emoji_count')
->select('COUNT(m.id) as total, SUM(CASE WHEN m.messageType = :emoji THEN 1 ELSE 0 END) as emoji_count')
->where('m.channel = :channel')
->andWhere('m.isDeleted = :isDeleted')
->setParameter('channel', $channel)

View file

@ -34,6 +34,7 @@ class ChatService
$channel->setDescription($description);
$channel->setIsPublic($isPublic);
$channel->setCreatedBy($creator);
$channel->setMemberCount(1); // Creator is the first member
// Add creator as admin member
$member = new ChatChannelMember();
@ -72,6 +73,10 @@ class ChatService
$member->setIsAdmin(false);
$this->entityManager->persist($member);
// Update channel member count
$channel->setMemberCount($channel->getMemberCount() + 1);
$this->entityManager->flush();
return true;
@ -88,6 +93,10 @@ class ChatService
}
$member->setIsActive(false);
// Update channel member count
$channel->setMemberCount($channel->getMemberCount() - 1);
$this->entityManager->flush();
return true;
@ -120,6 +129,10 @@ class ChatService
}
$member->setIsActive(false);
// Update channel member count
$channel->setMemberCount($channel->getMemberCount() - 1);
$this->entityManager->flush();
return true;
@ -258,6 +271,14 @@ class ChatService
return $this->messageRepository->findChannelMessages($channel, $limit, $offset);
}
/**
* Get total message count for a channel
*/
public function getChannelMessageCount(ChatChannel $channel): int
{
return $this->messageRepository->getChannelMessageCount($channel);
}
/**
* Search public channels
*/
@ -279,7 +300,10 @@ class ChatService
*/
public function getChannelStats(ChatChannel $channel): array
{
return $this->channelRepository->getChannelStats($channel);
return [
'memberCount' => $channel->getMemberCount(),
'messageCount' => $channel->getMessageCount()
];
}
/**

File diff suppressed because it is too large Load diff