init commit

This commit is contained in:
2025-09-12 21:25:54 +09:00
commit 17efb2fb53
37 changed files with 12637 additions and 0 deletions

257
src/services/chatService.ts Normal file
View 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;
}
}
}