init commit
This commit is contained in:
257
src/services/chatService.ts
Normal file
257
src/services/chatService.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { query } from '../database/connection';
|
||||
import { Message } from '../models/Message';
|
||||
import { Match } from '../models/Match';
|
||||
import { ProfileService } from './profileService';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export class ChatService {
|
||||
private profileService: ProfileService;
|
||||
|
||||
constructor() {
|
||||
this.profileService = new ProfileService();
|
||||
}
|
||||
|
||||
// Получить все чаты (матчи) пользователя
|
||||
async getUserChats(telegramId: string): Promise<any[]> {
|
||||
try {
|
||||
// Сначала получаем userId по telegramId
|
||||
const userId = await this.profileService.getUserIdByTelegramId(telegramId);
|
||||
if (!userId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = await query(`
|
||||
SELECT
|
||||
m.*,
|
||||
CASE
|
||||
WHEN m.user1_id = $1 THEN m.user2_id
|
||||
ELSE m.user1_id
|
||||
END as other_user_id,
|
||||
p.name as other_user_name,
|
||||
p.photos as other_user_photos,
|
||||
msg.content as last_message_content,
|
||||
msg.created_at as last_message_time,
|
||||
msg.sender_id as last_message_sender_id,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM messages msg2
|
||||
WHERE msg2.match_id = m.id
|
||||
AND msg2.sender_id != $1
|
||||
AND msg2.is_read = false
|
||||
) as unread_count
|
||||
FROM matches m
|
||||
LEFT JOIN profiles p ON (
|
||||
CASE
|
||||
WHEN m.user1_id = $1 THEN p.user_id = m.user2_id
|
||||
ELSE p.user_id = m.user1_id
|
||||
END
|
||||
)
|
||||
LEFT JOIN messages msg ON msg.id = (
|
||||
SELECT id FROM messages
|
||||
WHERE match_id = m.id
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE (m.user1_id = $1 OR m.user2_id = $1)
|
||||
AND m.status = 'active'
|
||||
ORDER BY
|
||||
CASE WHEN msg.created_at IS NULL THEN m.matched_at ELSE msg.created_at END DESC
|
||||
`, [userId]);
|
||||
|
||||
return result.rows.map((row: any) => ({
|
||||
matchId: row.id,
|
||||
otherUserId: row.other_user_id,
|
||||
otherUserName: row.other_user_name,
|
||||
otherUserPhoto: row.other_user_photos?.[0] || null,
|
||||
lastMessage: row.last_message_content,
|
||||
lastMessageTime: row.last_message_time || row.matched_at,
|
||||
lastMessageFromMe: row.last_message_sender_id === userId,
|
||||
unreadCount: parseInt(row.unread_count) || 0,
|
||||
matchedAt: row.matched_at
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error getting user chats:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Получить сообщения в чате
|
||||
async getChatMessages(matchId: string, limit: number = 50, offset: number = 0): Promise<Message[]> {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT * FROM messages
|
||||
WHERE match_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $2 OFFSET $3
|
||||
`, [matchId, limit, offset]);
|
||||
|
||||
return result.rows.map((row: any) => new Message({
|
||||
id: row.id,
|
||||
matchId: row.match_id,
|
||||
senderId: row.sender_id,
|
||||
content: row.content,
|
||||
messageType: row.message_type,
|
||||
fileId: row.file_id,
|
||||
isRead: row.is_read,
|
||||
createdAt: new Date(row.created_at)
|
||||
})).reverse(); // Возвращаем в хронологическом порядке
|
||||
} catch (error) {
|
||||
console.error('Error getting chat messages:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Отправить сообщение
|
||||
async sendMessage(
|
||||
matchId: string,
|
||||
senderTelegramId: string,
|
||||
content: string,
|
||||
messageType: 'text' | 'photo' | 'video' | 'voice' | 'sticker' | 'gif' = 'text',
|
||||
fileId?: string
|
||||
): Promise<Message | null> {
|
||||
try {
|
||||
// Получаем senderId по telegramId
|
||||
const senderId = await this.profileService.getUserIdByTelegramId(senderTelegramId);
|
||||
if (!senderId) {
|
||||
throw new Error('Sender not found');
|
||||
}
|
||||
|
||||
// Проверяем, что матч активен и пользователь является участником
|
||||
const matchResult = await query(`
|
||||
SELECT * FROM matches
|
||||
WHERE id = $1 AND (user1_id = $2 OR user2_id = $2) AND status = 'active'
|
||||
`, [matchId, senderId]);
|
||||
|
||||
if (matchResult.rows.length === 0) {
|
||||
throw new Error('Match not found or not accessible');
|
||||
}
|
||||
|
||||
const messageId = uuidv4();
|
||||
|
||||
// Создаем сообщение
|
||||
await query(`
|
||||
INSERT INTO messages (id, match_id, sender_id, content, message_type, file_id, is_read, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, false, CURRENT_TIMESTAMP)
|
||||
`, [messageId, matchId, senderId, content, messageType, fileId]);
|
||||
|
||||
// Обновляем время последнего сообщения в матче
|
||||
await query(`
|
||||
UPDATE matches
|
||||
SET last_message_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
`, [matchId]);
|
||||
|
||||
// Получаем созданное сообщение
|
||||
const messageResult = await query(`
|
||||
SELECT * FROM messages WHERE id = $1
|
||||
`, [messageId]);
|
||||
|
||||
if (messageResult.rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const row = messageResult.rows[0];
|
||||
return new Message({
|
||||
id: row.id,
|
||||
matchId: row.match_id,
|
||||
senderId: row.sender_id,
|
||||
content: row.content,
|
||||
messageType: row.message_type,
|
||||
fileId: row.file_id,
|
||||
isRead: row.is_read,
|
||||
createdAt: new Date(row.created_at)
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Отметить сообщения как прочитанные
|
||||
async markMessagesAsRead(matchId: string, readerTelegramId: string): Promise<void> {
|
||||
try {
|
||||
const readerId = await this.profileService.getUserIdByTelegramId(readerTelegramId);
|
||||
if (!readerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await query(`
|
||||
UPDATE messages
|
||||
SET is_read = true
|
||||
WHERE match_id = $1 AND sender_id != $2 AND is_read = false
|
||||
`, [matchId, readerId]);
|
||||
} catch (error) {
|
||||
console.error('Error marking messages as read:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Получить информацию о матче
|
||||
async getMatchInfo(matchId: string, userTelegramId: string): Promise<any | null> {
|
||||
try {
|
||||
const userId = await this.profileService.getUserIdByTelegramId(userTelegramId);
|
||||
if (!userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await query(`
|
||||
SELECT
|
||||
m.*,
|
||||
CASE
|
||||
WHEN m.user1_id = $2 THEN m.user2_id
|
||||
ELSE m.user1_id
|
||||
END as other_user_id
|
||||
FROM matches m
|
||||
WHERE m.id = $1 AND (m.user1_id = $2 OR m.user2_id = $2) AND m.status = 'active'
|
||||
`, [matchId, userId]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = result.rows[0];
|
||||
const otherUserProfile = await this.profileService.getProfileByUserId(match.other_user_id);
|
||||
|
||||
return {
|
||||
matchId: match.id,
|
||||
otherUserId: match.other_user_id,
|
||||
otherUserProfile,
|
||||
matchedAt: match.matched_at
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting match info:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Удалить матч (размэтчиться)
|
||||
async unmatch(matchId: string, userTelegramId: string): Promise<boolean> {
|
||||
try {
|
||||
const userId = await this.profileService.getUserIdByTelegramId(userTelegramId);
|
||||
if (!userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверяем, что пользователь является участником матча
|
||||
const matchResult = await query(`
|
||||
SELECT * FROM matches
|
||||
WHERE id = $1 AND (user1_id = $2 OR user2_id = $2) AND status = 'active'
|
||||
`, [matchId, userId]);
|
||||
|
||||
if (matchResult.rows.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Помечаем матч как неактивный
|
||||
await query(`
|
||||
UPDATE matches
|
||||
SET status = 'unmatched'
|
||||
WHERE id = $1
|
||||
`, [matchId]);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error unmatching:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user